Redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行 。事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行——Redis没有原子性,Redis单条命令保证原子性,但是事务不保证原子性。Redis事务没有隔离级别的概念。所有的命令在事务中,并没有被直接被执行,只有发起执行命令的时候才会被执行——Exec。
Redis事务的三个阶段:1开启事务(multi) 2命令入队(……) 3执行事务(exec);
0Redis事务相关命令
Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的;1 redis 不支持回滚——Redis 在事务失败时不进行回滚,而是继续执行余下的命令, 所以 Redis 的内部可以保持简单且快速;如果在一个事务中的命令出现编译错误,那么所有的命令都不会执行;如果在一个事务中出现运行错误,那么正确的命令会被执行。
WATCH 命令是一个乐观锁,可以为 Redis 事务提供CAS行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
命令 | 特点 |
---|---|
MULTI | 命令用于开启一个事务,它总是返回OK ,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行 |
EXEC | 执行所有事务块内的命令 ,返回事务块内所有命令的返回值,按命令执行的先后顺序排列, 当操作被打断时,返回空值 nil |
DISCARD | 客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出 |
UNWATCH | 取消watch对所有key的监控 |
1事务管理(ACID)概述
Redis的事务总是具有ACID中的一致性和隔离性,其他特性是不支持的。Redis单条命令保证原子性,但是事务不保证原子性。
2监控!Watch 面试常问
https://blog.csdn.net/zs18753479279/article/details/115616451
悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加 version 来获取数据,性能较悲观锁有很大的提高。
正常执行成功
watch key # 监视值
unwatch # 放弃监视
exec # 执行事务
multi # 开启事务
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 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
执行失败,使用watch可以当redis的乐观锁操作
# 主线程
127.0.0.1:6379> watch 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 # 执行之前,另外一个线程修改了值,就会导致执行失败
(nil)
# 线程2
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
3乐观锁控制事务
大多是基于数据版本的记录机制。什么是数据版本?就是为数据增加一个版本标识,即为数据库表添加一个version字段,当读取数据时,把数据库版本一同读出,当做了修改后,将数据库版本+1,同修改一起提交。如果提交数据的版本号 >数据库当前版本号,提交成功。如图:
4事务实操
1正常执行事务
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 # 执行事务
1) OK
2) OK
3) "v2"
4) OK
2放弃事务
事务中的命令都不会被执行
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)
3编译型异常(代码有问题,命令有错 ),事务中所有的命令都不会执行
所有的命令都不会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec # 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 # 所有的命令都不会被执行
(nil)
4运行时异常(1/0),如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 "v1"
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range # 虽然这条命令报错,但是依旧执行成功
3) OK
4) OK
5) "v3"