Redis session 驱动必须启用 phpredis 扩展,sess_save_path 需带 tcp:// 或 unix:// 前缀,sess_expiration 不应设为 0,锁超时默认 30 秒且需手动清理滞留锁。

Redis驱动必须启用 phpredis 扩展,否则会静默失败
CodeIgniter 3 的 Redis session 驱动底层直接调用 new Redis(),不兼容 predis 或纯 PHP 实现。若未安装 phpredis,$this->session 仍可加载,但所有写入操作(如 set_userdata())全部失效,且无错误提示——这是最常被忽略的「假成功」陷阱。
验证方式很简单:
- 运行
php -m | grep redis,确认输出包含redis - 在控制器中加一行:
var_dump(class_exists('Redis'));,应返回bool(true) - 若为 false,需安装扩展:Ubuntu 上执行
sudo apt install php-redis,然后重启 PHP-FPM 或 Apache
sess_save_path 格式不对会导致连接拒绝或超时
$config['sess_save_path'] 不是简单填个 host:port,它实际被解析为 URL 字符串,格式错误会触发底层 Redis::connect() 失败,但 CI 不抛异常,只默默退回到默认文件驱动(如果配置了 fallback)。
正确写法必须带协议前缀:
- 本地 TCP 连接:
$config['sess_save_path'] = 'tcp://127.0.0.1:6379'; - 带密码:
$config['sess_save_path'] = 'tcp://127.0.0.1:6379?auth=yourpass'; - Unix socket:
$config['sess_save_path'] = 'unix:///var/run/redis.sock'; - 错误示例:
'127.0.0.1:6379'或'redis://...'(CI 不识别 redis:// 协议)
连接失败时,日志里可能出现 Session: Error while trying to obtain lock,本质就是 Redis 连不上,锁根本没加成功。
sess_expiration 设为 0 会禁用 Redis 的自动过期,必须手动处理
CI 默认把 $config['sess_expiration'] 当作 SETEX 的 TTL 参数传给 Redis。设为 0 后,setex 变成 set,key 永不过期——这和 PHP 原生 session 行为不同,也违背高并发下 session 自清理的设计初衷。
后果很实际:
- Redis 内存持续增长,直到 OOM
-
gc()方法被跳过(因为 Redis 驱动认定“过期由服务端管”,而你关了过期) - 用户登出后 session 数据仍在 Redis 中残留
建议值:生产环境设为明确秒数(如 7200),不要依赖 0;若真需要长期有效,应在业务层控制生命周期,而非靠存储层禁用 TTL。
Redis 锁机制会阻塞请求,高并发下需警惕 30 秒等待上限
CI 的 Redis session 驱动为防并发写冲突,在 read() 和 write() 前会尝试获取锁,锁 key 形如 ci_session_xxx:lock,TTL 固定 300 秒。但获取失败时,它会循环重试最多 30 次、每次 sleep 1 秒——也就是单次请求最长卡 30 秒。
这意味着:
- 若某个请求因异常未释放锁(比如 PHP crash、超时中断),后续 30 秒内所有同 session_id 的请求都会排队等待
- 前端表现为「页面卡住、反复刷新没反应」,排查时容易误判为网络或 Nginx 问题
- 无法通过配置缩短等待时间,该逻辑硬编码在
system/libraries/Session/drivers/Session_redis_driver.php中
真正要解这个问题,得进 Redis 查锁:redis-cli --raw keys "ci_session_*:lock",手动 del 掉滞留锁——这点文档几乎从不提,但线上救火时非常关键。



















