Redis的事务(概念,用法与深度解读)

177 阅读7分钟

Redis的事务

www.redis.cn/topics/tran…

Redis的事务是什么

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

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

事务能干什么

一个队列中,一次性,顺序性,排他性的执行一系列命令

怎么用

image.png

单词中文
DISCARD丢弃; 抛弃

正常执行

image.png

放弃事务

image.png

全体连坐

image.png

冤头债主

image.png 这和连坐的区别是报错的时机都不一样 前者像编译时异常 后者是运行时才出现异常

Redis对事物的支持是部分支持

watch监控

悲观锁

悲观锁的意思就是认为拿数据时候认为别人会进行修改,所以每次在拿数据的时候都会上锁,其他要拿这个数据的就会阻塞直到拿到锁

传统的关系型数据库用到了很多这种锁机制,例如行锁,表锁,读锁,写锁等,都是在做操作之前先上锁

乐观锁

适合多读类型的情况

引入了版本号,A,B同时修改表中的一行数据,拿到的版本号都是1

如果A先commit提交,提交时候对比版本号是不是1,如果是1,提交成功,并将版本号设为2

如果B再commit提交,对比版本号就不是1了,报出异常,需要重新读区该条数据重新修改再提交(即:CAS算法)

watch指令类似于乐观锁事务提交时,如果key的值已被别的客户端改变,整个事务队列都不会被执行

CAS算法

乐观锁采用比较交换的技术(CAS Compare And Swap)

一旦检测到冲突产生,就重试当前操作直到没有冲突为止。

无锁如何鉴别冲突

CAS核心算法:执行函数:CAS(V,E,N)

  1. V表示准备要被更新的变量
  2. E表示我们提供的期望的值
  3. N表示新值,准备更新V的值

算法思路:V是共享变量,我们拿着自己准备的这个E,去跟V去比较,如果E == V ,说明当前没有其它线程在操作,所以,我们把N 这个值写入对象的V变量中。如果 E != V ,说明我们准备的这个E,已经过时了,所以我们要重新准备一个最新的E,去跟V比较,比较成功后才能更新V的值为N。

watch 示例

image.png

wathc的生效和失效

  1. watch命令可以被调用多次。对键的监视从watch执行之后开始生效

  2. 直到调用exec为止,不管事务是否成功执行,对所有键的监视都会被取消。

  3. unwatch命令可以手动取消对所有键的监视。

127.0.0.1:6379> WATCH balance
OK      //在这个过程中其他客户端使用了set balance 800
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCRBY balance 50
QUEUED
127.0.0.1:6379(TX)> INCRBY debt 50
QUEUED
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCRBY balance 50
QUEUED
127.0.0.1:6379(TX)> INCRBY debt 50
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 850
2) (integer) 70

UNWATCH

一旦执行了exec之前加的监控锁都会被取消掉了

Redis到底有没有事务

Redis 有事务的,但Redis并不都具备传统的ACID特性。

原子性

Redis 开始事务 multi 命令后,Redis 会为这个事务生成一个队列,每次操作的命令都会按照顺序插入到这个队列中。

但这并不保证原子性,redis的错误分为两种,一种是比较明显的语法错误,整个事务都不会被执行,但是另一种编译未错,运行错误时,其他语句仍然会执行 2.

Redis不支持回滚

这个其实跟 Redis 的定位和设计有关系

Redis 是完成操作之后才会进行 AOF 日志记录,AOF 日志的定位只是记录操作的指令记录。

Redis 应用的场景更多是对抗高并发具备高性能,所以 Redis 选择更简单,更快速无回滚的方式处理事务也是符合场景。

对比MySQL,MySQL有完善的 Redolog,并且是在事务进行 Commit 之前就会写完成 Redolog,Binlog:

image.png

一致性

事务具备一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否成功,数据库也应该是一致的。

redis虽然是多线程程序,但是其处理网络IO和执行客户端请求的只有一个线程,对于客户端而言是个单线程服务器。

所以redis一致性和隔离性都天然的好。

Redis执行发生错误时

Redis执行一个错误的事务,在事务执行的过程中会识别出来并进行错误处理,这些错误并不会对数据库作出修改,也不会对事务的一致性产生影响。

Redis挂掉或者宕机时

这和上文的aof以及rdb数据备份的机制和策略有关系,要保证数据的一致性必然牺牲了一定的效率,性能,吞吐量。

隔离性

隔离性指的是,数据库中有多个事务并发的执行,各个事务之间不会相互影响,并且在并发状态下执行的事务和串行执行的事务产生的结果是完全相同的。

Redis 因为是单线程操作,所以在隔离性上有天生的隔离机制,当 Redis 执行事务时,Redis 的服务端保证在执行事务期间不会对事务进行中断,所以,Redis 事务总是以串行的方式运行,事务也具备隔离性。

持久性

事务的持久性指的是,当一个事务执行完毕,执行这个事务所得到的结果被保存在持久化的存储中,即使服务器在事务执行完成后停机了,执行的事务的结果也不会被丢失。

这和上一篇文章redis的持久化中的aof以及rdb数据备份的机制和策略有关系。

Redis为什么需要事务

redis服务器本身而言是没有竞态的,将活跃的客户端一个一个取出,将客户端中的请求一条一条执行,所有的处理都是one by one的。

但是一个redis服务器会有多个客户端进行连接,他们之间可能会出现竞态的

举个例子:现在商城中只有一件物品A,现在有两名玩家B和C想要购买这个物品,购买商品的大致流程是:

  1. 客户端向服务器发起询问,商品还在商城中吗?
  2. 服务器对询问进行回答,商品还在。
  3. 客户端对服务器发起操作,减少用户的钱包金额,然后将物品这个键从商城中移除并在用户背包添加这个物品。
  4. 服务器执行完指令返回OK,购买完成。

按照1-4的顺序执行,商品也被多卖了

image.png

采用事务之后

执行顺序安装1-12,就不会有商品被多卖了

image.png

总结

  1. Redis 具备了一定的原子性,但不支持回滚;
  2. Redis 并不能用传统的一致性概念来看待。(或者说 Redis 在设计时就无视这点);
  3. Redis 具备隔离性;
  4. Redis 通过一定策略可以保证持久性。