Reference: https://blog.csdn.net/u011692780/article/details/81213010redis
1、事务的四大特性数据库
关系型数据库的事务具备四个特性:并发
1. 原子性.net
2. 一致性blog
3. 隔离性生命周期
4. 持久性队列
2、而在咱们redis数据库中,事务回事什么样子的呢?事务
首先咱们给出一个定义:redis的事务中,一次执行多条命令,本质是一组命令的集合,一个事务中全部的命令将被序列化,即按顺序执行而不会被其余命令插入监控
在redis中,事务的做用就是在一个队列中一次性、顺序性、排他性的执行一系列的命令。原理
事务的生命周期:
1. 事务的建立:使用MULTI开启一个事务
2. 加入队列:在开启事务的时候,每次操做的命令将会被插入到一个队列中,同时这个命令并不会被真的执行
3. EXEC命令进行提交事务
经常使用的关于事务的命令有:
1. MULTI:使用该命令,标记一个事务块的开始,一般在执行以后会回复OK,(但不必定真的OK),这个时候用户能够输入多个操做来代替逐条操做,redis会将这些操做放入队列中。
2. EXEC:执行这个事务内的全部命令
3. DISCARD:放弃事务,即该事务内的全部命令都将取消
4. WATCH:监控一个或者多个key,若是这些key在提交事务(EXEC)以前被其余用户修改过,那么事务将执行失败,须要从新获取最新数据重头操做(相似于乐观锁)。
5. UNWATCH:取消WATCH命令对多有key的监控,全部监控锁将会被取消。
注意:关于乐观锁等概念:
乐观锁:就像他的名字,不会认为数据不会出错,他不会为数据上锁,可是为了保证数据的一致性,他会在每条记录的后面添加一个标记(相似于版本号),假设A 获取K1这条标记,获得了k1的版本号是1,并对其进行修改,这个时候B也获取了k1这个数据,固然,B获取的版本号也是1,一样也对k1进行修改,这个时候,若是B先提交了,那么k1的版本号将会改变成2,这个时候,若是A提交数据,他会发现本身的版本号与最新的版本号不一致,这个时候A的提交将不会成功,A的作法是从新获取最新的k1的数据,重复修改数据、提交数据。
悲观锁:这个模式将认定数据必定会出错,因此她的作法是将整张表锁起来,这样会有很强的一致性,可是同时会有极低的并发性(经常使用语数据库备份工做,相似于表锁)。
那么,如今咱们来执行一次具体看看redis的事务机制:
首先我会开启事务,并向数据库中存储4条数据,能够看到没执行一条命令的时候都会显示入队,并不会返回执行结果,说明redis中在事务提交以前,其内部的全部命令将不会被执行:
那么,若是中间有命令出错了会怎样呢?如今我随便打几个字符试一试:
能够看出,在第三条命令中我随便打了几个字符,提交事务的时候并无成功,这也很符合咱们对事务的理解,嗯~具备原子性。可是,有一个细节,那就是错误命令在我输入的时候就已经报错了,也就是说这个条错误命令在进入队列的时候redis就已经知道这是一条错误命令,这样,整个事务的命令将所有失败,那么,有没有一种可能某个错误指令在进入队列的时候redis尚未发现他的错误呢?咱们试一试下面这个例子:
问题出现了,咱们能够看到,name+1这条指令实际上是错误的,可是提交事务的时候会发现,这条错误命令确实没有执行,可是其余正确的命令却执行,这是为何的?
缘由是在redis中,对于一个存在问题的命令,若是在入队的时候就已经出错,整个事务内的命令将都不会被执行(其后续的命令依然能够入队),若是这个错误命令在入队的时候并无报错,而是在执行的时候出错了,那么redis默认跳过这个命令执行后续命令。也就是说,redis只实现了部分事务。
下面咱们来看看刚刚提到的锁的问题,咱们说过,redis的锁CAS(check and set)相似于乐观锁,redis的实现原理是使用watch进行监视一个(或多个)数据,若是在事务提交以前数据发生了变化(估计使用了相似于乐观锁的标记),那么整个事务将提交失败,咱们能够举一个例子,咱们开启两个终端,模拟两我的的操做,设置一条数据为count,初始时100,如今A对其进行监控,而且为count增长20
在没有提交以前,B也获取了这个count,为其减小50,
那么这个时候A若是提交事务,会出现失败提示:
能够看到,在A对数据的修改过程当中,B对数据进行了修改,那么这条数据的“标记”就发生了变化,已经不是当初A取出数据的时候的标记了,这样,A的事务也就提交失败了。
最后经过上述的实验,咱们总结redis事务的三条性质:
1. 单独的隔离操做:事务中的全部命令会被序列化、按顺序执行,在执行的过程当中不会被其余客户端发送来的命令打断
2. 没有隔离级别的概念:队列中的命令在事务没有被提交以前不会被实际执行
3. 不保证原子性:redis中的一个事务中若是存在命令执行失败,那么其余命令依然会被执行,没有回滚机制
3、Redis中的事务为何没有原子性与watch锁
Redis事务能够一次执行多个命令,它先以 MULTI 开始一个事务, 而后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的全部命令
在传统的关系型数据中,只要有任意一条指令失败,则整个事务都会被撤销回滚,而在Redis中,中间某条指令的失败不会致使前面已作指令的回滚,也不会形成后续的指令不作,也所以得出 Redis 事务的执行并非原子性的。
multi,表明一段事务的开始
exec,表明一个事务的提交
queued,表明某个指令在队列中
可是,也有例外,好比以下这种状况
discard,表明某个事务撤销
watch 锁 ,在事务中不能改变被锁的值 (exec提交后返回nil)
4、总结
在redis中,对于一个存在问题的命令,若是在入队的时候就已经出错,整个事务内的命令将都不会被执行(其后续的命令依然能够入队),若是这个错误命令在入队的时候并无报错,而是在执行的时候出错了,那么redis默认跳过这个命令执行后续命令。也就是说,redis只实现了部分事务。
总结redis事务的三条性质:
1. 单独的隔离操做:事务中的全部命令会被序列化、按顺序执行,在执行的过程当中不会被其余客户端发送来的命令打断2. 没有隔离级别的概念:队列中的命令在事务没有被提交以前不会被实际执行3. 不保证原子性:redis中的一个事务中若是存在命令执行失败,那么其余命令依然会被执行,没有回滚机制--------------------- 做者:XCCS_澍 来源:CSDN 原文:https://blog.csdn.net/u011692780/article/details/81213010 版权声明:本文为博主原创文章,转载请附上博文连接!