不能在协程 Redis 中直接用 multi/exec,因为其 multi() 返回非协程安全对象,exec() 会同步阻塞协程;应改用 pipeline 实现原子批量操作,WATCH 场景则必须用 eval() Lua 脚本。

为什么不能在协程 Redis 中直接用 multi/exec
因为 Swoole\Coroutine\Redis 的 multi() 方法返回的是一个普通 PHP 对象,不是协程安全的 pipeline 实例,调用 exec() 会阻塞当前协程,破坏调度 —— 这是面试时最容易被问倒的点。官方文档没明说,但源码里 multi() 内部仍走同步 socket read,不 yield 控制权。
替代方案:用 pipeline 模拟事务语义
虽然 Redis 的 ACID 事务(multi/exec)在协程客户端中不可用,但多数业务场景真正需要的是「原子性批量执行」,而非严格事务回滚。这时应改用 pipeline():
-
pipeline()是协程安全的,所有命令攒入缓冲区,exec()一次性发出、一次性接收响应 - 它不提供 WATCH + EXEC 的乐观锁能力,但能保证网络层的原子提交(无中间状态暴露)
- 若需条件写入,必须退回到 Lua 脚本(
eval()),由 Redis 端保证逻辑一致性
示例:
$redis = new Swoole\Coroutine\Redis();
$redis->connect('127.0.0.1', 6379);
$pipe = $redis->pipeline();
$pipe->incr('counter');
$pipe->lPush('log', 'event_123');
$pipe->expire('counter', 3600);
$result = $pipe->exec(); // 返回数组,按顺序对应各命令结果
WATCH 场景下必须用 eval() 替代 multi/exec
当你依赖 WATCH 监控 key 变更并配合 EXEC 实现乐观锁时,协程 Redis 客户端完全不支持该模式。强行调用 watch() 后跟 multi() 会导致连接卡死或返回 false。
正确做法是把整个逻辑写进 Lua 脚本,用 eval() 一次性交由 Redis 执行:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- Lua 脚本在 Redis 单线程内原子运行,天然规避并发竞争
-
eval()是协程安全的,底层走统一异步 IO 流程 - 注意脚本长度限制(默认 512MB)和超时设置(
redis.call()不会触发 timeout)
例如实现「仅当 key 不存在时设值并返回成功」:
$script = "if redis.call('exists', KEYS[1]) == 0 then redis.call('set', KEYS[1], ARGV[1]); return 1 else return 0 end";
$result = $redis->eval($script, ['my_key'], ['my_value']);
连接池里 pipeline 的坑:别复用同一个实例
如果用了连接池(比如 Swoole\Coroutine\Channel 管理的 pool),切记:每个协程必须获取独立的 $redis 实例再调用 pipeline()。共用一个 $redis 对象并在不同协程里交替调用 pipeline() → exec(),会导致命令错乱或响应错位 —— 因为 pipeline 缓冲区是实例级的,不是协程隔离的。
常见错误写法:
// ❌ 错误:$pool->get() 后未绑定到当前协程上下文,多个协程共享同一 pipeline 缓冲
$redis = $pool->get();
$pipe = $redis->pipeline();
go(function () use ($pipe) { $pipe->set('a', '1'); $pipe->exec(); });
go(function () use ($pipe) { $pipe->set('b', '2'); $pipe->exec(); }); // 命令混入前一个 exec
正确方式是每个协程自己拿连接、自己建 pipeline。
真正难处理的从来不是“怎么写”,而是“哪些看似合理的方式其实会悄悄破坏协程调度”。比如你以为 multi() 是个普通方法,但它背后藏着同步阻塞;你以为 pipeline 可以跨协程复用,结果响应全串了。这些细节不踩一遍坑,光看文档根本意识不到。

















