必须在Redis服务端配置notify-keyspace-events Ex并重启服务,PHP用psubscribe('__keyevent@*__:expired')监听,回调中不可调用Redis命令,需通过消息队列或子进程处理业务逻辑。
phpEnv 环境下无法直接通过 php.ini 或 Apache/Nginx 配置启用 Redis 过期回调 —— 因为该功能完全依赖 Redis 服务端的设置,与 PHP 运行环境无关。
你得先改 Redis 本身,再让 PHP 客户端正确订阅,否则
永远收不到消息。
必须在 Redis 服务端开启
phpEnv 自带的 Redis(通常是 Windows 版或精简版)默认关闭键空间通知,且配置文件位置不统一。常见路径有:
或
。打开后查找:
将其改为:
注意:
中的
表示“键事件通知”(前缀
),
表示“过期事件”,二者缺一不可。只写
无效;写
也可用,但 PHP 订阅时需对应改频道名。
立即学习
“
PHP免费学习笔记(深入)
”;
改完必须重启 Redis 服务(不是重启 phpEnv,而是单独重启
或服务项),否则配置不生效。
PHP 代码里只能用
,不能用
因为过期事件是广播式、模式匹配的,必须用模式订阅(pattern subscribe)。常见错误是写成:
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
正确写法是:
中的
表示监听所有数据库(db0–db15),避免因业务写到非 0 库而漏事件
回调函数第四个参数
是纯 key 名字符串,不是 JSON 或完整消息体
该连接会一直阻塞等待,必须运行在常驻进程(如 CLI 脚本)中,Web 请求里调用会超时
回调内禁止调用
的其他命令
这是最隐蔽的坑:在
的回调闭包里执行
或
会直接报错或静默失败,典型现象是:
原因:Redis Pub/Sub 连接处于特殊状态,不支持混用命令。解决方案只有两个:
把要操作的 key 名(即
)通过消息队列(如 Beanstalkd、Redis List)转给另一个独立 worker 进程处理
或用
派生子进程,在子进程中新建
实例去操作 —— 但要注意资源泄漏和僵尸进程回收
别试图在回调里重连 Redis,它不会解决根本限制。
验证是否生效,别只信日志
光看 PHP 是否打印出 key 名不够,得确认三件事都通:
Redis 日志里有类似
返回
用
在终端手动监听,然后
,2 秒后应立刻收到一行
PHP 脚本用
启动后,同样
测试 key,观察是否输出
只要其中一环断掉,整个链路就失效 —— 而最容易被忽略的是:phpEnv 的 Redis 服务没真正重启,或者配置改在了错误的 .conf 文件里。
notify-keyspace-eventspsubscribenotify-keyspace-events ExC:\phpEnv\redis\redis.confC:\phpEnv\RedisServer\redis.windows.confnotify-keyspace-events ""notify-keyspace-events ExExE__keyevent@*xxKxRedisServer.exepsubscribesubscribe$redis->subscribe(['__keyevent@0__:expired'], $callback); // ❌ 错:这是精确订阅,收不到$redis->psubscribe(['__keyevent@*__:expired'], function ($redis, $pattern, $channel, $msg) {
echo "过期 key: {$msg}\n"; // $msg 就是 key 名
});__keyevent@*__:expired*$msgpsubscribe$redispsubscribe$redis->get($msg)$redis->del($msg)Warning: Redis::get(): read error on connection in ...$msgpcntl_fork()Redis127.0.0.1:6379> CONFIG GET notify-keyspace-events["notify-keyspace-events","Ex"]redis-cli --csv psubscribe '__keyevent@*__:expired'SET testkey 123 EX 2"pmessage","__keyevent@*__:expired","__keyevent@0__:expired","testkey"php listen.phpSET