PHP多并发请求场景下如何保证缓存仅生成一次
PHP 单实例缓存并发生成问题修复方案
核心逻辑是通过本地文件独占锁控制并发,保证仅首个请求能执行缓存生成逻辑,其余请求阻塞等待锁释放后直接读取已生成的缓存,避免重复执行耗时的生成逻辑。
修复后完整代码
<?php $cacheFile = __DIR__ . '/cache.html'; $lockFile = __DIR__ . '/cache_generate.lock'; $source1Path = __DIR__ . '/source1.txt'; $source2Path = __DIR__ . '/source2.xml'; // 首次校验缓存是否有效,有效直接读取返回 $cacheTime = file_exists($cacheFile) ? filemtime($cacheFile) : 0; if ($cacheTime > filemtime($source1Path) && $cacheTime > filemtime($source2Path)) { $content = file_get_contents($cacheFile); echo $content; exit; } // 缓存失效,尝试获取生成锁 $lockFp = fopen($lockFile, 'w+'); // 加独占阻塞锁:拿不到锁的请求会在此处阻塞,直到锁被释放 if (flock($lockFp, LOCK_EX)) { // 拿到锁后二次校验缓存是否已被生成:避免等待期间其他请求已完成缓存生成 clearstatcache(true, $cacheFile); // 清除PHP文件状态缓存,读取最新修改时间 $cacheTime = file_exists($cacheFile) ? filemtime($cacheFile) : 0; if ($cacheTime > filemtime($source1Path) && $cacheTime > filemtime($source2Path)) { $content = file_get_contents($cacheFile); } else { // 执行缓存生成逻辑 $content = generate_content($source1Path, $source2Path); // 写入缓存文件 file_put_contents($cacheFile, $content); } // 手动释放锁 flock($lockFp, LOCK_UN); } fclose($lockFp); // 输出最终内容 echo $content; ?>
可选优化说明
- 若不需要请求阻塞等待,可将
flock($lockFp, LOCK_EX)修改为flock($lockFp, LOCK_EX | LOCK_NB),拿不到锁时直接返回旧缓存或返回「内容生成中」的提示,适合对响应延迟要求高的场景 - 锁文件无需提前创建,fopen会自动生成,确保站点运行用户对所在目录有读写权限即可
- PHP进程异常退出时会自动释放文件锁,不会出现死锁问题
- 以上方案仅适用于单服务器部署场景,分布式部署场景需改用Redis、ETCD等实现分布式锁
内容的提问来源于stack exchange,提问作者Snowy
相关产品推荐
相关产品推荐

