【Redis 系列】redis 学习六,redis 事务处理和监控事务

写在后面

咱们学过的事务都是保障原子性的,然而 redis 的事务中执行多个指令,是不保障原子性的

redis 事务的实质

就是一组命令的汇合,一个事务中所有的命令都会被序列化,在事务执行的过程,是依照程序执行命令的,他们领有

  • 一次性
  • 程序性
  • 排他性

redis 的事务没有隔离级别的概念

redis 事务中,命令是这样执行的

命令放在事务中,并没有马上执行,而是发动执行命令的时候才会执行,通过 exec 触发

redis 是单条指令保障原子性,然而事务不保障原子性

执行一个事务的流程是这个样子的:

  • multi 开启事务
  • 各种命令入队
  • exec 执行事务

开启事务

MULTI

开启一个事务

EXEC

执行事务外面的指令

执行事务结束后,须要再应用事务,那么须要再次开启事务

127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set k1 v1QUEUED127.0.0.1:6379(TX)> set k2 v2QUEUED127.0.0.1:6379(TX)> set name xiaozhuQUEUED127.0.0.1:6379(TX)> set age 19QUEUED127.0.0.1:6379(TX)> set hobby palyQUEUED127.0.0.1:6379(TX)> exec1) OK2) OK3) OK4) OK5) OK127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set city changshaQUEUED127.0.0.1:6379(TX)> get cityQUEUED127.0.0.1:6379(TX)> exec1) OK2) "changsha"

放弃事务

DISCARD

放弃最近开启的事务

127.0.0.1:6379> flushdbOK127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set name xiaozhuQUEUED127.0.0.1:6379(TX)> set age 10QUEUED127.0.0.1:6379(TX)> set city beijingQUEUED127.0.0.1:6379(TX)> DISCARDOK127.0.0.1:6379> get city(nil)127.0.0.1:6379> get name(nil)

事务出错

事务出错分为两种:

  • 编译型出错
  • 运行时出错

编译型出错

代码编写出错,无奈通过编译,此时事务外面编写的指令,全副执行失败

127.0.0.1:6379> flushdbOK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> set name xiaozhuQUEUED127.0.0.1:6379(TX)> set age 10QUEUED127.0.0.1:6379(TX)> set hobby playQUEUED127.0.0.1:6379(TX)> set city changshaQUEUED127.0.0.1:6379(TX)> set dream xxxQUEUED127.0.0.1:6379(TX)> setget hahaha  # 指令不存在,编译不通过,间接报错(error) ERR unknown command `setget`, with args beginning with: `hahaha`,127.0.0.1:6379(TX)> exec(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get city(nil)

一旦编译出错,事务外面的所有指令都不会执行

运行时出错

程序运行时某一个指定的某个逻辑呈现问题,仅仅影响本条执行的执行,并且本条指令会跑出异样,不影响事务中其余指令的执行

127.0.0.1:6379> flushdbOK127.0.0.1:6379> set name xiaozhuOK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> get nameQUEUED127.0.0.1:6379(TX)> INCR name  # incr 指令用法正确,只不过 name 是字符串,运行的时候,是无奈执行通过的QUEUED127.0.0.1:6379(TX)> set age 29QUEUED127.0.0.1:6379(TX)> set hobby playQUEUED127.0.0.1:6379(TX)> exec1) "xiaozhu"2) (error) ERR value is not an integer or out of range3) OK4) OK127.0.0.1:6379> get hobby"play"127.0.0.1:6379> get name"xiaozhu"

根据上述后果,incr 指令的用法正确,然而 incr 只能对数字进行操作,name 是一个字符串,无奈做自增操作,因而运行时报错,事务中不影响其余的指令执行

redis 的 watch 监控

watch 监控,能够说说 乐观锁 和 乐观锁

乐观锁

  • 很乐观,认为什么时候都不会出问题,所以不会上锁,就更新数据的时候会去判断一下在此期间若数据产生了变动
  • 乐观锁会先获取一个根底数据版本
  • 更新数据的时候,会比拟这个版本是否发生变化,若发生变化,则更新失败,若未发生变化,则更新数据

乐观锁

  • 很乐观,无论什么时候都认为会出问题,都要加锁

redis 的监控测试

用 watch 来加锁模仿银行取钱

  • money 账户 有 2000
  • outer 账户 有 500
  • money 账户 给 outer 账户 500
127.0.0.1:6379> flushallOK127.0.0.1:6379> set money 2000OK127.0.0.1:6379> set outer 500OK127.0.0.1:6379> watch moneyOK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> DECRBY money 500QUEUED127.0.0.1:6379(TX)> INCRBY outer 500QUEUED127.0.0.1:6379(TX)> exec1) (integer) 15002) (integer) 1000

开始测试模仿多个线程来操作

redis 客户端 1

  • money 账户 有 2000
  • outer 账户 有 500
  • money 账户 给 outer 账户 500 ,还未开始执行
127.0.0.1:6379> set money 2000OK127.0.0.1:6379> set outer 500OK127.0.0.1:6379> watch moneyOK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> DECRBY money 500QUEUED127.0.0.1:6379(TX)> INCRBY outer 500QUEUED

客户端 1 的事务操作还未开始执行,客户端 2 就进行了如下操作

127.0.0.1:6379> set money 10000OK127.0.0.1:6379> set money 10000OK

此时执行客户端 1 的 exec 指令

127.0.0.1:6379(TX)> exec(nil)127.0.0.1:6379> get money"10000"

发现客户端 1 执行 exec 的时候,是一个 nil ,阐明变更失败,是因为 watch 监控到 money 有变动,因而事务执行失败

unwatch

发现事务执行失败了,须要应用命令 unwatch 解除监控

127.0.0.1:6379> UNWATCHOK127.0.0.1:6379> watch moneyOK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> DECRBY money 1000QUEUED127.0.0.1:6379(TX)> INCRBY outer 1000QUEUED127.0.0.1:6379(TX)> exec1) (integer) 90002) (integer) 1500

这就是 redis 实现乐观锁的操作,个别应用场景会放到秒杀零碎外面进行利用

参考资料:

redis_doc

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

我是小魔童哪吒,欢送点赞关注珍藏,下次见~