事务:可以一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序额地串行化的执行而不会被其他命令插入,不许加塞,是在一个队类中,一次性的顺序的,排他性的执行一系列命令
常用命令:
discard:取消事务,放弃执行事务块内的所有命令
exec:执行所有事务块内的命令
multi:标记一个事务块的开始
unwatch:取消watch命令对所有可以的监控
watch key ......:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断
情况一:正常执行:
1 输入multi,返回OK
2 写操作,set k1 v1,返回queued ,set k2 v2,返回queued ,表示加入队列,但是还没有执行这句话
3 get k2,返回queued,测试是否加入队列
4 输入exec,返回前几条命令的结果
情况二:放弃事务:
1 输入multi,返回OK
2 写操作,set k1 v1,返回queued ,set k2 2222,返回queued ,表示加入队列,但是还没有执行这句话
3 输入discard,返回OK
4 输入 get k2,返回v2,代表写操作没有发生,还是原来的值
情况三:全体连坐:
1 输入multi,返回OK
2 写操作,set k1 v1,返回queued ,set k2 v2,返回queued ,表示加入队列,但是还没有执行这句话
3 getset(故意错的) k3 v3,返回报错信息
4 set k4 v4,返回queued,还在加入队伍
5 输入exec,会返回报错信息,都没有写入成功
情况四:冤头债主:
1 输入multi,返回OK
2 输入 incr k1 (刚刚k1的值是v1,这个命令是有错的),返回queued
3 输入 set k2 22 ,返回queued
4 输入set k3 33 ,返回queued
5 输入exec,返回队列的操作结果,但是只有incr k1的结果是报错信息
情况三和情况四的区别是,情况三在加入队列的时候就报错了,所以最后会全部失败,而情况四加入队列没有保存,在执行队列的时候,检查有错,所以只有那一条返回是错的,其他返回都是正确的,有此可见,redis对事务是部分支持
情况五:watch监控:
悲观锁:每次去拿数据的时候都任务别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block知道它拿到锁,传统额关系型数据路里面就用到了很多这种锁机制,比如行锁,表锁,读锁写锁等,都是在操作之前上锁
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再次期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量 。
乐观锁策略:提交版本必须是大于当前版本才能执行更新
CAS(ChecK And Set):
例子:设置可用余额100,欠额0
set balance 100
set debt 0
操作一:
1 watch balance 返回OK
2 输入multi,返回OK
3 decrby balance 20 返回queued 余额减20
4 incrby debt 20 返回queued 欠额加20
5 exec 返回 80 20
操作二:再连一台redis终端(这简称二台)
1 watch balance 返回OK
2 输入multi,返回OK
3 set balance 800 ,返回OK (在二台操作,一台不知道)
4 decrby balance 20 返回queued 余额减20
5 incrby debt 20 返回queued 欠额加20
6 exec 返回nil,事务是失败的,
get balance 可以查看到是800,就像是乐观锁一样,需要再重新获取一次再进行事务的监视
操作三:
1 watch balance 返回OK
2 输入multi,返回OK
3 set balance 800 ,返回OK (在二台操作,一台不知道)
4 当监控到key被修改了,先输入unwatch,取消对所有key的监视锁,返回OK后,重新输入multi,开启事务
Watch指令,类似于乐观锁,事务提交时,如果Key的值已经被别的客户端改变,比如某个list已被别的客户端push/pop过了,真个事务队列都不会被执行
通过watch命令在事务执行之前监控了多个keys,倘若在watch之后有任何Key的值发生了变化,exec命令执行的事务豆浆被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败
事务的三个阶段:
开启:以multi开始一个事务
入列:将多个命令入队到事务中,街道这些命令并不会立刻执行,而是方法哦等待执行的事务队列里面
执行:有exec命令触发事务
事务三个特性:
单独的隔离操作:事务中的所有命令都会序列化,按顺序的执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何质量都不会被实际执行,也就不存在“”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个问题
不保证原子性:redis同一个事务中如果有一条,命令执行h失败,其后的命令仍然会被执行,没有回滚