Redis 事务

Redis 提供的事务机制与传统的数据库事务有些不同,传统数据库事务必须维护以下特性: 原子性(Atomicity),  一致性(Consistency),  隔离性(Isolation),  持久性(Durability),简称ACID。
我们逐项考察下 Redis 在事务的 ACID 上做出的权衡与取舍:

原子性(Atomicity)
原子意味着操作的不可再分,要么执行要么不执行。Redis 本身提供的所有 API 都是原子操作,那么 Redis 事务其实是要保证批量操作的原子性。Redis 实现批量操作的原理是在一个事务上下文中(通过 MULTI命令开启),所有提交的操作请求都先被放入队列中缓存,在 EXEC 命令提交时一次性批量执行。这样保证了批量操作的一次性执行过程,但 Redis 在事务执行过程的错误情况做出了权衡取舍,那就是放弃了回滚。
Redis 官方文档对此给出的解释是:
  1. Redis 操作失败的原因只可能是语法错误或者错误的数据库类型操作,这些都是在开发层面能发现的问题不会进入到生产环境,因此不需要回滚。
  2. Redis 内部设计推崇简单和高性能,因此不需要回滚能力。
据实而说第一条说法感觉有点站不住脚,可以想象得到 Redis 操作失败的原因绝对不止语法层面的错误,特别是一些像依赖操作系统、文件系统的操作。 第二条说法更实在,Redis 的应用场景明显不是为了数据存储的高可靠而设计的,而是为了数据访问的高性能而设计,设计者为了简单性和高性能而部分放弃了原子性。
出于以上考虑 Redis 的事务执行有以下特点:
  1. 批量操作在发送 EXEC 命令前被放入队列缓存
  2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
  3. 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中
一致性(Consistency)
一致性意味着事务结束后系统的数据依然保证一致。在事务开始之前,数据保持有效的状态,事务结束后也如此。显然在前面讨论原子性时,Redis 舍弃了回滚的设计,基本上也就舍弃对数据一致性的有效保证。不过对于一个高效的 key-value store 或 data structure server,数据操作一致性很多时候更多应该依赖应用层面,事实也是我们使用 Redis 时很多时候都是分片和集群的,数据一致性无法依靠任何事务机制。

隔离性(Isolation)
隔离性保证了在事务完成之前,该事务外部不能看到事务里的数据改变。也不能访问一些中间状态,因为假如事务终止的话,这些状态将永远不会发生。 Redis 采用单线程设计,在一个事务完成之前,其他客户端提交的各种操作都无法执行因此自然没法看见事务执行的中间状态,隔离性得到保证。

持久性(Durability)
Redis 一般情况下都只进行内存计算和操作,持久性无法保证。但 Redis 也提供了2种数据持久化模式,SNAPSHOT 和 AOF,SNAPSHOT的持久化操作与命令操作是不同步的,无法保证事务的持久性。 而AOF模式意味着每条命令的执行都需要进行系统调用操作磁盘写入文件,可以保证持久性,但会大大降低 Redis 的访问性能。

Redis 在2.6版本开始提供脚本(Lua scripting)能力,一种更灵活的批量命令组织方式用于取代目前的事务机制。 脚本提供了更强大和灵活的编程能力,但也是一把双刃剑,由于 Redis 需要保证脚本执行的原子性和隔离性,脚本执行期间会阻塞其他命令的执行,因此建议写一些高效的脚本。 不过从开发者的角度来说使用Lua脚本的成本(学习成本、开发成本、维护成本)都要更大,特别是一些开发者如果将应用的业务逻辑放入脚本中来执行,是不是让人想起了数据库的存储过程。
### Redis 事务机制概述 Redis 使用 `MULTI`、`EXEC`、`DISCARD` 和 `WATCH` 命令来实现事务功能[^2]。这些命令使得一组命令可以作为一个整体被执行,确保它们要么全部成功执行,要么都不执行。 #### 事务特点 - **原子性**:虽然 Redis事务不提供严格的 ACID 中的原子性,但在执行过程中,所有命令会按照顺序依次执行,不会被打断。 - **一致性**:当命令语法错误、命令执行错误或 Redis 发生意外故障时,Redis 事务机制能够保证数据的一致性[^3]。 - **隔离性**:事务中的所有命令会被序列化并按顺序执行,在整个过程里不会受到其他客户端的影响[^4]。 #### 主要命令解释 - **MULTI**:标记一个事务块的开始。之后所有的命令都将被放入队列中等待执行。 - **EXEC**:触发之前排队的所有命令一次性执行,并返回各个命令的结果列表。 - **DISCARD**:取消当前事务,清空已经入队但是还未执行的命令。 - **WATCH**:监视键的变化情况;如果在 WATCH 后有任何修改,则 EXEC 执行失败,以此支持乐观锁的功能[^1]。 #### 错误处理与回滚特性 需要注意的是,即使某个命令在事务内发生了运行期错误(比如除零),这并不会导致整个事务自动回滚——只有那些确实能完成的操作才会生效,而无法继续下去的部分则停止尝试。因此开发者应该自行设计合理的逻辑去应对可能出现的问题。 ```bash # 开始一个新的事务 MULTI # 将几个命令加入到事务队列中 SET key1 "value" INCR key2 LPUSH mylist "item" # 提交事务 EXEC ``` 对于更复杂的业务需求来说,还可以利用 Lua 脚本来编写更加紧凑高效的程序片段来进行批量操作,从而达到类似的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值