Redis能保证ACID的哪些特性?
文章目录
Redis事务ACID分析
总结:
原子性:
- 命令入队时就报错,会放弃事务执行,保证原子性;
- 命令入队时没报错,实际执行时报错,不保证原子性;
- EXEC 命令执行时实例故障,如果开启了 AOF 日志,可以保证原子性。
一致性:能保证一致性
隔离性:能保证隔离性
持久性:不能保证持久性
原子性(A)分析
如果命令正常运行,事务中的原子性是可以得到保证的。
那么在执行命令的过程中如果有命令失败了呢?
过程中执行命令失败很可以不会保证原子性操作,因为这是要分情况的。
1、命令入队就报错
比如执行一个不存在的命令,或者命令的写错了
例如下面代码:
127.0.0.1:6379> set test-mult-key 100 OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECR test-mult-key QUEUED # DECR 命令拼写错了 127.0.0.1:6379> DECRR test-mult-key (error) ERR unknown command `DECRR`, with args beginning with: `test-mult-key`, 127.0.0.1:6379> DECR test-mult-key QUEUED 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors.
可以看到事务中 DECR 的命令拼写错了,写成了 DECRR。这时候事务是不能执行的,在执行 EXEC 的时候,Redis 抛出了错误,整个事务的执行被丢弃了。
对于这种情况,在命令入队时,Redis就会报错并且记录下这个错误。此时,我们还能继续提交命令操作。等到执行了EXEC命令之后,Redis就会拒绝执行所有提交的命令操作,返回事务失败的结果。这样一来,事务中的所有命令都不会再被执行了,保证了原子性。
2、命令执行的时候报错
这种情况,就是我们操作 Redis 命令时候,命令的类型不匹配。
栗如:我们对一个 value 为 string 类型的 key,执行 DECR 操作。
127.0.0.1:6379> set test-mult-key 100 OK 127.0.0.1:6379> set test-mult-key-string 's100' OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECR test-mult-key QUEUED 127.0.0.1:6379> DECR test-mult-key QUEUED # 对 value 为 string 的,执行 DECR 操作,结果会报错 # 模拟错误的命令 127.0.0.1:6379> DECR test-mult-key-string QUEUED 127.0.0.1:6379> DECR test-mult-key QUEUED 127.0.0.1:6379> EXEC 1) (integer) 99 2) (integer) 98 3) (error) ERR value is not an integer or out of range 4) (integer) 97
这种情况下,虽然错误的命令会报错,但是还是会把正确的命令执行完成。
这种情况下,命令的原子性就无法得到保证了。Redis 中没有提供事务的回滚机制。
3、EXEC命令执行时实例发生故障
如果 Redis 开启了 AOF 日志,那么,只会有部分的事务操作被记录到 AOF 日志中。
机器实例恢复后,我们可以使用 redis-check-aof 工具检查 AOF 日志文件,这个工具可以把已完成的事务操作从 AOF 文件中去除。这样一来,我们使用 AOF 恢复实例后,事务操作不会再被执行,从而保证了原子性。
总结
所以关于 Redis 中事务原子性的总结,就是下面几点
- 命令入队时就报错,会放弃事务执行,保证原子性;
- 命令入队时没报错,实际执行时报错,不保证原子性;
- EXEC 命令执行时实例故障,如果开启了 AOF 日志,可以保证原子性。
一致性(C)分析
关于一致性的分析还是从上面三个点来展开
1、命令入队时就报错
事务本身就不会执行,一致性可以得到保证
2、命令执行的时候报错
有错误的命令不会被执行,正确的命令可以正常执行,也不会改变数据库的一致性。
3、EXEC命令执行时实例发生故障
如果没有开启持久化,那么实例故障重启后,数据都没有了,数据库是一致的。
如果使用 RDB 快照,因为 RDB 快照不会在事务执行时执行,所以事务执行的结果不会保存到 RDB 快照中,使用 RDB 快照进行恢复时,数据库中的数据也是一致性的。
如果我们使用了 AOF 日志,而事务操作还没有被记录到 AOF 日志时,实例就发生了故障,那么,使用 AOF 日志恢复的数据库数据是一致的。如果只有部分操作被记录到了 AOF 日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。
总结:Redis 中对于数据一致性属性还是有保证的。
隔离性(I)分析
事务的隔离性要求每个读写事务的对象对其他事务的操作对象相互分离,即该事务提交前对其他事务都不可见。
这里分析下 Redis 中事务的隔离性,Redis 中事务的隔离性将从下面两个方面进行分析
1、如果在命令入队,EXEC执行之前,有并发操作
因为 Redis 在事务提交之前只是把命令,放入到了队列中,所以如果在命令入队,EXEC执行之前,有并发操作,这种情况下,事务是没有隔离性的。
这种情况下,可以借助于 watch 实现:
1、客户端 1 首先,使用 watch 监听一个 key,然后开始一个事务,在事务中写入一些命令;
127.0.0.1:6379> set test-mult-key 100 OK 127.0.0.1:6379> watch test-mult-key OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECR test-mult-key QUEUED 127.0.0.1:6379> DECR test-mult-key QUEUED
2、客户端 2 在客户端 1 事务提交之前,操作修改该键值;
127.0.0.1:6379> DECR test-mult-key (integer) 99
3、客户端 1 提交事务;
127.0.0.1:6379> EXEC (nil)
从上面的结果可以看到如果使用 watch 之后,如果当前键值,在事务之外有了修改,那么当前事务就会放弃本次事务的执行。这样就实现了事务的隔离性。
1、如果在事务提交之后,有并发操作
这种情况下是没有问题的,Redis 会先把事务中的命令执行完成,然后再去执行后续的命令,因为 Redis 对于命令的执行是单线程的,这种情况下,可以保证事务的隔离性。
总结:事务的隔离性可以保证。
持久性(D)分析
Redis 是会存在丢数据的情况的,如果在数据持久化之前,数据库宕机,那么就会有一部分数据没有及时持久化,而丢失。
虽然他有RDB和AOF日志,但是要保证性能,所以不能保证真正的保存,所以可能存在一部分数据一部分数据没有及时持久化的情况
所以,Redis 中不能保证事务的持久性。
Redis 为什么不支持回滚?源码分析
Redis 中为什么没有提供事务的回滚,有下面两个方面的考量
1、Redis是单线程的,支持回滚会对 Redis 的简单性和性能有很大的影响;
2、Redis 中只有在 语法错误或者键值的类型操作错误 中才会出错,这些问题应该在开发中解决,不应该出现在生产中。
基于上面两点的考虑,目前 Redis 中不支持事务的回滚。