PHP内置GC不可依赖,因其概率触发(如1/100仅约每100次session_start()触发一次)、非定时、高并发易集中爆发、低流量长期不清理,且扫描海量文件可能超时;必须设gc_probability=0并用Cron定时清理,同时确保session.cookie_lifetime与gc_maxlifetime严格一致。

PHP内置GC为什么不能依赖
PHP的session.gc_probability和session.gc_divisor组合只是概率性触发,不是定时器。哪怕设成1/100,也只是“约每100次session_start()调用中触发一次”,且只在当前请求中执行——这意味着高并发下可能集中爆发,低流量时又长期不清理。更关键的是:GC扫描整个session.save_path目录,当文件数达数十万级,单次扫描就可能超max_execution_time,直接导致页面500或超时。
禁用内置GC后必须配Cron清理
生产环境应设session.gc_probability = 0,彻底关闭请求中GC。然后用系统级Cron定期清理过期文件:
-
0 * * * * find /var/lib/php/sessions -name 'sess_*' -mmin +1800 -delete(每小时清1800秒前未访问的) - 路径需与
session.save_path一致,可用php -i | grep session.save_path确认 - 避免用
-mtime,它按天粒度,-mmin才精确到分钟 - 加
-depth -print0 | xargs -0可提升大目录下删除效率
session.cookie_lifetime必须和gc_maxlifetime对齐
浏览器Cookie过期时间和服务端数据过期时间不一致,会导致“用户看着没登出,但后台已删数据”或“Cookie早过期了,服务器还在留着垃圾文件”。必须同步:
- 设
session.gc_maxlifetime = 1800(30分钟) - 同时设
session.cookie_lifetime = 1800(而非0),让Set-Cookie带明确Expires时间戳 - 运行时可用
session_set_cookie_params(1800),但必须在session_start()之前调用
Redis存储能绕过文件GC问题吗
能,但不是“自动变好”,而是换了一套规则:
立即学习“PHP免费学习笔记(深入)”;
- 改用
session.save_handler = redis后,gc_maxlifetime失效——Redis靠自身EXPIRE指令控制TTL - 仍需确保
redis.session_prefix唯一,避免多应用共享key冲突 - 必须显式设置
session.cookie_lifetime,否则浏览器Cookie默认会话级(关浏览器即丢),和服务端Redis TTL脱节 - Redis本身不自动清理过期key,依赖其惰性删除+定期抽样,所以
maxmemory-policy建议设为allkeys-lru或volatile-lru
真正容易被忽略的是:数据库或Redis方案里,你依然得检查session.cookie_lifetime是否被覆盖——比如框架中间件、Nginx的fastcgi_param PHP_VALUE、甚至某段ini_set()都可能悄悄把它重置为0。



















