没错,Redis也有事务管理,但是功能很简单,在正式开发中也并不推荐使用。但是面试中有可能会问到,所以本文简单谈一谈Redis的事务。

通过这篇文章,你会了解

  • Redis为什么要提供事务?
  • Redis事务基本指令和使用方法
  • CAS乐观锁是什么?
  • Redis事务为什么不支持回滚?

1. 为什么要用事务

我们知道Redis的单个命令是原子性的,比如getsetmgetmset等指令。

原子性是指操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断,也就不会有并发的安全性问题

在涉及到多个命令的时候,如果需要把多个命令设置为一个不可分割的处理序列,就需要用到事务了。

比如,招财和陀螺各有100元,招财给陀螺转了10元,这时候需要在Redis中把招财的金额总数-10,同时需要把陀螺的金额总数+10。这两个操作要么同时成功,要么同时失败,这时候就需要事务了。

实际上,Redis连这个简单的需求都没办法完美做到,至于为啥,接着往下看吧

2. 事务的用法

2.1 5个基本指令

Redis提供了以下5个基本指令,先混个眼熟就行,接下来在案例中进行实操,想记不住都难

命令 格式 作用 返回值
MULTI MULTI 显式开启Redis事务,后续命令将排队,等候使用EXEC进行原子执行 always OK.
EXEC EXEC 执行事务中的commands队列,恢复连接状态。如果WATCH在之前被调用,只有监测中的Keys没有被修改,命令才会被执行,否则停止执行(详见下文,CAS机制) 成功: 返回数组 —— 每个元素对应着原子事务中一个 command的返回结果;
失败: 返回NULLRuby 返回nil);
DISCARD DISCARD 清除事务中的commands队列,恢复连接状态。如果WATCH在之前被调用,释放监测中的Keys always OK.
WATCH WATCH key [key ...] 将给出的Keys标记为监测态,作为事务执行的条件 always OK.
UNWATCH UNWATCH 清除事务中Keys的监测态,如果调用了EXEC或者 DISCARD,则没有必要再手动调用UNWATCH always OK.

2.2 案例演示

案例场景:招财和陀螺各有100元,招财给陀螺转了10元,这时候需要在Redis中把招财的金额-10,同时需要把陀螺的金额+10。

2.2.1 事务提交

我们首先为陀螺和招财初始化自己的金额;然后使用MULTI命令显式开启Redis事务。 该命令总是直接返回OK。此时用户可以发送多个指令,Redis不会立刻执行这些命令,而是将这些指令依次放入当前事务的指令队列中;EXEC被调用后,所有的命令才会被依次执行。

# 给陀螺初始化100元
127.0.0.1:6379> set tuoluo 100
OK
# 给招财初始化100元
127.0.0.1:6379> set zhaocai 100
OK
# 显式开启事务
127.0.0.1:6379> MULTI
OK
# 给陀螺增加10元
127.0.0.1:6379(TX)> INCRBY tuoluo 10
QUEUED
# 给招财减少10元
127.0.0.1:6379(TX)> DECRBY zhaocai 10
QUEUED
# 执行事务中的所有指令(提交事务)
127.0.0.1:6379(TX)> EXEC
1) (integer) 110
2) (integer) 90

2.2.2 嵌套事务

Redis不支持嵌套事务,多个MULTI命令和单个MULTI命令效果相同。

# 第一次开启事务
127.0.0.1:6379> MULTI
OK
# 尝试嵌套事务
127.0.0.1:6379(TX)> MULTI
(error) ERR MULTI calls can not be nested
# 仍然处于第一个事务当中
127.0.0.1:6379(TX)>

2.2.3 放弃事务

如果开启事务之后,中途后悔了怎么办?调用DISCARD可以清空事务中的指令队列,退出事务。

127.0.0.1:6379> MULTI
OK
# 在事务中调用DISCARD指令
127.0.0.1:6379(TX)> DISCARD
OK
# 会退出当前事务
127.0.0.1:6379>

2.2.4 watch指令

假如我们在一个客户端连接中开启了事务,另一个客户端连接修改了这个事务涉及的变量值,将会怎样?

client1开启了一个转账的事务,事务开始时招财和陀螺各自拥有100元,在执行EXEC指令之前,client2将陀螺的余额添加了10元,此时执行EXEC之后,陀螺最终的金额为120元,招财为90元。

很明显,这种情况下存在数据安全问题。

为此Redis提供了WATCH的指令,该指令可以为Redis事务提供CAS乐观锁行为,即多个连接同时更新变量的时候,会和变量的初始值进行比较,只在这个变量的值没有被修改的情况下才会更新成新的值。

2.2.4.1 WATCH用法

对应我们的案例,我们可以使用WATCH监听一个或多个key,如果开启事务之前,至少有一个被监视的key在EXEC执行之前被修改了,那么整个事务都会被取消,直接返回nil(见下面的案例)。UNWATCHWATCH的反操作。

2.2.4.2 CAS机制

CAS(Compare And Swap)比较并替换,是多并发时常用的一种乐观锁技术

CAS需要三个变量信息,分别是内存位置(JAVA中的内存地址,V),旧的预期值(A)和新值(B)。CAS执行时,当且仅当V和预期值A相等时,更新V的值为新值B,否则不执行更新。

3. 事务执行出错怎么办

事务执行时可能遇到问题,按照发生的时机不同分为两种:

  • 执行EXEC之前
  • 执行EXEC之后

3.1 执行EXEC之前发生错误

比如指令存在语法错误(参数数量不对,指令单词拼错)导致不能进入commands队列,这一步主要是编译错误,还未到运行时。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET tuoluo
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

这种情况下事务会执行失败,队列中的所有指令都不会得到执行。

3.2 执行EXEC之后发生错误

这种错误往往是类型错误,比如对String使用了Hash的命令,这是运行时错误,编译期间不会出错

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET tuoluo 100
QUEUED
127.0.0.1:6379(TX)> LPOP tuoluo
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

我们发现,SET tuoluo 100的命令居然执行成功了,也就是在发生了运行时异常的情况下,错误的指令不会被执行,但是其他的命令不会受影响。

这种方式显然不符合我们对原子性的定义,也就是Redis的事务无法实现原子性,无法保证数据一致。

针对这种缺陷,Redis官方也是做了说明的。

4. Redis事务为什么不支持回滚

引自Redis官方文档

为了方便大家理解,我翻译一下就是:

你们程序员的锅,关我们Redis屁事儿!

Redis官方认为,只有在命令语法错误或者类型错误的时候,Redis命令才会执行失败。而且他们认为有这种错误的语法一般也不会进入到生产环境。而且不支持回滚可以使他们有更多时间玩儿Redis运行得更简单快捷。

这种说法多牛!如果出问题就是程序员的问题,写错了还让代码进入生产环境,那就是罪上加罪,你永远赖不着Redis官方。

这可能就是不推荐使用Redis事务的原因了吧,鸡肋是一方面,万一被官方打脸了呢?所以Redis事务的知识稍微了解一下就好,面试被问到能回到上来就可以了。


下期见!

5. 推荐阅读

聊一聊Redis事务的更多相关文章

  1. Redis学习笔记~Redis事务机制与Lind.DDD.Repositories.Redis事务机制的实现

    回到目录 Redis本身支持事务,这就是SQL数据库有Transaction一样,而Redis的驱动也支持事务,这在ServiceStack.Redis就有所体现,它也是目前最受业界认可的Redis ...

  2. Redis学习笔记(4) Redis事务、生存时间及排序

    1. Redis事务 Redis中的事务(transaction)是一组命令的集合,一个事务中的命令要么都执行,要么都不执行.事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次 ...

  3. REDIS 事务机制

    基本事务操作: 任何数据库都必须要保证一种原子执行操作:最基本的原子执行操作肯定是需要提供: 举一个例子来说明: 当对某个Key 做一个统计: 可能不同的Client做它那部分的统计,一段时间后,服务 ...

  4. redis 事务

    概述 相信学过MySQL等其他数据库的同学对事务这个词都不陌生,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行.为什么会有这样的需求呢?看看下面的场景: 微博是一个弱关系型社交网络,用户之 ...

  5. (6)redis 事务

    redis对事务的支持目前还比较简单.redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令. 由于redis是单线程来处理所有client的请求的所 ...

  6. Redis事务的分析及改进

    Redis事务的分析及改进 Redis的事务特性 数据ACID特性满足了几条? 为了保持简单,redis事务保证了其中的一致性和隔离性: 不满足原子性和持久性: 原子性 redis事务在执行的中途遇到 ...

  7. Spring Framework 中启动 Redis 事务操作

    背景: 项目中遇到有一系列对Redis的操作,并需要保持事务处理. 环境: Spring version 4.1.8.RELEASE Redis Server 2.6.12 (64位) spring- ...

  8. 2016022612 - redis事务命令集合

    参考地址:http://www.yiibai.com/redis/redis_transactions.html Redis事务由指令 MULTI 启动,以EXEC结束. 1.multi 用途:事务开 ...

  9. 2016022606 - redis事务

    Redis事务 Redis事务让一组命令在单个步骤执行.事务中有两个属性,说明如下: 1.在一个事务中的所有命令按顺序执行作为单个隔离操作.通过另一个客户端发出的请求在Redis的事务的过程中执行,这 ...

随机推荐

  1. RabbitMQ --- 直连交换机 【 同步操作,等到消费者处理完后返回处理结果 】

    1.前言 RabbleMQ这种消息中间件,主要的功能是使用异步操作,来达到解耦的目的,比如,有系统a和系统b, 系统a通过消息中间件通知系统b来做业务,那么系统a只需要把要做的事情[也就是消息]发给消 ...

  2. java 方法 compareTo()的正确使用

    总结:(1)如果比较的是数字 则结果大于则为1 等于则为0 小于则为-1(2)如果比较的是字符[串] 则按照从左到右排序找对应不一样的字符第一个字符, 然后将字符装对应的ASCLL码数字,做减法运算, ...

  3. hisql ORM 查询语句使用教程

    HiSql 提供一个可以适合多种数据库的中间查询语法,不需要关注各个数据库的语法特性,通过HiSql写语句可以在常用的不同类型数据库中执行,且语法与Sql语境类似一看就懂一学就会 hisql.net ...

  4. SSM工程常见问题

    1.引入Junit测试 <!--引入junit单元测试,两个一起引用,spring-test版本要与Spring-core保持一致--> <dependency> <gr ...

  5. IK 分词器

    目录 IK 分词器-介绍 IK 分词器-安装 环境准备:Maven 安装 IK 分词器 IK 分词器-使用 IK 分词器-介绍 现有问题:ES 默认对中文分词并不友好,实际上是把中文进行了每个字的分词 ...

  6. 关于MySQL中group by的一些问题的记录

    only full group by问题的解约方案 这个报错的原因是,在SQL语句中,你查询的字段应该是你分组的依据,即select的column应该全部跟在group by之后.这个也和MySQL的 ...

  7. Java 在PDF中添加工具提示|ToolTip

    本文,将介绍如何通过Java后端程序代码在PDF中创建工具提示.添加工具提示后,当鼠标悬停在页面上的元素时,将显示工具提示内容. 导入jar包 本次程序中使用的是 Free Spire.PDF for ...

  8. C\C++ IDE 比较以及调试

    C\C++ IDE 比较以及调试 内容概要 这个作业属于哪个课程 2022面向对象程序设计 这个作业要求在哪里 2022面向对象程序设计寒假作业1 这个作业的目标 IDE 选择以及代码调试 作业正文 ...

  9. spring 事务失效的几种场景

    以下场景是基于mysql数据库,InnoDB的存储引擎. 一.没有添加@Transactional注解 二.方法声明是private或者static 三.没有抛出异常而是try catch了异常 下面 ...

  10. 学习AJAX必知必会(4)~JQuery发送Ajax请求

    一.JQuery发送Ajax请求 ■ 对于get和post请求,jQuery内部封装了Ajax请求的4个步骤和数据格式的设置 ■ 对于Ajax通用请求,jQuery内部封装了Ajax请求的4个步骤和数 ...