redis事物学习笔记

Redis事物概念

首先非常重要的一个概念: redis是单线程执行命令的,也就是说用户所有的请求都会被串行化,redis同一时间内只会帮用户执行一条命令,其余的都需要进行等待。

Redis 事务的本质是一组命令的集合。

事务支持一次执行多个命令,一个事务中所有命令都会被序列化。

在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

lua脚本是怎么保证原子性的?

Redis使用同一个Lua解释器来执行所有命令,同时,Redis保证以一种原子性的方式来执行脚本:当lua脚本在执行的时候,不会有其他脚本和命令同时执行,这种语义类似于 MULTI/EXEC。从别的客户端的视角来看,一个lua脚本要么不可见,要么已经执行完。

然而这也意味着,执行一个较慢的lua脚本是不建议的,由于脚本的开销非常低,构造一个快速执行的脚本并非难事。但是你要注意到,当你正在执行一个比较慢的脚本时,所以其他的客户端都无法执行命令。

当然所有对redis操作时间过长的命令,都不建议使用,例如 keys * ,如果数据量过大的话,redis会一直在查找,导致后续的命令无妨执行,从而客户端可能也在不停的等待,造成系统Hang住。

如上图,所有的命令都在排队等待redis执行。

Redis事务没有隔离级别的概念

批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。

Redis不保证原子性

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

也就是说,Redis的事物和传统的关系型数据库(例如MySQL)是不同的,MySQL可以保证一个事物中其中一步执行失败,其余的数据都回滚成修改前的状态,但是Redis不可以,其中一个数据修改失败了,其余的依旧正常执行。

Redis事务的三个阶段

  1. 开始事务
  2. 命令入队
  3. 执行事务

Redis事务相关命令

  1. watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )

  2. multi : 标记一个事务块的开始( queued )

  3. exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )

  4. discard : 取消事务,放弃事务块中的所有命令

  5. unwatch : 取消watch对所有key的监控

Redis事物使用示例

正常执行

通过 multi 开启一个事物 , 随后我们按序存入了 k1,k2,k3 ,

并且最终get获取k1的结果,

但是我们输入这些命令后,

redis给我们返回的结果都是QUEUED,

意味着它并没有立即执行,而是存入了一个队列,

等到我们输入exec这个命令后,redis才真正的执行了我们上面的命令,并且按照顺序返回了结果。

取消事物

首先我们获取k1的值,这时候是k1,

当我们开启一个事物,将k1的值改为change后,

取消了事物,

最终再确认一下,发现k1的值没有更改还是k1,说明事物被成功取消,没有执行。

事物执行过程中,发生语法错误

首先获取k1,值也是k1,

获取k5的值是空 nil,不存在

随后开启一个事物,并设置k1为change,

然后设置k2时发生语法错误,

再设置k5,最终执行事物,

我们看到redis返回的结果是事物执行失败,取消了事物,

这时候看k1和k5的值和执行事物之前的一样,说明发生语法错误的话,整个事物的命令都将不会执行。

这种错误类似于编译错误,如果java代码编译失败的话,后续将不会执行。

事物执行过程中,发生命令执行错误

首先获取k1,值也是k1,

获取k5的值是空 nil,不存在

随后开启一个事物,并将k1的值进行自增操作,

但是因为k1的值是字符串类型,无法自增,这时候命令语法是没有错误的,存入了队列当中,

随后我们设置k5的值为change,

redis这时候第一个命令发生运行时错误,自增失败,但是第二个命令设置k5成功了,

这时候k1的值依然是k1,但是k5的值是change了,

这类似java的运行时异常,通过语法检测后,redis开始执行命令,只要命令的语法没有错误,redis不关心结果有没有执行成功,依然会继续去执行下一条命令,所以说redis的事物和mysql等关系型数据库不一样,mysql如果发生执行失败,会进行回滚,redis不会。

使用watch监听数据

首先我们设置一个k1 和 money的值,分别是k1 和 100,

随后我们watch 监听 这个money,然后开启一个事物,

在事物中修改k1的值为666,将money自增1,变成101,

这时候我们不要执行exec事物,而是打开另一个客户端,执行修改money的命令,

随后我们回到刚刚的事物命令客户端,执行exec,

这时候我们可以看到,执行exec后,redis返回的结果是nil,

这说明当我们监听一个key后,执行事物过程中,如果这个key的值被外界因素改动,当前事物则会被取消,所有的命令都将不会执行,只有当被监听的key没有被改变时,事物才可以成功。

如上,执行事物中,没有修改监听的key,则事物执行成功了。

watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

猜你喜欢

转载自blog.csdn.net/cainiao1412/article/details/109075117