介绍
可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞!
能干嘛
一个队列中,一次性、顺序性、排他性的执行一系列命令。
怎么玩
常用命令
正常执行
-- 开启事务
multi
--关闭事务
exec
我们结束事务之后再使用 keys * ,可以发现刚才在事务中写入的数据是存在的!
放弃事务
-- 放弃事务
discard
因为我们放弃了事务,所以刚才的写操作不会被执行,查出来的还是我们第一次写入的 v2
全体连坐
全体连坐,顾名思义就是指在事务执行的时候如果其中有一个地方出错,那么不管其它的操作能不能正常执行,一律都不会被执行!
上图圈起来的部分可以看到,我们不按格式去给他执行操作,出现了错误,这个时候它不会直接给你跳出来,而是可以继续进行执行操作,但是在我们退出来的时候就会提示 “EXECABORT事务由于先前的错误而被丢弃”!也就表示里面的操作都已经被丢弃了,不会被执行!
冤头债主
顾明思议就是指谁出错谁就不被执行,而成功的就照成执行
因为我们在之前给 k1 设置的值为 v1,是非整数类型,所以执行 incr 的时候就会导致报错!
执行完后我们可以发现除了 incr 那一行以外,其他的部分都正常执行了!
但是这里大家可能会有点好奇,为什么之前在事务中执行错误的命令会导致全部不执行,而这里却只是单独的不执行呢?
因为在 redis 中,事务是部分支持的,而不是全部都支持,所以在 incr 命令执行错误的时候只会让它单独执行失败,而不会影响到其它的命令执行!
watch监控
悲观锁
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会进入等待直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
乐观锁策略:乐观锁会在每条记录的后面加上一个版本号,提交的版本号必须大于记录的当前版本号才能执行更新!
并发性和一致性的对立
假设我们现在有一张表,有n条记录
乐观锁和悲观锁有点像数据库中的行锁和表锁
表锁:给整张表加锁,表示一旦上锁,当前表只能允许我自己修改,别人不能修改!虽然这样很好的保证了安全性,基于少数记录时还好,但是如果有几万或几十万条记录,这个时候我们需要修改666行记录的数据,别人又无法进行修改,这个时候操作就会处于排队状态,一两个还好,但是一直累积,到最后压力过大导致宕机!所以并发性差,但一致性好。
行锁:给某行加锁,
CAS
例子:初始化信用卡可用余额和欠额
比如我这个月借了10块钱花呗,那么我下个月就需要还10块钱。也就是需要一边扣除余额,一边增加欠额
这种情况在 redis 当中就需要将这两个操作放进同一个事务中进行处理,执行完了之后一边减一边增。在执行的时候就需要进行监控
-- 监控对应key值
watch key
我们可以看到,结束事务之后正常修改了余额和欠额
上图可以看到,我们监控后就表示给该key值加了锁,在加锁状态下修改了它的数据,然后在启动事务进行借钱操作,这样是不会正常执行的,因为它需要先获取到最新的值才能够去进行事务提交。
注:此处我启动了两个redis(一个在VM,一个在Xshell),大家不要觉得我只启动了一个redis来执行。
上图可以看到,我们在监控下修改余额之后,我们直接使用 unwatch 停止本次的监控(因为不停止就会出现刚才的错误),然后再次开启监控,这个时候我们开启事务修改余额和欠额,最后关闭事务就可以发现都执行成功了!这是因为停止后数据会获取到最新的,然后我们在去进行监控的时候就已经是最新的数据了,所以就可以正常的修改里面的数据!
PS:还有一个需要知道的就是,我们在执行 exec 或者 unwatch 命令的时候之前的监控锁都会被自动取消掉!
3 阶段-回顾
开启
以 multi 开启事务
入队
将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行
有 exec 命令触发事务
3 特性-回顾
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
- 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚