欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个Redis事务,可以让你征服面试官半小时。

程序员文章站 2022-04-07 21:53:41
...

理解原子性

我们知道,java中的 i++ 或者 ++i 这些自增操作不具备原子性,因为自增操作在我们代码层面是一个指令,但是在 jvm 底层,它分为这两个步骤:

  1. 从局部变量表中读取 i 的值压入操作数栈
  2. 将局部变量表中 i 的值加1

若 jvm 在执行第 1 步的时候,其他线程先于这个线程改变了 i 的值,然后执行第二步后得出的值就不是我们希望的。

在 redis 中也有类似的自增操作,如我们之前学的 string 数据类型中的 incr 指令,它可以对一个 integer 类型的值加1。但是 incr 指令与 java 自增不一样的是,它是一个原子操作,因为 incr 是调用操作系统底层的指令,它不会被其他线程打断。所以 incr 指令具有原子性。

为什么叫原子性?

原子性其实就是有类似原子的性质,我们知道原子中有质子和中子,他们构成了原子,不可分割,如果分割了,就不是一个原子了,是不是很好理解。整理redis的同时也整理了最近两个月最新的面试题。需要的朋友可以点击:这个,点这个!!,备注:csdn。
一个Redis事务,可以让你征服面试官半小时。

什么是事务

事务就是指一系列操作,这些操作要么同时成功,要么同时失败,它是一种原子操作。原子就是一个整体,不可分割,事务中的这些操作也是一样。
假设有这样一个场景:张三向李四转账 100 块钱,最少需要经历两个步骤

  1. 张三的银行卡中钱减100块
  2. 李四的银行卡中钱加100块

在没有事务的情况下,可能会导致 1 号操作执行成功,2 号操作执行失败,那结果是张三的钱少了 100 块,李四的钱却没增加,张三的 100 块凭空消失了,这肯定会出问题,所以这两部操作必须要放在事务中执行,要么转账成功,要么转账失败,这就是事务的作用。

redis事务

redis 是单线程的程序,每个指令都保证原子性,但是不保证多个指令的原子性。
当有多个客户端连接同一个 redis 时,客户端之间会竞争cpu资源,来争夺 rdis 指令的执行权,所以如果一个 client 要执行一系列指令时,可能会被其他客户端打断。

要使多个指令也具有原子性,可以使用 redis 事务
redis 事务保证两点:

  1. 执行一个事务时,所有的命令都会被顺序执行,且不会被其他客户端打断
  2. 执行一个事务时,所有命令要么同时成功,要么同时失败

如果 redis 开启 AOF 模式,在执行 redis 事务之前,redis 会先把事务写在磁盘中再执行命令。如果事务执行到一半,redis 进程突然就挂了,那么就会导致事务中只有一部分指令执行成功,还有一部分指令还未执行。这种情况下,在 redis 下一次启动时会检测到错误,并退出 redis。使用 redis-check-aof 工具可以对 AOF 文件进行修复,删除掉磁盘中的未执行成功的事务,然后重新启动加载 AOF 文件,redis 就恢复到事务没有执行创建的状态,就可以正常启动了

事务相关的命令

和 redis 事务有关的指令有:

  • multi
  • exec
  • discard

下面开始介绍每个命令

multi

使用 multi 指令来开启事务

127.0.0.1:6379> set zhangsan 1000
OK

127.0.0.1:6379> set lisi 1000
OK

127.0.0.1:6379> multi
OK

127.0.0.1:6379> incrby zhangsan -100
QUEUED

127.0.0.1:6379> incrby lisi 100
QUEUED

multi 指令返回 ok,证明事务开启成功。
开启事务后,接下来执行每条正确的指令都返回 QUEUE,代表命令被放入等待队列,但并没有执行。

exec

使用 exec 命令来执行事务

127.0.0.1:6379> set zhangsan 1000
OK

127.0.0.1:6379> set lisi 1000
OK

127.0.0.1:6379> multi
OK

127.0.0.1:6379> incrby zhangsan -100
QUEUED

127.0.0.1:6379> incrby lisi 100
QUEUED

127.0.0.1:6379> exec   # 执行事务
1) (integer) 900
2) (integer) 1100

使用 exec 执行事务后,会按顺序返回事务中每天指令的返回结果。
还整理了Java核心的知识点,大家也可以看看。需要的朋友可以点击:这个,点这个!!,备注:csdn。
一个Redis事务,可以让你征服面试官半小时。

discard

如果事务创建成功,但你又不想执行了,使用 discard 命令来取消

127.0.0.1:6379> multi                  # 开启事务
OK

127.0.0.1:6379> incrby zhangsan -100
QUEUED

127.0.0.1:6379> incrby lisi 100
QUEUED

127.0.0.1:6379> discard                 # 取消事务
OK

redis 事务乐观锁

乐观锁,即认为所有时候都不会出现问题,它不会去加锁,他会在修改数据的时候判断这个数据是否被改动过,如果没有被其他人改动过,就更新数据。

在 redis 中也有乐观锁的操作,来防止数据出现问题,现在有如下一个场景:

你想通过事务对 redis 中的 key/value 进行修改,在没有执行 exec 命令之前,这时候另一个 client 修改了这个 key/value,这样再执行 exec 后可能会出现问题。这种情况下用 redis 的乐观锁操作就可以很轻松解决
在 redis 中有一种 check and set(CAS)的乐观锁操作,相关命令如下

  • watch
  • unwatch

watch

通过 watch 命令来监视这个 key/value,如果发现该 key/value 被修改,则调用 exec 时事务不会被执行

127.0.0.1:6379> watch zhangsan lisi   # 监视 zhangsan 和 lisi
OK

127.0.0.1:6379> multi
OK

127.0.0.1:6379> incrby zhangsan -100
QUEUED

127.0.0.1:6379> incrby lisi 100
QUEUED

================ 这时候zhangsan的值被其他客户端修改=======================

127.0.0.1:6379> exec   # 事务执行失败
(nil)

unwatch

取消监视,与 watch 命令相对
在执行 exec 后,不管是否执行成功,watch 命令都会 unwatched
客户端退出后,watch 命令也会 unwatched

127.0.0.1:6379> watch zhangsan lisi   # 监视 zhangsan 和 lisi
OK

127.0.0.1:6379> multi
OK

127.0.0.1:6379> incrby zhangsan -100
QUEUED

127.0.0.1:6379> incrby lisi 100
QUEUED

127.0.0.1:6379> unwatch  # 取消监视
OK

和事务相关的错误

在 redis 事务中,可能会发生两种错误

1、编译型错误

在 multi 后,exec 调用之前,由于输入不正确的 redis 命令导致的错误

127.0.0.1:6379> multi
OK

127.0.0.1:6379> sets zhangsan 22   # 事务中命令输错
(error) ERR unknown command `sets`, with args beginning with: `zhangsan`, `22`, 

127.0.0.1:6379> set zhangsan 22
QUEUED

127.0.0.1:6379> exec   # 事务执行不成功
(error) EXECABORT Transaction discarded because of previous errors.

在这种情况下事务不会执行成功

2、运行时错误

在 exec 调用之后,由于 redis 命令执行不成功导致的错误

127.0.0.1:6379> set test abc
OK

127.0.0.1:6379> multi
OK

127.0.0.1:6379> incr test   # 由于test中存的不是 integer,在exec时会出错
QUEUED

127.0.0.1:6379> set info aaa
QUEUED

127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK

从执行结果看出,在 exec 时,即使其中有命令出错,后面的命令不会被影响,依旧会被执行从这里也可以看出,redis 不支持回滚

最后提供免费的Java架构学习资料,学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

点击:点这个!点这个!暗号:csdn。

还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书。
一个Redis事务,可以让你征服面试官半小时。