๐ 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: 0.0005 seconds of CPU time
- Redis (localhost): 0.1 seconds of CPU time
- Redis (remote): 2-5 seconds of CPU time
APCu is 200-1000x faster for local operations.
โ
When to Use APCu
PERFECT FOR APCu
- Application configuration โ Values that rarely change
- Feature flags โ Toggle features without redeploy
- Local counters โ Per-server request counts, rate limiting
- Computed values โ Expensive calculations that are server-specific
- Route caching data โ Laravel's route cache could use APCu
- OpCache for autoloader โ APCu can cache classmap
- Local cache for read-only data โ Data that doesn't change often
โ ๏ธ APCu IS NOT FOR
- Sessions โ Sessions need to be shared across servers
- Queue data โ Queues need to be shared across workers
- Shared counters โ Counters that must be consistent across servers
- Multi-server caching โ APCu is local to one server only
- Persistent storage โ APCu clears when PHP-FPM restarts
โ๏ธ 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
- Data is lost when PHP-FPM restarts (unlike Redis)
- Not shared across multiple servers (use Redis for that)
- Limited to available PHP memory
- No persistence โ cache is purely in-memory
๐ 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.