事务与管道
This content is not available in your language yet.
Redis 事务和 pipeline 都要求多个命令共享连接,但两者语义不同:事务关注队列与原子提交,pipeline 关注批量往返。
先给答案:Pipeline 优化往返,事务定义提交边界,两者都不能替代业务一致性
Section titled “先给答案:Pipeline 优化往返,事务定义提交边界,两者都不能替代业务一致性”Pipeline 把多条命令集中发送,减少网络往返;MULTI/EXEC 把命令排队并在 EXEC 时提交执行。Spring 的事务连接绑定保证同一线程中的命令进入同一事务上下文,但不会自动把数据库事务和 Redis 事务变成分布式事务。
事务中读取结果、序列化时机和异常处理都有特殊语义;Pipeline 中的返回值也可能延迟到批量执行。使用前要先确认目标是降低 RTT、保证 Redis 内部命令连续执行,还是需要跨资源一致性,三者不是同一个问题。
Spring transaction active -> RedisConnectionUtils.doGetConnection -> bind RedisConnectionHolder -> connection.multi() -> template commands are queued -> commit: connection.exec() -> rollback/unknown: connection.discard()RedisTemplate#setEnableTransactionSupport 在 core/RedisTemplate.java:220-232 开启参与事务;执行时在 :403-426 把参数传给 RedisConnectionUtils。事务绑定逻辑位于 core/RedisConnectionUtils.java:121-184,非只读事务在 :198-218 注册同步并执行 multi()。
提交和回滚位于 RedisConnectionUtils.java:401-425:commit 执行 exec,rollback 或 unknown 执行 discard,最后解绑资源。
Pipeline
Section titled “Pipeline”RedisTemplate#executePipelined(SessionCallback) 位于 core/RedisTemplate.java:447-482,先绑定连接,再 openPipeline,执行 session,closePipeline 后用 serializer 转换混合结果。普通 callback 版本在 :486-511。
为什么事务连接要线程绑定
Section titled “为什么事务连接要线程绑定”替代方案:事务中的每个命令重新取连接。
为什么不行:Redis 的 MULTI 队列是连接状态,换连接后命令不在同一事务里。
证据:RedisConnectionUtils 以 TransactionSynchronizationManager 绑定 RedisConnectionHolder,见 :126-181。
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| 事务里读取结果 | 得到 null 或 queued | MULTI 阶段只入队 | 在 EXEC 后读取结果 |
| pipeline 误以为原子 | 部分命令已执行 | pipeline 只是批量发送 | 需要原子性时使用事务或 Lua |
| Cluster 跨槽事务 | CROSSSLOT | key 不在同一 hash slot | 使用 hash tag 或改模型 |
连接绑定是“会话语义”的通用实现:数据库事务、MQ producer transaction、分布式锁都需要把一组操作固定到同一会话或资源 holder。
面试锚点
- pipeline 和 MULTI/EXEC 的区别?
- 为什么 Redis 事务需要 ThreadLocal 资源绑定?
- rollback 时为什么执行 DISCARD 而不是关闭连接即可?