Version: Next
Redis事务
Redis单挑命令保证原子性,但是redis的事务不保证原子性
Redis事务的本质:
- 一次性:一组命令的集合
- 顺序性:会形成队列顺序执行
- 排他性:在事务执行过程中,不受其他影响
-----setsetset-----
caution
redis事务没有隔离级别的概念,所有的命令在事务中并没有被直接执行,只有发起执行命令的时候才会执行
正常执行事务
Redis事务的三个阶段:
- 开启事务——
multi
- 命令入队
- 执行事务——
exec
开启事务
一旦执行了multi
指令开启事务,接下来的指令就会被放到队列里
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 k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379>
执行exec
指令将队列中的指令一次性执行
127.0.0.1:6379> exec
1) OK
2) OK
3) "v2"
4) OK
入队列时指令并没有执行,只有执行exec
指令时队列中的指令才被真正执行了
放弃事务
discard
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> set k4 v4
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k4
(nil)
编译型异常
- 在一组事务中,有一步操作存在语法错误,是编译期就可以发现的错误,则整个事务都不会执行
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> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction descarded because of parevious errors
127.0.0.1:6379> get k1
(nil)
运行型异常
- 对字符串执行自动加1
- 只有错误语句失败了,事务中的其他命令依然可以执行成功,因此说Redis的事务不保证原子性
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) "v2"
127.0.0.1:6379>
Redis实现乐观锁
悲观锁
无论做什么都会加锁
乐观锁
认为不会出什么问题,不会上锁
- 在更新数据时,判断在此期间是否有人修改过数据
Redis监视测试——watch
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> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
事务正常结束,没有异常
模拟多线程情况,多开Redis客户端
线程1
写一些操作,但并未提交事务
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>
线程2
第二条线程修改了共享资源的值
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379>
此时线程1提交,watch指令会发现共享数据被修改过了,则事务提交失败
127.0.0.1:6379> exec
(nil)
此时,要
unwatch
释放监视,然后重新用watch
对共享资源进行监视
127.0.0.1:6379> unwatch # 1.如果事务失败,就先unwatch放弃锁
OK
127.0.0.1:6379> watch money # 2.再次监视,获取新锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 1
QUEUED
127.0.0.1:6379> INCRBY money 1
QUEUED
127.0.0.1:6379> exec # 3.比对监控值是否发生变化,没变化就执行成功
1) (integer) 990
2) (integer) 1000