6-Redis事务与锁
事务
ACID 要么同时成功 要么同时失败
事务等于一组命令的集合
相当于一组命令的队列 事务中的所有命令都会被序列化
会按照顺序 一次性 顺序执行 ,执行过程中不会被打断。
Redis的事务没有隔离级别的概念 所以不会出现传统数据库隔离级别(脏读、幻读、不可重复读)等情况
Redisa单条命令保证原子性 Redis事务不保证原子性。
所有的redis命令在事务中 并没有被直接执行,只有真正执行到这条命令的时候才执行
redis 事务:
- 开启事务(multi)
- 命令入队(具体命令)
- 执行事务(exec)
开启一个事务
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> set key3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK #key1 v1 创建成功
2) OK #key2 v2 创建成功
3) "v1" #get key1
4) OK #key3 v3创建成功
取消一个事务
执行完了 这个事务就没有了 如果需要 需要再次开启
127.0.0.1:6379> MULTI #上面执行完成事务就没有了 需要再次开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> set key5 v5
QUEUED
127.0.0.1:6379> DISCARD #取消事务
OK
127.0.0.1:6379> get key4 #key4 没有创建成功
(nil)
事务语法错误
整个事务不会被执行 有点像java的编译错误 代码语法有问题 编译不通过 不会生成.class文件
整个都不会编译成功
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> getou key3 v3 #队列中有一个命令是错误的
(error) ERR unknown command `getou`, with args beginning with: `key3`, `v3`,
127.0.0.1:6379> set key4 v4
QUEUED
127.0.0.1:6379> exec #执行事务的时候报错 整个事务不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key1 # 查询不到创建结果 所有命令没有执行
(nil)
事务逻辑错误
语法没问题 逻辑上的问题 只有逻辑错误的语句不执行,其它语句正常执行
像java运行错误,比如内存溢出问题 编译的时候不会出问题 运行也不会出问题 只有当内存真的溢出了才会有故障。
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 "测试"
QUEUED #进入队列
127.0.0.1:6379> incr key1 #key1自增 这个是错误的 字符串没法自增
QUEUED
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> set key3 v3
QUEUED
127.0.0.1:6379> get key3
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"
127.0.0.1:6379> get key3 #可以查询到key3的值
"v3"
127.0.0.1:6379>
监控watch
-
悲观锁 认为一直都会出问题 比较谨慎 做什么都先加锁 性能比较差
-
乐观锁 认为一直都不会出问题 所以不会上锁 更新数据的时候判断一下 是否有人修改过数据
2.1 获取version
2.2 更新的时候比较version
127.0.0.1:6379> set num 1000 OK 127.0.0.1:6379> set outmum 0 OK 127.0.0.1:6379> watch num #开启监控 OK 127.0.0.1:6379> MULTI #开启事务 OK 127.0.0.1:6379> DECRBY mun 20 QUEUED 127.0.0.1:6379> INCRBY outmun 20 QUEUED 127.0.0.1:6379> exec #事务正常执行 1) (integer) -20 2) (integer) 20 127.0.0.1:6379>
127.0.0.1:6379> set num 1000 OK 127.0.0.1:6379> set outnum 0 OK 127.0.0.1:6379> watch num OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRBY money 30 QUEUED 127.0.0.1:6379> INCRBY outnum 30 QUEUED 127.0.0.1:6379>
窗口2在窗口一事务还未执行的情况下 修改了num
127.0.0.1:6379> get num "1000" 127.0.0.1:6379> set num 100 OK 127.0.0.1:6379>
返回窗口1 执行事务失败返回nil
结论:在执行事务之前 另外一个线程修改了我们需要的值 这时候会导致事务执行失败
乐观锁
利用watch命令监听key,实现乐观锁 应用场景比如秒杀来防止超卖
127.0.0.1:6379>unwatch #如果执行失败 就先解锁 OK 127.0.0.1:6379>watch num #在重新上锁 获取最新的值 OK 127.0.0.1:6379> MULTI #开启事务 以上三步 如果失败 重新操作 OK 127.0.0.1:6379> DECRBY money 30 QUEUED 127.0.0.1:6379> INCRBY outnum 30 QUEUED 127.0.0.1:6379> exec