In modern web applications, distributed architecture has become the mainstream way to improve system availability and scalability. However, one of the challenges posed by distributed environments is. If the cache state between distributed nodes cannot be properly managed, inconsistent data may lead to errors, degraded user experience, and even security risks.
This article will introduce how to use PHP's apcu_entry function to implement an efficient and consistent caching mechanism in a distributed system.
apcu_entry is a convenient function provided by the PHP APCu extension to obtain cached values atomically, or generate and store the cache if it does not exist. The function signature is as follows:
mixed apcu_entry(string $key, callable $generator, int $ttl = 0)
$key : cache key name.
$generator : A function called when the cache item does not exist, used to generate cached values.
$ttl : cache time, in seconds. 0 means expired.
This function has thread-safe characteristics and avoids the "shocking" effect in a multi-threaded (or concurrent) environment, ensuring that the cache value will only be generated once.
By default, APCu is based on local memory and cannot be shared across servers. Therefore, among multiple PHP-FPM nodes, the APCu of each node is isolated.
For example:
Server A requests the cache key user_123_profile and will execute the generation logic.
Server B requests this key again, and APCu does not hit the cache because it exists only in the local memory of Server A.
In this case, if multiple nodes try to pull data at the same time and write to the local APCu cache, it will cause redundant resource consumption and possible data inconsistencies.
We can use APCu and Redis to use APCu as the first-level cache for nodes (L1 Cache) and Redis as the shared cache layer (L2 Cache) .
Each node prioritizes the attempt to retrieve data from local APCu.
If APCu does not hit, use apcu_entry to ensure that the data generation logic is called only once.
In the generation logic of apcu_entry :
First check whether Redis already has the data;
If Redis hits, load from Redis and save to APCu;
If Redis also misses, get it from the database or other source and save it to Redis and APCu.
function getUserProfile($userId) {
$cacheKey = "user_profile_$userId";
return apcu_entry($cacheKey, function() use ($cacheKey, $userId) {
// Redis As a distributed cache
$redis = new Redis();
$redis->connect('gitbox.net', 6379);
$redisKey = "global_cache:$cacheKey";
$cached = $redis->get($redisKey);
if ($cached !== false) {
return json_decode($cached, true);
}
// Simulate loading data from a database
$profileData = loadUserProfileFromDB($userId);
// Save Redis(Set expiration time,For example10minute)
$redis->setex($redisKey, 600, json_encode($profileData));
return $profileData;
}, 60); // APCu cache60Second
}
Through the above method, apcu_entry ensures that each node will enter the data loading logic only once when the first request is made, and avoids repeated query of the database. The existence of Redis ensures cache consistency across nodes.
To improve data consistency, the following strategies can be adopted:
Unified expiration time : Ensure that the TTL of Redis and APCu are relatively consistent and avoid "dirty reading".
Use version number to control data : add a version number or timestamp to cache data to verify that it is latest when read.
Active failure mechanism : For example, after user data is updated, all nodes are notified to clear the corresponding APCu cache (can be implemented by Redis publish/subscribe).
Although APCu cannot directly share data in a distributed environment, through the thread safety mechanism of apcu_entry , we can build an efficient cache system that works in collaboration with local + global cache. Combined with shared caching tools such as Redis, it can effectively reduce database load, improve system performance, and maintain data consistency as much as possible.
This model is especially suitable for business scenarios such as user information, configuration data, product details, etc., where the reading frequency is high and the update frequency is low. It is one of the important means to build a high-performance PHP distributed system.