Redis中的事务
Redis的事务定义
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
Redis中的事务操作Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard来放弃组队。
事务的错误处理
案例:
1.组队成功,提交成功,如下图:
2.组队阶段语法错误,提交失败事务不进行回滚,如下图:
在开启事务之后,将key1的值从value1修改成value11,key2改成value22,key3改成value33,但由于key2处语法错误,导致事务提交失败,key1、key2和key3保持原值。
3.在组队阶段出现运行时错误,事务并没有进行回滚,而是跳过错误内容继续执行,如下图:
在开启事务之后,将key1的值从value1修改成value11,key2改成value22,key3改成value33,但将key2的类型作为List,在运行时检测类型错误,最终导致事务提交失败。此时事务并没有回滚,而是跳过错误命令继续执行, 结果key1和key3的值改变、key2保留原值。
4.使用discard命令取消组队,如下图:
在上图中开启事务之后,执行了一系列的操作后使用discard命令,放弃组队,前面的操作都没有执行。
综上,我们可以得出下列结论
1.组队中某个命令出现了报告错误(语法错误、编译错误),执行时整个的所有队列都会被取消,如下图:
2.如果执行阶段某个命令报出了错误(运行时错误),则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚,如下图:
事务冲突问题
悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
悲观锁主要分为共享锁和排他锁:
**共享锁(shared locks)**又称为读锁,简称 S 锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
**排他锁(exclusive locks)**又称为写锁,简称 X 锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。获取排他锁的事务可以对数据行读取和修改。
乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
乐观锁的实现:
1.CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
2.版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
Redis中的事务冲突问题解决
watch
使用:
watch key1 key2 ...
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些)key 被其他命令所改动,那么事务将被打断。
例:
我在两个不同的终端进行操作示范
终端1:
终端2:
上图中终端1和终端2的执行过程几乎是一致的,都是从watch开始执行,但是终端2执行exec命令时表示执行失败,这是因为终端1先与终端2执行了对balance的修改操作,导致终端2的事务被打断了。
unwatch
使用:
unwatch key1 key2 ...
取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
Redis事务的三大特性
- 单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 - 没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行 - 不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚