网站简易访问计数器计数随机重置问题求助
问题原因及修复方案
核心错误1:读取阶段未加锁,并发读写导致数据异常
你的代码仅在file_put_contents时添加了LOCK_EX写锁,但读取文件的过程没有加锁。高并发场景下,多个请求会同时读取到同一个旧计数,各自加1后写回,不仅会丢失部分计数,更严重的是:如果某个请求在写入过程中,另一个请求读取到未写完的半份数据(比如文件被截断但新值还没写完),就会把这个残缺的数值加1后写回,直接导致计数器“重置”到错误的低数值。
核心错误2:未处理边界异常情况
当$statfile不存在时,$count变量未初始化,直接执行file_put_contents会写入空字符串;后续请求读取空字符串时,file_get_contents($statfile)+1会得到1,这也是计数器偶尔重置为1的原因之一。另外,如果文件读取失败(权限问题、磁盘临时错误),同样会导致$count异常,进而写入错误数值。
序列化数组文件损坏的根源
之前用单文件存储序列化数组的方案,本质问题和上述一致:并发读写时,多个进程同时写入文件,导致文件内容被部分覆盖、截断,最终序列化结构损坏,无法正常解析,读取时只能得到错误的初始值。
修复代码示例
要解决问题,必须保证读-改-写的整个过程是原子性的,用锁包裹全部操作:
$statfile = 'counter.txt'; // 打开文件(不存在则自动创建,支持读写) $handle = fopen($statfile, 'c+'); if ($handle) { // 获取独占锁(阻塞等待,直到拿到锁) flock($handle, LOCK_EX); // 移到文件开头并读取内容,强制转为整数避免异常 fseek($handle, 0); $count = (int)fread($handle, filesize($statfile)); // 计数加1 $count++; // 移回开头、截断文件后写入新值 fseek($handle, 0); ftruncate($handle, 0); fwrite($handle, $count); // 释放锁并关闭文件 flock($handle, LOCK_UN); fclose($handle); }
代码说明:
fopen('c+')模式确保文件不存在时自动创建,同时支持读写操作。- 读取前就加独占锁,彻底避免并发冲突,同一时间只有一个进程能操作文件。
- 强制将读取内容转为
int,即使文件为空或内容异常,也能得到0,避免计数异常。 - 写入前用
ftruncate截断文件,防止旧内容残留(比如新数值长度比旧值短的情况)。
额外注意事项
- 权限检查:确保Web服务器进程(如www-data)对
$statfile所在目录有读写权限,避免因权限问题导致文件读写失败。 - 分散存储:如果要存储多个页面的计数器,建议每个页面对应一个独立文件(如
page1_counter.txt),减少单个文件的并发压力,降低损坏风险。
内容的提问来源于stack exchange,提问作者Láng Attila D.
相关产品推荐
相关产品推荐

