前言:宁可一思进,莫要一思停
文章大部分参考尚硅谷redis课程的笔记。
事务
为什么需要事务(经典例子)
经典的银行转账行为,A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。
redis的原子性
- redis事务的原子性是说的:一次事务提交的多个命令,要么都不执行(watch发现某个key的值变了则不开始执行),要么所有的命令都执行(一旦开始执行事务,事务中所有的命令都会执行,即使有命令报错了,后面的命令也会执行)
- redis的报错并不会回滚
-只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力。
是什么
可以一次执行多个命令,本质是一组命令的集合。一个事务中的
所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞能干吗
一个队列中,一次性、顺序性、排他性的执行一系列命令
常用命令
redis的处理事务的五种方式
正常执行
正常执行的相关的命令
放弃事务
输入discard会放弃当前事务
放弃全部
当redis可以直接判断出命令出错时会放弃当前的事务
个体放弃
当执行的事务时命令出错不会影响事务的进行
Watch监控
- Redis使用WATCH命令实现事务的“检查再设置”(CAS——Check And Set)行为。
- 作为WATCH命令的参数的键会受到Redis的监控,Redis能够检测到它们的变化。在执行EXEC命令之前,如果Redis检测到至少有一个键被修改了,那么整个事务便会中止运行,然后EXEC命令会返回一个Null值,提醒用户事务运行失败。所有的WATCH命令都会在被调用之时立刻对相应的键进行监控,直到EXEC命令被调用之时为止
乐观锁
- 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,
- 乐观锁策略:提交版本必须大于记录当前版本才能执行更新
悲观锁
- 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
watch监控示范
背景:old给new转账(无加塞)
无加塞篡改,先监控再开启multi,
保证两笔金额变动在同一个事务内
old被转账(有加塞)
监控了key,如果key被修改了,后面一个事务的执行失效
unwatch取消监控
- 在知道watch的key被更改时可以unwatch
疑问:既然EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD命令在取消事务的同时也会取消所有对 key 的监视,那unwatch有什么用?
小结
- Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变, 比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行
- 通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化, EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败
3阶段
- 开启:以MULTI开始一个事务
- 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
- 执行:由EXEC命令触发事务
3特性
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
不遵循传统的ACID中的AI