php默认会话存储为文件,改用redis需同时配置session.save_handler=redis和含连接参数的session.save_path,否则无效;memcached不支持原子过期,建议优先选Redis。

PHP 默认 session.save_handler 是 files,不是缓存
直接用 session_start() 时,PHP 默认把会话数据写到文件系统(session.save_path 指定的目录),和 Redis、Memcached 这类缓存无关。想用缓存存会话,必须显式切换 session.save_handler,否则所有“缓存会话”的尝试都只是在操作文件。
常见错误现象:
– 代码里写了 $redis->set($key, $val) 手动存 session 数据,但没关掉原生 session 文件写入,导致数据不一致;
– 修改了 php.ini 的 session.save_handler = redis 却没配 session.save_path,结果会话完全失效,且无明确报错;
– 在 CLI 环境下调用 session_start(),但缓存服务(如 Redis)未监听本地 socket 或权限不足,静默失败。
- 确认当前 handler:
var_dump(ini_get('session.save_handler')); - files 方式下,
session_id()对应的文件名是sess_{id},可在session.save_path下直接看到 - 切换前务必检查扩展是否加载:
extension=redis.so(linux)或extension=php_redis.dll(windows)
用 Redis 存 PHP 会话要配对三个关键项
只改 session.save_handler = redis 不够,Redis 连接参数必须通过 session.save_path 一次性传入,PHP 不会自动读取 redis.conf 或环境变量。
典型配置(php.ini 或运行时):
立即学习“PHP免费学习笔记(深入)”;
session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379?database=2&auth=mypass"
注意:
– database 参数必须是数字,不能写成 db=2;
– auth 只在 Redis 配了密码时需要,且不能带空格;
– URL 中的 & 在 php.ini 里要写成 &(xml 实体),否则解析失败;
– 若用 unix socket,写成 unix:///var/run/redis.sock?database=0,路径必须真实存在且 PHP 进程有读权限。
- 运行时动态设置(适合共享主机无权改 php.ini):
ini_set('session.save_handler', 'redis'); ini_set('session.save_path', 'tcp://127.0.0.1:6379'); - PHP 7.0+ 支持
redis.cluster模式,但需扩展版本 ≥ 5.3.2,且session.save_path格式完全不同,别混用 - 测试是否生效:启动会话后,执行
redis-cli -n 2 keys "PHPREDIS_SESSION:*"应能看到 key
自定义 session handler 更灵活,但也更容易出错
当 Redis 连接逻辑复杂(比如多实例路由、连接池、鉴权封装),或需要记录会话元数据(如登录 IP、设备指纹),就得实现 SessionHandlerInterface。
核心陷阱:
– read() 方法返回空字符串(不是 NULL)表示“会话不存在”,返回其他值都会被 PHP 当作序列化数据解析;
– write() 被调用时,PHP 已完成内部序列化,传入的 $session_data 是字符串,不是数组;
– gc()(垃圾回收)默认不触发,除非设了 session.gc_probability,而 Redis 没有 TTL 自动清理,必须自己加 EXPIRE。
- 简单示例中,
write()必须配合setex()而非set():$this->redis->setex($key, ini_get('session.gc_maxlifetime'), $session_data); - 若用
serialize()/unserialize()手动处理数据,必须确保session.serialize_handler = php(默认值),否则与 PHP 内部格式不兼容 - apache + mod_php 环境下,多个请求可能并发调用同一个 handler 实例,避免在 handler 里用静态变量存连接
Memcached 和 Redis 的行为差异直接影响会话可靠性
Memcached 的 session.save_handler = memcached 看似类似,但底层机制不同:它不支持原子性的“设置并设过期”,也没有事务或管道,且默认不启用二进制协议,容易因数据截断导致会话解析失败。
关键区别点:
– Memcached 的 session.save_path 是逗号分隔的 host:port 列表(如 127.0.0.1:11211,192.168.1.10:11211),不支持 URL 查询参数;
– 它无法像 Redis 那样用 GET 直接查原始值,调试时得用 memcached-tool 127.0.0.1:11211 dump;
– Memcached 的 value 大小默认 1MB,超长会话(比如存了大量用户权限树)会被静默截断,而 Redis 默认无限制(但受内存和配置影响)。
- 线上环境强烈建议优先选 Redis:有持久化选项、支持 lua 原子操作、过期策略更可控
- 若必须用 Memcached,请在
php.ini加memcached.sess_binary_protocol = 1避免 ASCII 协议乱码 - 无论哪种缓存,
session.cookie_httponly和session.cookie_secure仍需手动开启,缓存本身不提供传输层保护
实际部署时最常被跳过的一步:没验证 session.gc_maxlifetime 是否与缓存的 TTL 严格对齐。差几秒就可能导致用户被登出,而日志里只显示“session expired”。