Redis事务的ACID性质

在传统关系型数据库中,常用ACID性质来检验事务的安全性和可靠性。

在Redis中,事务总是具有原子性(Atomicity)、一致性(Consistency)、和隔离性(Isolation)的,并且当Redis运行在一些特定的持久化模式下,事务也具有耐久性(Durability)。

原子性

事务具有原子性是指,数据库事务中将多个操作看做一个整体来执行,要么执行所有的操作,要么一个操作也不执行。

事务队列

首先弄清楚 Redis 开始事务 multi 命令后,Redis 会为这个事务生成一个队列,每次操作的命令都会按照顺序插入到这个队列中。

这个队列里面的命令不会被马上执行,直到 exec 命令提交事务,所有队列里面的命令会被一次性,并且排他的进行执行。

对于Redis的事务来说,事务队列中的命令要么都执行,要么就一个也不执行,因此,Redis的事务是具有原子性的。

一下展示一个成功执行的事务,事务中所有命令都被执行:

redis> MUTLI
OK
redis> SET msg "hello"
QUEUED
redis> GET msg 
QUEUED
redis> EXEC
1) OK
2) "hello"

与此相反,以下展示一个执行失败的事务,这个事务因命令入队出错而被服务器拒绝执行,事务中的所有命令都不会被执行:

redis> MUTLI
OK
redis> SET msg "hello"
QUEUED
redis> GET
(error) ERR wrong number of arguments for 'get' command
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

Redis的事务和一般关系型数据库事务最大的区别在于Redis不支持事务回滚机制。即使事务队列中的某个命令在执行中出现了错误,整个事务也会继续执行下去,知道事务队列中的命令执行完毕。

下面这个例子,即使RPUSH命令在执行期间出现了错误,事务的后续命令也会持续下去,并且之前的命令也不会受任何影响:

redis> SET msg "hello"	# msg键是一个字符串
OK
redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> RPUSH msg "goods bye" "bye bye"	# 错误的对字符串键msg执行列表键命令
QUEUED
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
1) (integer) 3
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 3

但原子性又一个特点就是要么全部成功,要么全部失败,也就是我们传统 DB 里面说的回滚。要知道 MySQL 为了能进行回滚是花了不少的代价。

新增一条记录
更新到内存
写入redolog,状态prepare
写binlog
commit事务

Redis的作者在事务功能文档解释说,不支持事务回滚是因为这种复杂的功能和Redis最求简单高效的设计主旨不相符,并且认为,Redis执行错误通常是编程是上产生的,这种错误只会出现在开发环境中,而很少会出现在实际生产环境中,所以他认为没必要开发Redis事务回滚功能。Redis官网记录处理事务错误的方式,以及不支持回滚的原因。从严格的意义上来说 Redis 并不具备原子性。

一致性

事务具有一致性是指,如果数据库在执行事务之前是一致的,那么在执行事务之后,无论事务是否执行成功,数据库也仍一致的。
Redis通过谨慎的错误检测和简单的设计来保证了数据库的一致性。以下介绍三个Redis事务可能出错的地方,并说明Redis是如何妥善的处理这些错误,从而保证了数据的一致性。

1、入队错误

如果一个事务在入队过程中,出现了命令不存在,或者命令格式不正确等情况,Redis将拒绝这个事务:

redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> YAHoo
(error) ERR unknown command "YAHoo"
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

服务器拒绝执行入队过程中出错的事务,多以Redis的一致性不会被影响。

2、执行错误
  • 执行过程中发生的错误是一些不能再入队是被服务器发现的错误,这些错误只会在执行过程中被出发。
  • 即使在事务执行过程中发现了错误的命令,服务器也不会终止事务,它会继续执行事务队列中剩余的命令,并且已执行的命令不会被错误的命令影响。
redis> SET msg "hello"	# msg键是一个字符串
OK
redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> RPUSH msg "goods bye" "bye bye"	# 错误的对字符串键msg执行列表键命令
QUEUED
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
1) (integer) 3
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 3

在执行事务总,错误的命令被识别出来,并且不会影响其他命令,错误的命令不会对数据做任何修改,所以Redis的一致性不会被影响。

3、宕机

无论是采用RDB或者AOF持久化方案,都可以使用RDB文件或者AOF文件进行数据恢复,从而将数据库还原至一个一致的状态。

隔离性

隔离性是指,数据库中有多个事务并发的执行,各个事务之间不会相互影响,并且在并发状态下执行的事务和串行执行的事务产生的结果是完全相同的。

Redis 因为是单线程操作,所以在隔离性上有天生的隔离机制,当 Redis 执行事务时,Redis 的服务端保证在执行事务期间不会对事务进行中断,所以,Redis 事务总是以串行的方式运行,事务也具备隔离性。

持久性

事务的持久性是指,当一个事务执行完毕,执行这个事务所得到的结果被保存在持久化的存储中,即使服务器在事务执行完成后停机了,执行的事务的结果也不会被丢失。

因为Redis事务只不过是简单的用队列包裹了一组Redis命令,Redis并没有为事务提供额外的持久化功能,所以Redis的持久性取决于Redis的持久化模式。

  • 纯内存运行,不具备持久化,服务一旦停机,所有数据将丢失。
  • RDB 模式,取决于 RDB 策略,只有在满足策略才会执行 BGSAVE,异步执行并不能保证 Redis 具备持久化。
  • AOF 模式,只有将 appendfsync 选项设置为 always,程序才会在执行命令同步保存到磁盘,这个模式下,Redis 具备持久性。当选项值为everysec,程序每秒同步一次命令到磁盘,宕机可能发生在等待同步的那一秒内,可能会造成数据丢失,这种配置下Redis不具备持久性。当选项值为no,程序交由操作系统来觉得合适同步命令到硬盘,这种配置也可能造成数据丢失,也不具备持久性。
总结
  • Redis 具备了一定的原子性,但不支持回滚。
  • 如果仅仅就一致性的表述上来说,一致性就是从 A 状态经过事务到达 B 状态没有破坏各种约束性,仅就 Redis 而言不谈实现的业务,那是满意一致性。
  • Redis 具备隔离性。
  • 在某些持久化模式下,Redis具备持久性。

[1] 摘自 黄健宏 《Redis 设计与实现》
[2] 摘自 陈于喆 来源:51CTO技术栈

<think>嗯,用户想了解Redis事务的原子性和回滚功能。根据提供的引用内容,Redis事务确实存在一些特殊性质。 关于原子性,引用[2]明确指出Redis不满足传统事务的原子性定义。那个测试案例显示,当中间命令hincrby执行失败时,前后的命令仍然执行了,这不符合"要么全部成功要么全部失败"的原则。这说明Redis只实现了部分事务特性。 对于回滚支持,引用[3]的测试案例很有说服力。用户故意发送了错误的EXPIRE命令(使用'10s'而非数字),虽然命令入队成功,但执行时出错。关键的是,事务中之前的SET命令仍然生效了,name值被成功修改。这证明Redis在命令执行错误时不会自动回滚已执行的操作。 需要特别注意Redis事务的特殊机制:命令在EXEC前只是入队,执行时才会逐个运行。引用[1]提到的传统原子性定义在Redis中并不适用,因为部分失败不会影响其他命令。 用户可能是开发者或系统管理员,正在评估Redis事务是否适合其应用场景。ta可能担心数据一致性问题,特别是在金融等敏感领域。需要明确告知Redis事务的局限性,避免误用。 在回答中会强调两点核心差异:1) Redis的原子性仅体现在命令队列执行不被中断,而非结果一致性;2) 错误处理机制是"跳过错误继续执行",而非回滚。这些特性使Redis事务更适用于可容忍部分失败的场景。</think>关于Redis事务的原子性和回滚特性,结合引用资料分析如下: ### 一、原子性分析 Redis事务**不具备严格的原子性**。引用[2]的测试案例清晰表明: > 当`hincrby myhash php 1`执行出错时(因php值非整数),**前后正确的命令仍被执行**,未出现回滚或跳过后续操作的情况。这违背了"要么全部成功,要么全部失败"的原子性原则[^1]。 根本原因在于Redis事务实现机制: 1. 命令入队阶段:仅进行语法检查(如参数数量) 2. 执行阶段(`EXEC`): - **无运行时错误检测**(如数据类型错误) - **无自动回滚机制**,错误命令被跳过,其他命令继续执行 ### 二、回滚能力分析 Redis**不支持自动回滚**。引用[3]的测试案例证明: ```redis 127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> SET name yangyongli // 正确命令 QUEUED 127.0.0.1:6379(TX)> EXPIRE name 10s // 错误命令(参数应为数字) QUEUED 127.0.0.1:6379(TX)> EXEC 1) OK // SET命令成功 2) (error) ERR value... // EXPIRE失败 127.0.0.1:6379> GET name "yangyongli" // 错误前的修改仍生效 ``` 关键结论: - 语法错误命令(如参数类型不匹配)**仍可入队** - 执行时错误命令**跳过执行**,但**不撤销已执行操作** - 事务中**无任何命令被执行**的唯一情况:入队时检测到语法错误(如命令不存在) ### 三、Redis事务特性总结 | 特性 | 是否支持 | 说明 | |--------------|----------|----------------------------------------------------------------------| | **原子性** | ❌ | 部分失败不影响其他命令执行 | | **自动回滚** | ❌ | 错误命令跳过执行,但已执行操作不会撤销 | | **隔离性** | ✅ | 执行期间不会被其他客户端命令打断 | | **一致性** | ⚠️ | 依赖开发者:需通过`WATCH`实现乐观锁,避免执行期间数据被修改[^1] | ### 四、适用场景建议 Redis事务更适用于: 1. **批量非依赖操作**:如同时更新多个独立键值 2. **可容忍部分失败**的场景 3. 需结合`WATCH`实现简单乐观锁控制 > ⚠️ **重要提示**:若需要强原子性,应考虑使用Lua脚本(Redis保证脚本的原子执行),或选用支持ACID的关系型数据库。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值