Redis事务
Redis事务的本质是一组命令的集合,一个事务的所有命令都会被序列化,在事务执行过程中会按照顺序执行。
Redis没有隔离级别的概念
Redis单条命令保证原子性,但事务不保证原子性。
所有的命令在事务中并没有直接被执行,而是发起执行命令后才会执行。
Redis事务:
- 开启事务(multi)
- 命令入队
- 执行事务(exec)
127.0.0.1:6379> MULTI #开启事务
OK
#命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v1"
4) OK
注意:
当事务执行完成后说明事务已经执行完毕,需要再次进行事务操作需要重新去开启事务。
放弃事务 :discard
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD #放弃事务
OK
127.0.0.1:6379> get k4 #获取不到值,因为事务被放弃,事务队列中的命令都不会被执行
(nil)
异常分析:
编译型异常:
代码有问题,命令写错。事务中所有的命令都不会被执行
运行时异常:
事务队列中存在着语法,当执行命令时存在的错误,正确的命令会正常执行,错误的命令会抛出异常。
监控 — Watch监视器
了解悲观锁和乐观锁
悲观锁:
- 无论做什么都要加锁,认为任何情况都会出问题
乐观锁:
- 认为什么时候都不会出问题,不会去上锁。
- 获取version
- 更新的时候比较version
我们通常使用乐观锁,因为悲观锁效率低下,无论什么时候都要去加锁。
测试:
正常执行状态下
单线程
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money #监视money对象
OK
127.0.0.1:6379> multi #事务正常结束,期间数据没有发生变动,这时候就正常执行成功。
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 90
2) (integer) 10
在多线程的情况下修改值,使用watch作为Redis的乐观锁操作。
127.0.0.1:6379> watch money #监视money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> incrby money 20
QUEUED
127.0.0.1:6379> exec #在执行之前另外一个线程修改了money对象值导致了事务执行失败。
(nil)
在另一个线程下修改money对象的值
127.0.0.1:6379> get money
"90"
127.0.0.1:6379> set money 1000
OK
解决:
如果修改失败,获取到最新的值即可。 先进行解锁操作,然后再次进行监视。
对比监视的值是否发生了变化,如果值没有发生变化,那么可执行成功。如果值在另外的线程中发生了变化就不能执行成功。 要想解决就要再次解锁,然后再次监视。