首页 > Redis事务介绍

Redis事务介绍

概述

相信学过Mysql等其他数据库的同学对事务这个词都不陌生,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行。为什么会有这样的需求呢?看看下面的场景:

  • 微博是一个弱关系型社交网络,用户之间有关注和被关注两种关系,比如两个用户A和B,如果A关注B,则B的粉丝中就应该有A。关注这个动作需要两个步骤完成:在A的关注者中添加B;在B的粉丝中添加A。 这两个动作要么都执行成功,要么都不执行。否则就可能会出现A关注了B,但是B的粉丝中没有A的不可容忍的情况。
  • 转账汇款,假设现在有两个账户A和B,现在需要将A中的一万块大洋转到B的账户中,这个动作也需要两个步骤完成:从A的账户中划走一万块;在B的账户中增加一万块。这两个动作要么全部执行成功,要么全部不执行,否则自会有人问候你的!!!

Redis作为一种高效的分布式数据库,同样支持事务。

Redis事务

Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行。Redis事务的实现需要用到 MULTI  EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXEC 命令表示事务命令结束。

举个例子,使用redis-cli连接redis,然后在命令行工具中输入如下命令:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> set url http://qifuguang.me

QUEUED

127.0.0.1:6379> set title winwill2012

QUEUED

127.0.0.1:6379> set desc java

QUEUED

127.0.0.1:6379> EXEC

1) OK

2) OK

3) OK

127.0.0.1:6379>

127.0.0.1:6379> get url

"http://qifuguang.me"

127.0.0.1:6379> get title

"winwill2012"

127.0.0.1:6379> get desc

"java"

127.0.0.1:6379>

从输出中可以看到,当输入MULTI命令后,服务器返回OK表示事务开始成功,然后依次输入需要在本次事务中执行的所有命令,每次输入一个命令服务器并不会马上执行,而是返回”QUEUED”,这表示命令已经被服务器接受并且暂时保存起来,最后输入EXEC命令后,本次事务中的所有命令才会被依次执行,可以看到最后服务器一次性返回了三个OK,这里返回的结果与发送的命令是按顺序一一对应的,这说明这次事务中的命令全都执行成功了。

再举个例子,在命令行工具中输入如下命令:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> set a a

QUEUED

127.0.0.1:6379> sett b b

(error) ERR unknown command 'sett'

127.0.0.1:6379> set c c

QUEUED

127.0.0.1:6379> EXEC

(error) EXECABORT Transaction discarded because of previous errors.

127.0.0.1:6379> get a

(nil)

127.0.0.1:6379> get b

(nil)

127.0.0.1:6379> get c

(nil)

127.0.0.1:6379>

和前面的例子一样,先输入MULTI最后输入EXEC表示中间的命令属于一个事务,不同的是中间输入的命令有一个错误(set写成了sett),这样因为有一个错误的命令导致事务中的其他命令都不执行了(通过后续的get命令可以验证),可见事务中的所有命令式同呼吸共命运的。

如果客户端在发送EXEC命令之前断线了,则服务器会清空事务队列,事务中的所有命令都不会被执行。而一旦客户端发送了EXEC命令之后,事务中的所有命令都会被执行,即使此后客户端断线也没关系,因为服务器已经保存了事务中的所有命令。

除了保证事务中的所有命令要么全执行要么全不执行外,Redis的事务还能保证一个事务中的命令依次执行而不会被其他命令插入。试想一个客户端A需要执行几条命令,同时客户端B发送了几条命令,如果不使用事务,则客户端B的命令有可能会插入到客户端A的几条命令中,如果想避免这种情况发生,也可以使用事务。

Redis事务错误处理

如果一个事务中的某个命令执行出错,Redis会怎样处理呢?要回答这个问题,首先要搞清楚是什么原因导致命令执行出错:

  1. 语法错误 就像上面的例子一样,语法错误表示命令不存在或者参数错误

    这种情况需要区分Redis的版本,Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行,就比如上面的例子(使用的Redis版本是2.8的)

  2. 运行错误 运行错误表示命令在执行过程中出现错误,比如用GET命令获取一个散列表类型的键值。

    这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果食物里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。比如下例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    127.0.0.1:6379> MULTI

    OK

    127.0.0.1:6379> set key 1

    QUEUED

    127.0.0.1:6379> SADD key 2

    QUEUED

    127.0.0.1:6379> set key 3

    QUEUED

    127.0.0.1:6379> EXEC

    1) OK

    2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

    3) OK

    127.0.0.1:6379> get key

    "3"

    Redis中的事务并没有关系型数据库中的事务回滚(rollback)功能,因此使用者必须自己收拾剩下的烂摊子。不过由于Redis不支持事务回滚功能,这也使得Redis的事务简洁快速。

回顾上面两种类型的错误,语法错误完全可以在开发的时候发现并作出处理,另外如果能很好地规划Redis数据的键的使用,也是不会出现命令和键不匹配的问题的。

WATCH命令

从上面的例子我们可以看到,事务中的命令要全部执行完之后才能获取每个命令的结果,但是如果一个事务中的命令B依赖于他上一个命令A的结果的话该怎么办呢?就比如说实现类似Java中的i++的功能,先要获取当前值,才能在当前值的基础上做加一操作。这种场合仅仅使用上面介绍的MULTI和EXEC是不能实现的,因为MULTI和EXEC中的命令是一起执行的,并不能将其中一条命令的执行结果作为另一条命令的执行参数,所以这个时候就需要引进Redis事务家族中的另一成员:WATCH命令

换个角度思考上面说到的实现i++的方法,可以这样实现:

  1. 监控i的值,保证i的值不被修改
  2. 获取i的原值
  3. 如果过程中i的值没有被修改,则将当前的i值+1,否则不执行

这样就能够避免竞态条件,保证i++能够正确执行。

WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,EXEC命令执行完之后被监控的键会自动被UNWATCH)

举个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

127.0.0.1:6379> set mykey 1

OK

127.0.0.1:6379> WATCH mykey

OK

127.0.0.1:6379> set mykey 2

OK

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> set mykey 3

QUEUED

127.0.0.1:6379> EXEC

(nil)

127.0.0.1:6379> get mykey

"2"

127.0.0.1:6379>

上面的例子中,首先设置mykey的键值为1,然后使用WATCH命令监控mykey,随后更改mykey的值为2,然后进入事务,事务中设置mykey的值为3,然后执行EXEC运行事务中的命令,最后使用get命令查看mykey的值,发现mykey的值还是2,也就是说事务中的命令根本没有执行(因为WATCH监控mykey的过程中,mykey被修改了,所以随后的事务便会被取消)。

有了WATCH命令,我们就可以自己实现i++功能了,伪代码如下:

1

2

3

4

5

6

7

8

9

10

11

def incr($key):

WATCH $key

$value = GET $key

if not $value

$value = 0

$value = $value + 1



MULTI

SET $key $value

result = EXEC

return result[0]

因为EXEC返回的是多行字符串,使用result[0]表示返回值的第一个字符串。

注意:由于WATCH命令的作用只是当被监控的键被修改后取消之后的事务,并不能保证其他客户端不修改监控的值,所以当EXEC命令执行失败之后需要手动重新执行整个事务。

执行EXEC命令之后会取消监控使用WATCH命令监控的键,如果不想执行事务中的命令,也可以使用UNWATCH命令来取消监控。

声明

原创文章,转载请注明出处,本文链接:http://qifuguang.me/2015/09/30/Redis事务介绍/

如果你喜欢我的文章,请关注我的微信订阅号,更多干货,第一时间与你分享:

转载于:https://www.cnblogs.com/thrillerz/p/5036327.html

更多相关:

  • 在DOS窗口执行了一些列命令完成某项工作后,如果要查看都执行了那些命令,该如何办呢?(前提:DOS窗口未关闭的情况下)     一、方法一:使用↑↓箭头上下翻看执行过的命令,此方式适宜执行命令较少的情况; 二、方法二:使用快捷键; F7快捷键查看所有执行过的命令 F3:调出上一条执行过的命令,调出后直接回车即可执行; F...

  • 在windows的DOS操作界面里面,清屏的命令是cls,那么在linux 里面的清屏命令是什么呢?下面笔者分享几种在linux下用过的清屏方法。 1、clear命令、这个命令将会刷新屏幕,本质上只是让终端显示页向后翻了一页,如果向上滚动屏幕还可以看到之前的操作信息。 2、Ctrl+l(小写的L)、这是一个清屏的快捷键,这个是笔者...

  • LINUX下用CTRL+R快速搜索HISTORY历史命令,快速索引到之前使用过的命令行语句 前提是,搜索已经使用的命令,否则是查不出来结果的。 ctrl+r 用途:反向搜索执行过的命令。(reverse-i-search) 1、任何目录下按住ctrl + r 2、输入历史命令中的字符串 ,比如 输入shut 会检索...

  • 刚开始接触linux,总有些简单的问题不知道怎么搞定,先将目前汇总的解决方法叫做"linux入门-1",后续在使用过程中逐步总结。   1. 连接 ADSL : sudo pon dsl-provider 断开 ADSL: sudo poff 查看 ADSL 状态: plog 2. dpkg 命令 dpkg 是 Debia...

  • 文章目录NVME 和 AHCI 性能比较NVME-CLI nvme工具使用1. 安装2. 命令综述3. 基本命令演示4. NVME 固件设备升级...

  • 推荐阅读:恕我直言!收藏这个MySQL文档,你其余的MySQL学习资料都可以扔了前言如果数据库中的事务都是串行执行的,这种方式可以保障事务的执行不会出现异常和错误,但带来的问题是串行执行会带来性能瓶颈;而事务并发执行,如果不加以控制则会引发诸多问题,包括死锁、更新丢失等等。这就需要我们在性能和安全之间做出合理的权衡,使用适当的并发控制...

  • ylbtech-DB-MySQL:MySQL 事务  1.返回顶部 1、 MySQL 事务 MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务! 在 MySQL 中只有使用了...

  • 五、锁与事务隔离级别 事务隔离级别简单的说,就是当激活事务时,控制事务内因SQL语句产生的锁定需要保留多入,影响范围多大,以防止多人访问时,在事务内发生数据查询的错误。设置事务隔离级别将影响整条连接。 SQL Server 数据库引擎支持所有这些隔离级别: · 未提交读(隔离事务的最低级别,只能保证不读取物理上损坏的数据) · 已提...

  • MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务、行级锁、并发 3.Berkeley DB:支持事务 一个事务是一个连续的一组数据库操作,就好像它是一个单一的工作单元进行。换言之,永远不会是完整的事务,除非该组内的每个...

  • 1. 基于注解的事务配置   1. 在需要添加事务的方法上加上@Transactional注解   2. Spring的配置文件中配置事务管理器      1 2

    c#程序设定使用期限_C# 给某个方法设定执行超时时间

    在某些情况下(例如通过网络访问数据),常常不希望程序卡住而占用太多时间以至于造成界面假死。在这时、我们可以通过Thread、Thread + Invoke(UI)或者是 delegate.BeginInvoke 来避免界面假死,但是这样做时,某些代码或者是某个方法的执行超时的时间还是无法操控的。那么我们又是否有一种比较通用的方法、来设...

  • 传参 如果程序执行的时候需要加入参数,如 ./sample aa bb 使用 gdb 的时候可以使用如下方式 gdb --args ./sample aa bb 执行 启动 gdb 之后,直接使用 r,就是 run 的意思,或者可以使用 b 加一个断点进行调试。处理信号 如果在使用的时候,遇到类似下面的报错 Threa...

  • 进程的图文形象表示 阮一峰–进程与线程的一个简单解释 多进程实质 现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢? 答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务...

  • redis 事物: Redis 事物的实现: 首先 wath监控键值 myKey开启批量执行 multi,执行命令入列,执行 exec 。如果监控的键值mykey 没有被修改过,则exec 中批量执行的命令成功,否则执行失败。无论执行成功与否,都会执行取消wath的执行  Redis multi 批量执行,是先把批量中的命令放入队列...

  • 一、准备工作: 1.登录服务器,切换到root用户(su - root,然后输入密码,按enter),进入根目录:cd / 2.进入要安装jdk的目录,自己可以创建一个java目录,执行命令如下: cd /usr/local/ mkdir java 二、下载安装包 1.打开官网下载界面:https://www.oracle.com/...