必须在redis服务端配置notify-keyspace-events ex并重启服务,php用psubscribe('__keyevent@*__:expired')监听,回调中不可调用redis命令,需通过消息队列或子进程处理业务逻辑。

phpEnv 环境下无法直接通过 php.ini 或 Apache/Nginx 配置启用 Redis 过期回调 —— 因为该功能完全依赖 Redis 服务端的 notify-keyspace-events 设置,与 PHP 运行环境无关。你得先改 Redis 本身,再让 PHP 客户端正确订阅,否则 psubscribe 永远收不到消息。
必须在 Redis 服务端开启 notify-keyspace-events Ex
phpEnv 自带的 Redis(通常是 Windows 版或精简版)默认关闭键空间通知,且配置文件位置不统一。常见路径有:C:\phpEnv\redis\redis.conf 或 C:\phpEnv\RedisServer\redis.windows.conf。打开后查找:
notify-keyspace-events ""
将其改为:
notify-keyspace-events Ex
注意:Ex 中的 E 表示“键事件通知”(前缀 __keyevent@*),x 表示“过期事件”,二者缺一不可。只写 x 无效;写 Kx 也可用,但 PHP 订阅时需对应改频道名。
改完必须重启 Redis 服务(不是重启 phpEnv,而是单独重启 RedisServer.exe 或服务项),否则配置不生效。
PHP 代码里只能用 psubscribe,不能用 subscribe
因为过期事件是广播式、模式匹配的,必须用模式订阅(pattern subscribe)。常见错误是写成:
$redis->subscribe(['__keyevent@0__:expired'], $callback); // ❌ 错:这是精确订阅,收不到
正确写法是:
$redis->psubscribe(['__keyevent@*__:expired'], function ($redis, $pattern, $channel, $msg) {<br> echo "过期 key: {$msg}\n"; // $msg 就是 key 名<br>});
-
__keyevent@*__:expired中的*表示监听所有数据库(db0–db15),避免因业务写到非 0 库而漏事件 - 回调函数第四个参数
$msg是纯 key 名字符串,不是 JSON 或完整消息体 - 该连接会一直阻塞等待,必须运行在常驻进程(如 CLI 脚本)中,Web 请求里调用会超时
psubscribe 回调内禁止调用 $redis 的其他命令
这是最隐蔽的坑:在 psubscribe 的回调闭包里执行 $redis->get($msg) 或 $redis->del($msg) 会直接报错或静默失败,典型现象是:
Warning: Redis::get(): read error on connection in ...
原因:Redis Pub/Sub 连接处于特殊状态,不支持混用命令。解决方案只有两个:
- 把要操作的 key 名(即
$msg)通过消息队列(如 Beanstalkd、Redis List)转给另一个独立 worker 进程处理 - 或用
pcntl_fork()派生子进程,在子进程中新建Redis实例去操作 —— 但要注意资源泄漏和僵尸进程回收
别试图在回调里重连 Redis,它不会解决根本限制。
验证是否生效,别只信日志
光看 PHP 是否打印出 key 名不够,得确认三件事都通:
- Redis 日志里有类似
127.0.0.1:6379> CONFIG GET notify-keyspace-events返回["notify-keyspace-events","Ex"] - 用
redis-cli --csv psubscribe '__keyevent@*__:expired'在终端手动监听,然后SET testkey 123 EX 2,2 秒后应立刻收到一行"pmessage","__keyevent@*__:expired","__keyevent@0__:expired","testkey" - PHP 脚本用
php listen.php启动后,同样SET测试 key,观察是否输出
只要其中一环断掉,整个链路就失效 —— 而最容易被忽略的是:phpEnv 的 Redis 服务没真正重启,或者配置改在了错误的 .conf 文件里。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











