一、概述:

和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis中事务的实现特征:
      1). 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
      2). 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
      3). 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
      4). 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
      5). 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。

二、相关命令列表:

命令原型 时间复杂度 命令描述 返回值
MULTI   用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。 始终返回OK
EXEC   执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。 原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。
DISCARD   回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的Keys。 始终返回OK。
WATCH key [key ...] O(1) 在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执行该事务队列中的所有命令。 始终返回OK。
UNWATCH O(1) 取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监控的Keys都将自动取消。 始终返回OK。

三、命令示例:

1. 事务被正常执行:
    #在Shell命令行下执行Redis的客户端工具。
    /> redis-cli
    #在当前连接上启动一个新的事务。
    redis 127.0.0.1:6379> multi
    OK
    #执行事务中的第一条命令,从该命令的返回结果可以看出,该命令并没有立即执行,而是存于事务的命令队列。
    redis 127.0.0.1:6379> incr t1
    QUEUED
    #又执行一个新的命令,从结果可以看出,该命令也被存于事务的命令队列。
    redis 127.0.0.1:6379> incr t2
    QUEUED
    #执行事务命令队列中的所有命令,从结果可以看出,队列中命令的结果得到返回。
    redis 127.0.0.1:6379> exec
    1) (integer) 1
    2) (integer) 1
       
   2. 事务中存在失败的命令:
    #开启一个新的事务。
    redis 127.0.0.1:6379> multi
    OK
    #设置键a的值为string类型的3。
    redis 127.0.0.1:6379> set a 3
    QUEUED
    #从键a所关联的值的头部弹出元素,由于该值是字符串类型,而lpop命令仅能用于List类型,因此在执行exec命令时,该命令将会失败。
    redis 127.0.0.1:6379> lpop a
    QUEUED
    #再次设置键a的值为字符串4。
    redis 127.0.0.1:6379> set a 4
    QUEUED
    #获取键a的值,以便确认该值是否被事务中的第二个set命令设置成功。
    redis 127.0.0.1:6379> get a
    QUEUED
    #从结果中可以看出,事务中的第二条命令lpop执行失败,而其后的set和get命令均执行成功,这一点是Redis的事务与关系型数据库中的事务之间最为重要的差别。
    redis 127.0.0.1:6379> exec
    1) OK
    2) (error) ERR Operation against a key holding the wrong kind of value
    3) OK
    4) "4"

3. 回滚事务:
    #为键t2设置一个事务执行前的值。
    redis 127.0.0.1:6379> set t2 tt
    OK
    #开启一个事务。
    redis 127.0.0.1:6379> multi
    OK
    #在事务内为该键设置一个新值。
    redis 127.0.0.1:6379> set t2 ttnew
    QUEUED
    #放弃事务。
    redis 127.0.0.1:6379> discard
    OK
    #查看键t2的值,从结果中可以看出该键的值仍为事务开始之前的值。
    redis 127.0.0.1:6379> get t2
    "tt"

四、WATCH命令和基于CAS的乐观锁:

在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
      val = GET mykey
      val = val + 1
      SET mykey $val
      以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:
      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC
      和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。

Redis学习手册(事务)相关推荐

  1. 转-Redis学习手册(目录)

    为什么自己当初要选择Redis作为数据存储解决方案中的一员呢?现在能想到的原因主要有三.其一,Redis不仅性能高效,而且完全免费.其二,是基于C/C++开发的服务器,这里应该有一定的感情因素吧.最后 ...

  2. Redis学习手册1—数据结构之字符串

    Redis数据结构之字符串 字符串 存储结构 命令 SET命令 改变覆盖规则 GET命令 GETSET命令 MSET命令 MGET命令 MSETNX命令 STRLEN命令 GETRANGE SETRA ...

  3. Redis学习手册(Sorted-Sets数据类型)

    一.概述: Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中.它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score ...

  4. Redis学习手册(实例代码)

    在之前的博客中已经非常详细的介绍了Redis的各种操作命令.运行机制和服务器初始化参数配置.本篇博客是该系列博客中的最后一篇,在这里将给出基于Redis客户端组件访问并操作Redis服务器的代码示例. ...

  5. Redis学习手册(String数据类型)

    2019独角兽企业重金招聘Python工程师标准>>> 一.概述: 字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的 ...

  6. Redis学习手册(List数据类型)(转)

    一.概述: 在Redis中,List类型是按照插入顺序排序的字符串链表.和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的 元素.在插入时,如果该键并不存在,Redi ...

  7. Redis学习总结(数据类型、持久化、事务、数据删除策略、主从复制、哨兵、缓存雪崩等)

    Redis学习总结 1.Redis是什么 1.概念 2.特点 3.应用场景 2.Linux环境安装redis 3.Redis的数据存储格式 1.String类型 1.String类型的常用操作 2.S ...

  8. Redis学习笔记(一) 数据类型事务异常Jredis

    Redis学习笔记(一) NoSql概述 Nosql四大分类 Redis入门 简介 Windows安装 基础使用 问题 Redis4.0之前为什么是单线程 单线程为什么这么快 Redis4.0后的多线 ...

  9. Redis学习笔记---Redis的事务

    Redis学习笔记-Redis的事务 1. Redis事务(弱事务)和Mysql事务对比 Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行. Redi ...

最新文章

  1. 【arduino】童芯派彩屏显示图片,图片取模后在TFT液晶显示
  2. 科大星云诗社动态20210807
  3. Java Web专题(一)
  4. notepad json格式化插件_Prettier + ESLint VSCode 插件配置指南
  5. centos php fpm 停止_如何关闭php-fpm进程?
  6. 记录一次@Transactional问题处理
  7. oracle+技术面试,Oracle技术面试问题
  8. window.opener的用法
  9. linux中 ECShop的文件不能写
  10. 计算机网络及光纤通信实验教程,电子科技大学光纤通信视频教程
  11. NOIP2016排名(919~1419)
  12. 服务器系统内存不能为written,LOL选了英雄之后出现 无法连接服务器, 0x007197ea指令引用的0x0000003c内存。该内存不能为written。高悬赏...
  13. dmitry -iwnse yuming
  14. 网曝悼念牌汤圆吃出创可贴(图)
  15. 【洛谷P1903】数颜色
  16. Python 爬虫 数据提取
  17. 如何有效进行项目成本管理?
  18. samba不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
  19. 2021laysns内核仿大婶娱乐网模板整站源码 非常漂亮 可做娱乐资源网 整站打包源码
  20. golang 原码 反码 补码

热门文章

  1. vba与python相比2019_重大改变!Python 或将取代 VBA 成为 Excel 官方脚本语言
  2. 三维空间坐标的旋转算法详解_视觉slam | 三维空间刚体运动的五种表达:旋转矩阵 变化矩阵 欧拉角 旋转向量 四元数及互相转换...
  3. java lambda 排序_Java8特性:Lambda表达式之概念篇
  4. python 个人所得税问题_Python实现的个人所得税计算器
  5. axios获取图片显示_Vue.js+axios图片预览以及上传显示进度
  6. SSM 整合 3:一个 Spring 入门程序带你来了解什么是控制反转(IoC)/依赖注入(DI)!
  7. Java 跨平台运行机理:Dos 命令在桌面新建文件夹,并在其中编译、运行一段 Java 程序
  8. form表单ajax提交 ac,請求Ajax 帶返回值的通用方法, 自動獲取頁面控件值(form表單post方法提交 ),自動給控件賦值...
  9. java string的作用_浅谈java String不可变的好处
  10. 用HTML做一个简单的web登录页面,简单的JavaWeb注册登录案例