一.Redis事务介绍
- Redis单条命令是保证原子性的(要么一起成功,要么一起失败),但是事务不保证原子性
- Redis事务没有隔离级别的概念
Redis事务本质:一组命令的集合!一个事务所以的命令都会被序列化,在事务执行过程中,会按照顺序执行!一次性,排他性,顺序性的执行一些命令!
1.Redis事务流程
- 开启事务(multi)
- 命令入队(..........一些命令)
- 执行事务(exec)
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> exec #启动事务(执行事务)
1) OK
2) OK
127.0.0.1:6379> get k1
"v1"
2.编译时异常会导致事务里面的命令全部失败---》命令入队中输入不存在的命令,会导致事务出错,整个事务里面的命令无效
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> setget k2 v2 #setget命令不存在,导致错误
(error) ERR unknown command `setget`, with args beginning with: `k2`, `v2`,
127.0.0.1:6379> exec #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 #获取k1的值失败,证明事务的命令全部失败
(nil)
3.运行时异常会导致事务里面的命令部分失败,部分成功。这也代表Redis事务不保证原子性---》命令入队中输入的命令逻辑(语法)有问题,会导致事务里面的该条命令失效,错误命令抛出异常,其他正确的命令正常执行。
场景:给k1的值设置v1字符串,然后在事务命令入队加入“incr k1”。 其他为正常命令
127.0.0.1:6379> set k1 v1 #设置一个v1字符串给k1
OK
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> incr k1 #让k1的值自增1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) (error) ERR value is not an integer or out of range #incr k1报错:是因为k1的值是v1是字符串,但是incr只能对整数加1,导致逻辑或者命令使用错误,但是不影响其他正确的命令
3) OK
4) "v1"
二.Redis乐观锁
1.Redis乐观锁通过Watch(监测)实现
- 乐观锁
很乐观,默认认为不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过数据,如果有就撤销操作
- 悲观锁
很悲观,默认什么时候都会出现问题,无论做什么都会加锁
2.Watch正常流程。没有其他操作修改money或者out数据
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 这个key的值
OK
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> decrby money 20 #花费20元,money减少20元
QUEUED
127.0.0.1:6379> incrby out 20 #店铺收入20元,out加20元
QUEUED
127.0.0.1:6379> exec #执行事务,数据正常
1) (integer) 80
2) (integer) 20
3.测试多线程修改money或者out数据,使用watch可以当做redis的乐观锁操作
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 这个key的值
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 #在执行事务之前,多线程下某个用户修改了money的值,就会导致事务执行失败
(nil)
4.如果watch监测某个值,出现并发的情形,事务执行失败,可以使用unwatch解除监测,然后再次watch加监测
127.0.0.1:6379> unwatch #如果发现事务执行失败,解除监测
OK
127.0.0.1:6379> watch mnony #获取最新的值,重新监测
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) 980
2) (integer) 20