1.事务的四大特性

  原子性(Atomicity):化学中的原子指不可再分的基本微粒,数据库中原子性强调事务是一个不可分割的整体,事务开始后所有操作要么全部成功,要么全部失败,不可能停滞在中间某个环节。如果事务执行过程中出错就会回滚到事务开始前的状态,所有的操作就像没有发生一样不会对数据库有任何影响。简单来说要么就是全部执行,即使有一条失败,也全部不执行。

  一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态,即一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性。

  隔离性(Isolation):当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离,比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转入钱。

  持久性(Durability):一个事务一旦被提交,则对数据库的所有更新将被保存到数据库中,不能回滚。

2.Redis的事务是什么样子?

  在此之前,学习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中的事务测试

  Redis事务可以一次执行多个命令,它先以MULTI开始一个事务, 然后将多个命令入队到事务中,最后由EXEC命令触发事务,一并执行事务中的所有命令。

  在传统的关系型数据中,只要有任意一条指令失败,则整个事务都会被撤销回滚,而在Redis中,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做,也因此得出 Redis 事务的执行并不是原子性的。

  也有以下命令参数执行错误的情况。

  还有watch锁,在事务中不能改变被锁的值。

4.总结

  在redis中,对于一个存在问题的命令,如果在入队的时候就已经出错,整个事务内的命令将都不会被执行(其后续的命令依然可以入队),如果这个错误命令在入队的时候并没有报错,而是在执行的时候出错了,那么redis默认跳过这个命令执行后续命令。也就是说,redis只实现了部分事务。

  总结redis事务的三条性质:

  1.单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断

  2.没有隔离级别的概念:队列中的命令在事务没有被提交之前不会被实际执行

  3.不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制

最新文章

  1. java时间
  2. 表达式括号匹配(stack)
  3. C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
  4. CO-类的本质、description方法
  5. 搜索引擎爬虫技术研究(爬虫框架)-WebCollector
  6. 验证页面多个input文本的必填项
  7. 20145312 GDB调试汇编堆栈过程分析
  8. LA 4123 - Glenbow Museum
  9. ctags对部分目录生成tags
  10. 制作SM2证书
  11. Compass 使用手册
  12. 通过js实现在页面中添加音乐
  13. Eclipse常用快捷键记录
  14. wkhtmltopdf
  15. RocketMQ环境搭建(双master模式)
  16. spring aop 切面编程中获取具体方法的方法
  17. 二分图学习——基础dfs判断二分图
  18. ubuntu创建用户命令
  19. MongoDB对Javascript的支持
  20. kindeditor asp.net 模板问题 clientidmode="Static"

热门文章

  1. Servlet-授课
  2. ES6中的Set和Map对象数据结构
  3. Linux信号与golang中的捕获处理
  4. nohup /usr/local/node/bin/node /www/im/chat.js >> /usr/local/node/output.log 2>&1 &
  5. ip_conntrack or nf_conntrack : table full, dropping packet
  6. shell脚本 在后台执行de 命令 >> 文件 2>&1 将标准输出与错误输出共同写入到文件中(追加到原有内容的后面)
  7. 用于监视Linux上的内存使用情况的Bash脚本
  8. python基础之pip、.pyc、三元运算、进制、一切皆对象、可变与不可变类型
  9. 重新整理 .net core 实践篇—————配置系统之军令状[七](配置文件)
  10. Hadoop MapReduce 一文详解MapReduce及工作机制