浅析Redis事务
Posted 风在哪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析Redis事务相关的知识,希望对你有一定的参考价值。
Redis事务
Redis事务是一个单独的隔离操作:事务的所有命令都回序列化、按顺序执行。事务在执行过程中,不会被其他客户端发送来的命令请求打断。
Redis事务主要作用就是串联多个命令,防止其他命令插队。
事务相关命令
Redis中与事务相关的命令有如下三种:
- multi:开启事务,会将开启事务后的命令按顺序加入到队列中(类似于mysql的start transaction),入队阶段
- exec:执行事务,会按照顺序执行事务队列中存储的命令(类似于MySQL的commit),执行阶段
- discard:丢弃事务,如果不想执行命令就丢弃这个事务队列中的命令(类似于MySQL的rollback)
例如:
事务失败处理
入队阶段错误处理
当我们开启事务之后,向事务队列中添加命令时,如果此时命令出错的话,那么该事务就会失败,不会执行任何一条语句。
例如:
可以看到,当我们入队的命令出错时,redis会直接报错,并且在我们执行事务队列中的命令时,redis会报错,直接丢弃事务队列中的所有命令,不会执行任何一条命令。
执行阶段错误处理
当我们提交命令阶段没有任何错误,但是命令的执行出错时,此时只会对执行失败的命令报错,其他命令会正常执行不受影响。
例如:
从这里我们可以发现,其实redis开启事务之后,提交命令时,只是简单的验证命令是否存在语法错误,不存在则将命令入队,而不会预处理我们提交的命令。
事务冲突问题
现在网购比较发达,我们假设有三个人同时拥有一个账户(该账户绑定的同一张银行卡),它们在同一时刻用同一个账户去购买商品,此时可能会出现一些问题。
问题描述
假设三个人同时去购买商品:
- A获取银行卡余额1000元,购买500元的商品
- B获取银行卡余额1000元,购买700元的商品
- C获取银行卡余额1000元,购买100元的商品
此时他们都任务银行卡余额为1000元,那么可能会引发如下问题,如图:
此时银行卡的余额成为了-300元(在现实中不会出现这种问题,因为银行们已经解决啦!)
那么我们如何解决这种问题呢,主要有两种方法,通过悲观锁或者乐观锁可以解决这个问题。
悲观锁
悲观锁通过名字就可以看出它很悲观,它认为每次访问共享资源时,一定会有其他用户去修改数据,也就是会产生多线程的冲突,所以它在每次访问数据时都会对数据加锁,这样其他用户访问数据时就要等待锁的释放之后才能访问。
在关系数据库中,如MySQL,就存在这种锁机制,包括表锁、行锁、读锁、写锁等。他们都是在操作之前先加上锁。
那么我们怎么通过锁机制解决这个问题呢?
我们每次访问数据获取银行卡余额时,都加上锁,这样其他用户在访问时由于没有锁,无法获取余额,所以会等待上一个用户释放锁,这样就不会出现银行卡余额负数的情况了,过程如下图所示:
虽然加锁解决了我们的问题,但是加锁的方式时间效率太低了,耗时。在网购过程中肯定不合适,那么有没有更好的方式呢?
接下来就是乐观锁机制了。
乐观锁
乐观锁就是很乐观,它认为访问共享资源时,不会有其他用户修改,也就是只有自己在使用这个资源,不会出现资源的竞争,也就不会产生冲突。所以它每次访问数据都不会上锁,但是它会在更新数据的时候判断其他人有没有更新这个数据,这就可以使用版本号机制来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
在关系型数据库MySQL中,通过MVCC(多版本并发控制)实现了这种乐观锁机制。
在redis中是利用check-and-set(检查版本号并设值)来实现事务的。
那么用乐观锁如何解决我们这里的问题呢:
我们可以对银行卡余额设置版本号,每次修改数据时都要比较现有数据的版本号和我们获得的版本号是否相同,版本号相同才能修改数据,修改完数据以后还要修改数据对应的版本号,通过check-and-set机制就可以不加锁来解决我们之前遇到的问题。流程如图:
watch命令
在redis中,我们可以通过watch命令来实现乐观锁,watch命令就是用于监控一个或者多个key,如果在事务执行之前,被监视的key被其他命令改动,那么事务将会被打断。这里watch就相当于进行了版本的比较,然后决定是否进行修改。
我们可以通过开启多个中断来模拟watch命令,在每个中断里面监控相同的key,然后分别执行事务,看被监控的key在事务执行中的变化。
首先我们设置money的值为1000,然后开启两个redis客户端,同时监控money。
然后在两个端口中分别开启事务,对money进行操作,此时第一个提交的事务可以对money进行修改,第二个提交的事务想要修改money就没办法了。这里watch就实现了一种乐观锁,当其他事务修改监控的key后,另一个事务就无法再对其修改了。
Redis事务的特性
单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
参考
以上是关于浅析Redis事务的主要内容,如果未能解决你的问题,请参考以下文章