๐Ÿ“ Volume II: Laravel Performance Tuning Kit

โšก Topic 20: APCu vs Redis

Local shared memory vs network-based cache.

"Everyone tells you to use Redis for caching. Redis is great.
But Redis requires a network roundtrip โ€” even on localhost.
For tiny, frequently accessed data, APCu is 1000x faster.
Use Redis for shared state across servers.
Use APCu for local, request-scoped, or config data."
โš ๏ธ THE MISCONCEPTION

Many developers use Redis for everything โ€” sessions, cache, queues, rate limiting. But Redis calls involve TCP/IP overhead (even on localhost). For data that doesn't need to be shared across servers, APCu is dramatically faster. The right tool for the right job.

๐Ÿ” What is APCu?

APCu (Alternative PHP Cache - User Cache) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• APCu is a shared memory cache that lives INSIDE each PHP-FPM process. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ PHP-FPM Process 1 โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ APCu (Shared Memory) โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ key: 'config.app_name' โ†’ 'MyApp' โ”‚โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ key: 'feature_flags' โ†’ ['new-ui' => true] โ”‚โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ key: 'counter' โ†’ 12345 โ”‚โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ PHP-FPM Process 2 โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ APCu (Shared Memory) โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ SAME DATA! (Shared across processes on same server)โ”‚โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ KEY FEATURES: โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• โ€ข NO network overhead โ€” direct memory access โ€ข Shared across all PHP-FPM processes on the SAME server โ€ข NOT shared across multiple servers (local only) โ€ข Perfect for: config, feature flags, small counters, local cache

๐Ÿ“Š Performance Comparison: APCu vs Redis

Operation APCu (Local Memory) Redis (Localhost) Redis (Remote) APCu Advantage
Get (1KB value) 0.05 ยตs 10 ยตs 200-500 ยตs 200x faster
Set (1KB value) 0.08 ยตs 12 ยตs 250-600 ยตs 150x faster
Increment counter 0.05 ยตs 8 ยตs 150-400 ยตs 160x faster
REAL-WORLD EXAMPLE

If your app makes 10,000 cache reads per second:

APCu is 200-1000x faster for local operations.

โœ… When to Use APCu

PERFECT FOR APCu
โš ๏ธ APCu IS NOT FOR

โš™๏ธ Installing APCu

UBUNTU / DEBIAN
# Install APCu extension
sudo apt-get install php8.2-apcu

# Or via PECL
pecl install apcu

# Enable APCu
sudo phpenmod apcu
sudo systemctl restart php8.2-fpm
PHP.INI CONFIGURATION
# /etc/php/8.2/mods-available/apcu.ini

extension=apcu.so
apc.enabled=1
apc.shm_size=128M           # Shared memory size
apc.ttl=7200                # Time to live (seconds)
apc.enable_cli=0            # Disable for CLI (artisan commands)
apc.slam_defense=0
apc.serializer=php          # Serializer (php, igbinary, json)
VERIFY APCu IS WORKING
# Check if APCu is loaded
php -m | grep apcu

# Check APCu status
php -r "print_r(apcu_cache_info());"

# Create a test
php -r "apcu_store('test', 'Hello APCu'); echo apcu_fetch('test');"

๐Ÿ”ง Using APCu in Laravel

METHOD 1: Cache Driver (with custom driver)
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Cache;

public function boot()
{
    Cache::extend('apcu', function ($app) {
        return Cache::repository(new \Illuminate\Cache\Repository(
            new \Illuminate\Cache\ApcStore(
                new \Illuminate\Cache\ApcWrapper(),
                config('cache.prefix')
            )
        ));
    });
}

// config/cache.php
'stores' => [
    'apcu' => [
        'driver' => 'apcu',
        'prefix' => 'laravel_',
    ],
],
METHOD 2: Direct APCu Usage (Faster)
// Use directly for maximum performance
class FeatureFlagService
{
    public function isEnabled(string $feature): bool
    {
        // Check APCu first (0.05 ยตs)
        $cached = apcu_fetch("feature.{$feature}");
        
        if ($cached !== false) {
            return $cached;
        }
        
        // Fall back to database (10ms)
        $value = DB::table('feature_flags')
            ->where('name', $feature)
            ->value('enabled');
        
        // Store in APCu for 5 minutes
        apcu_store("feature.{$feature}", $value, 300);
        
        return $value;
    }
    
    public function clearCache(string $feature): void
    {
        apcu_delete("feature.{$feature}");
    }
}

๐Ÿ“Š Laravel Cache Driver Performance

Driver Read Latency Write Latency Shared Across Servers? Best For
file (default) 100-500 ยตs 200-1000 ยตs No (local filesystem) Nothing โ€” avoid in production
redis 10-50 ยตs 15-60 ยตs โœ… Yes Sessions, queues, shared cache
memcached 10-40 ยตs 12-50 ยตs โœ… Yes Shared cache (alternative to Redis)
array 0.01 ยตs 0.01 ยตs No (per request only) Testing only
apcu (custom) 0.05 ยตs 0.08 ยตs No (per server only) Local cache, config, feature flags
๐Ÿ”ด THE RULE: Use APCu for data that is server-specific and changes rarely. Use Redis for data that must be shared across servers (sessions, queues, shared cache). Never use file cache in production.

๐Ÿ”„ Hybrid Approach: APCu + Redis

LAYERED CACHING STRATEGY โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Request โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ L1: APCu Cache (Local, 0.05ยตs) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ€ข Feature flags โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Application config โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Server-local counters โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Expensive computed values (per server) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ MISS โ”‚ โ”‚ โ–ผ โ”‚ โ”‚ L2: Redis Cache (Shared, 10ยตs) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ€ข User sessions โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Database query results โ”‚ โ”‚ โ”‚ โ”‚ โ€ข API response cache โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Rate limiting counters (shared) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ MISS โ”‚ โ”‚ โ–ผ โ”‚ โ”‚ L3: Database (Shared, 1,000-10,000ยตs) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
IMPLEMENTATION
class LayeredCache
{
    public function remember(string $key, int $ttl, callable $callback)
    {
        // Try APCu first (fastest)
        $value = apcu_fetch($key);
        if ($value !== false) {
            return $value;
        }
        
        // Try Redis second (shared)
        $value = Cache::get($key);
        if ($value !== null) {
            // Store in APCu for next time
            apcu_store($key, $value, $ttl);
            return $value;
        }
        
        // Finally, compute the value
        $value = $callback();
        
        // Store in both caches
        Cache::put($key, $value, $ttl);
        apcu_store($key, $value, $ttl);
        
        return $value;
    }
    
    public function forget(string $key): void
    {
        apcu_delete($key);
        Cache::forget($key);
    }
}

๐Ÿ“Š Monitoring APCu

# APCu status via CLI
php -r "print_r(apcu_cache_info());"

# Sample output:
Array
(
    [num_slots] => 16384
    [ttl] => 7200
    [num_hits] => 125000    # Cache hits
    [num_misses] => 5000    # Cache misses
    [mem_size] => 5242880   # Memory used (bytes)
    [num_entries] => 342    # Number of cached items
    [start_time] => 1704067200
    [expunges] => 12
)

# Hit rate = hits / (hits + misses)
# Aim for > 95% hit rate
APCu LIMITATIONS

๐Ÿ“ Topic 20 Summary: APCu vs Redis

Feature APCu Redis Speed (read) 0.05 ยตs 10-500 ยตs Network overhead? No (local memory) Yes (TCP/IP) Shared across servers? No Yes Persistence? No (lost on restart) Yes (Redis persistence) Max size php.ini limit (apc.shm_size) Server RAM Best for Config, flags, local counters Sessions, queues, shared cache
๐Ÿ“Œ THE RULE: Use APCu for server-local, high-frequency data (config, feature flags, local counters). Use Redis for data that must be shared across servers (sessions, queues, shared cache). For maximum performance, use both in a layered cache strategy.
NEXT TOPIC PREVIEW

Topic 21: Laravel Octane โ€” The nuclear option. Keep Laravel alive in memory with Swoole or RoadRunner. Eliminate bootstrap overhead entirely. But beware of memory leaks.