描述
Redis 事务本质:一个事务就是一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中按照顺序执行!
特性
Reids事务保证:一次性、顺序性、排他性!执行一系列的命令!
一次性:只有当所有的命令进入队列后,再一次性执行。
顺序性:按照进入队列的先后顺序执行,先进先出。
排他性:事务在执行过程中,是不允许被干扰的。
------- 队列 set set set 执行 ------
注意点:
-
Redis事务没有隔离级别的概念!
-
所有的命令在事务中,并没有直接执行!只有发起执行的命令的时候才会执行!
-
Redis 单条命令是保证原子性的,但是事务不保证原子性!
Redis的事务,需要做3步:
-
开启事务(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 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
放弃事务!
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 k3 v3 QUEUED 127.0.0.1:6379> discard # 取消事务 OK 127.0.0.1:6379> get k3 # 事务队列中的命令都取消了,不会执行! (nil)
编译型异常(代码有问题!命令有错的时候!)
- 事务中的所有命令都不会被执行!
127.0.0.1:6379> multi # 开启事务 OK 127.0.0.1:6379> set k1 v1 k2 v2 k3 v3 QUEUED 127.0.0.1:6379> getset k3 # getset key value,故意制造编译的错误 (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)
运行时异常(比如:1/0)
- 如果队列中存在与发行错误,其他命令是可以正常执行的,错误命令会抛出异常!
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> set k3 v3 QUEUED 127.0.0.1:6379> get k3 QUEUED 127.0.0.1:6379> exec # 只有错误语法的命令会报错,其他正常执行 1) (error) ERR value is not an integer or out of range 2) OK 3) OK 4) "v3" 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k1 "v1"
Redis 监视
正常执行
正常执行成功!
127.0.0.1:6379> set money 100 # 设置余额 100 OK 127.0.0.1:6379> set out 0 # 设置转出 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 # 余额减少 20 QUEUED 127.0.0.1:6379> incrby out 20 # 转出的钱 +20 QUEUED 127.0.0.1:6379> exec # 执行事务,如果数据期间没有发生变化,这个时候就正常执行成功! 1) (integer) 80 2) (integer) 20
测试多线程修改值,使用watch 可以当做redis乐观锁操作!
1、一个线程正在执行事务
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
2、另一个线程插队,将money的值修改为200
[root@zxh bin]# redis-cli -p 6379 127.0.0.1:6379> get money "100" 127.0.0.1:6379> set money 200 # 插队,将值修改 OK
3、再执行之前的事务
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 # 在执行之前,会进行对比,发现另一个线程修改了我们的值,这个时候,就会导致事务执行失败! (nil)
如果修改失败,获取最新的值就好了
127.0.0.1:6379> unwatch OK 127.0.0.1:6379> watch 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) 180 2) (integer) 20