您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

一篇文章带你彻底搞懂Redis事务

2024/3/13 2:01:46发布21次查看
本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于事务的相关内容,其本质上是一种命令的集合,事务支持一次执行多个命令,在事务执行过程中,会顺序执行队列中的命令;下面一起来看一下,希望对大家有帮助。
推荐学习:redis视频教程
redis 事务简介redis 只是提供了简单的事务功能。其本质是一组命令的集合,事务支持一次执行多个命令,在事务执行过程中,会顺序执行队列中的命令,其他客户端提交的命令请求不会插入到本事务执行命令序列中。命令的执行过程是顺序执行的,但不能保证原子性。无法像 mysql 那样,有隔离级别,出了问题之后还能回滚数据等高级操作。后面会详细分析。
redis 事务基本指令redis 提供了如下几个事务相关的基础指令。
multi开启事务,redis 会将后续命令加到队列中,而不真正执行它们,直到后续使用exec来原子化的顺序执行这些命令 exec执行所有事务块内的命令 discard取消事务,放弃执行事务块内所有的命令 watch监视一个或多个 key,若事务在执行前,这些 key 被其他命令修改,则事务被终端,不会执行事务中的任何命令 unwatch取消 watch命令对所有 keys 的监视
一般情况下,一个简单的 redis 事务主要分为如下几个部分:
执行命令multi开启一个事务。 开启事务之后,执行命令的多个命令会依次被放入一个队列,放入成功则会返回queued消息。 执行命令exec提交事务,redis 会依次执行队列中的命令,并依次返回所有命令的结果。(若想放弃提交事务,则执行discard)。
下图简单介绍了下 redis 事务执行的过程:
实例分析下面我们来通过一些实际具体例子,来体会下 redis 中的事务。前面我们也说到 redis 的事务不是正真的事务,是无法完全满足标准事务的acid特性的。通过下面的例子,我们来看看,redis 的“破产版”事务到底存在什么问题。
[a]正常执行提交
127.0.0.1:6379> multiok127.0.0.1:6379> set a 1queued127.0.0.1:6379> set b 2queued127.0.0.1:6379> exec1) ok2) ok127.0.0.1:6379> get a"1"127.0.0.1:6379> get b"2"
开启事务后,提交的命令都会加入队列(queued),执行 exec 后会逐步执行命令并返回结果。这个看起来是不是和我们平时使用 mysql 的事务操作相似,类似 start transaction 和 commit。
[b]正常取消事务
127.0.0.1:6379> multiok127.0.0.1:6379> set a 1queued127.0.0.1:6379> set b 2queued127.0.0.1:6379> discardok127.0.0.1:6379> 127.0.0.1:6379> get a(nil)127.0.0.1:6379> get b(nil)
开启事务后,若不想继续事务,使用 discard 取消,前面提交的命令并不会真正执行,相关的 key 值不变。这个看起来也和 mysql 的事务相似,类似 start transaction 和 rollback。
[c]watch 监视 key
-- 线程 1 中执行127.0.0.1:6379> del a(integer) 1127.0.0.1:6379> get a(nil)127.0.0.1:6379> set a 0ok127.0.0.1:6379> watch aok127.0.0.1:6379> multiok127.0.0.1:6379> set a 1queued----------------------------------------- 线程 2 中执行----------------------------------------- 127.0.0.1:6379> set a 2----------------------------------------- ok127.0.0.1:6379> exec(nil)127.0.0.1:6379> get a"2"
在开启事务之前 watch 了 a 的值,随后再开启事务。在另一个线程中设置了 a 的值(set a 2),然后再 exec 执行事务,结果为 nil,
说明事务没有被执行。因为 a 的值在 watch 之后发生了变化,所以事务被取消了。
需要注意的是,这里和开启事务的时间点没有关系,与 multi 和另一个线程设置 a 的值的先后没有关系。只要是在 watch 之后发生了变化。无论事务是否已经开启,执行事务(exec)的时候都会取消。
普通情况下,在执行 exec 和 discard 命令时,都会默认执行 unwatch。
[d]语法错误
127.0.0.1:6379> set a 1ok127.0.0.1:6379> set b 2ok127.0.0.1:6379> multiok127.0.0.1:6379> set a 11queued127.0.0.1:6379> sets b 22(error) err unknown command 'sets'127.0.0.1:6379> exec(error) execabort transaction discarded because of previous errors.127.0.0.1:6379> get a"1"127.0.0.1:6379> get b"2"
当 redis 开启一个事务后,若添加的命令中有语法错误,会导致事务提交失败。这种情况下事务队列中的命令都不会被执行。如上面例子中 a 和 b 的值都是原有的值。
这类在 exec 之前产生的错误,如命令名称错误,命令参数错误等,会在 exec 执行之前被检测出来,所以在发生这些错误的时候,事务会被取消,事务中的所有命令都不会执行。(这种情况看起来是不是有点像回滚了)
[e]运行时错误
127.0.0.1:6379> multiok127.0.0.1:6379> set a 1queued127.0.0.1:6379> set b helloqueued127.0.0.1:6379> incr bqueued127.0.0.1:6379> exec1) ok2) ok3) (error) err value is not an integer or out of range127.0.0.1:6379> get a"1"127.0.0.1:6379> get b"hello"
当 redis 开启一个事务后,添加的命令没有出现前面说的语法错误,但是在运行时检测到了类型错误,导致事务最提交失败(说未完全成功可能更准确点)。此时事务并不会回滚,而是跳过错误命令继续执行。
如上面的例子,未报错的命令值已经修改,a 被设置成了 1,b 被设置为了 hello,但是报错的值未被修改,即 incr b 类型错误,并未执行,b 的值也没有被再更新。
redis 事务与 acid通过上面的例子,我们已经知道 redis 的事务和我们通常接触的 mysql 等关系数据库的事务还有有一定差异的。它不保证原子性。同时 redis 事务也没有事务隔离级别的概念。下面我们来具体看下 redis 在 acid 四个特性中,那些是满足的,那些是不满足的。
事务执行可以分为命令入队(exec 执行前)和命令实际执行(exec 执行之后)两个阶段。下面我们在分析的时候,很多时候都会分这两种情况来分析。
原子性(a)
上面的实例分析中,[a],[b],[c]三种正常的情况,我们可以很明显的看出,是保证了原子性的。
但是一些异常情况下,是不满足原子性的。
如 [d] 所示的情况,客户端发送的命令有语法错误,在命令入队列时 redis 就判断出来了。等到执行 exec 命令时,redis 就会拒绝执行所有提交的命令,返回事务失败的结果。此种情况下,事务中的所有命令都不会被执行了,是保证了原子性的。 如 [e] 所示的情况,事务操作入队时,命令和操作类型不匹配,此时 redis 没有检查出错误(这类错误是运行时错误)。等到执行 exec 命令后,redis 实际执行这些命令操作时,就会报错。需要注意的是,虽然 redis 会对错误的命令报错不执行,但是其余正确的命令会依次执行完。此种情况下,是无法保证原子性的。 在执行事务的 exec 命令时,redis 实例发生了故障,导致事务执行失败。此时,如果开启了 aof 日志,那么只会有部分事务操作被记录到 aof 日志中。使用redis-check-aof工具检测 aof 日志文件,可以把未完成的事务操作从 aof 文件中去除。这样一来,使用 aof 文件恢复实例后,事务操作不会被再执行,从而保证了原子性。若使用的 rdb 模式,最新的 rdb 快照是在 exec 执行之前生成的,使用快照恢复之后,事务中的命令也都没有执行,从而保证了原子性。若 redis 没有开启持久化,则重启后内存中的数据全部丢失,也就谈不上原子性了。 一致性(c)
一致性指的是事务执行前后,数据符合数据库的定义和要求。这点在 redis 事务中是满足的,不论是发生语法错误还是运行时错误,错误的命令均不会被执行。
exec 执行之前,入队报错(实例分析中的语法错误)
事务会放弃执行,故可以保证一致性。
exec 执行之后,实际执行时报错(实例分析中的运行时错误)
错误的命令不会被执行,正确的命令被执行,一致性可以保证。
exec 执行时,实例宕机
若 redis 没有开启持久化,实例宕机重启后,数据都没有了,数据是一致的。
若配置了 rdb 方式,rdb 快照不会在事务执行时执行。所以,若事务执行到一半,实例发生了故障,此时上一次 rdb 快照中不会包含事务所做的修改,而下一次 rdb 快照还没有执行,实例重启后,事务修改的数据会丢失,数据是一致的。若事务已经完成,但新一次的 rdb 快照还没有生成,那事务修改的数据也会丢失,数据也是一致的。
若配置了 aof 方式。当事务操作还没被记录到 aof 日志时,实例就发生故障了,使用 aof 日志恢复后数据是一致的。若事务中的只有部分操作被记录到 aof 日志,可以使用 redis-check-aof清除事务中已经完成的操作,数据库恢复后数据也是一致的。
隔离性(i) 并发操作在 exec 执行前,隔离性需要通过 watch 机制来保证 并发操作在 exec 命令之后,隔离性可以保证
情况 a 可以参考前面的实例分析 watch 命令的使用。
情况 b,由于 redis 是单线程执行命令,exec 命令执行后,redis 会保证先把事务队列中的所有命令执行完之后再执行之后的命令。
持久性(d)
若 redis 没有开启持久化,那么就是所有数据都存储在内存中,一旦重启,数据就会丢失,因此此时事务的持久性是肯定无法得到保证的。
若 redis 开启了持久化,当实例宕机重启,还是会有可能丢失数据,因此也并能完全保证持久性。
因此,我们可以说 redis 事务无法一定保证持久性,仅在特殊的情况下,可以保证持久性。
关于 redis 在开启持久化之后,为啥还会丢失数据,笔者会单独整理一篇 redis 持久化与主从相关的文章来介绍,此处简单说下。
如果配置了 rdb 模式,在一个事务执行后,下一次 rdb 快照还未执行前,redis 实例发生了宕机,数据就会丢失、
如果配置了 aof 模式,而 aof 模式的三种配置选项 no,everysec,always 也都可能会产生数据丢失的情况。
总结一下,redis 事务对 acid 的支持情况:
具备一定的原子性,但不支持回滚 满足一致性 满足隔离性 无法保证持久性 redis 事务为什么不支持回滚
看一下官网的的说明:
what about rollbacks?
redis does not support rollbacks of transactions since supporting rollbacks would have a significant impact on the simplicity and performance of redis.
大部分需要事务回滚的情况是程序错误导致的,这种情况一般是开发环境,生产环境不应该出现这种错误。
对于逻辑错误,例如应该加 1,结果写成了加 2,这种情况无法通过回滚来解决。
redis 追求的是简单高效,而传统事务的实现相对复杂很多,这和 redis 的设计思想是违背的。当我们享受 redis 的快速时,也就无法再要求它更多。
推荐学习:redis视频教程
以上就是一篇文章带你彻底搞懂redis事务的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product