Redis事务介绍

来源:互联网 发布:企业网站用什么cms 编辑:程序博客网 时间:2024/06/05 19:09

概述

相信学过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,然后在命令行工具中输入如下命令:

1234567891011121314151617181920
127.0.0.1:6379> MULTIOK127.0.0.1:6379> set url http://qifuguang.meQUEUED127.0.0.1:6379> set title winwill2012QUEUED127.0.0.1:6379> set desc javaQUEUED127.0.0.1:6379> EXEC1) OK2) OK3) OK127.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,这里返回的结果与发送的命令是按顺序一一对应的,这说明这次事务中的命令全都执行成功了。

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

1234567891011121314151617
127.0.0.1:6379> MULTIOK127.0.0.1:6379> set a aQUEUED127.0.0.1:6379> sett b b(error) ERR unknown command 'sett'127.0.0.1:6379> set c cQUEUED127.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接受并执行。如果食物里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。比如下例:

    1234567891011121314
    127.0.0.1:6379> MULTIOK127.0.0.1:6379> set key 1QUEUED127.0.0.1:6379> SADD key 2QUEUED127.0.0.1:6379> set key 3QUEUED127.0.0.1:6379> EXEC1) OK2) (error) WRONGTYPE Operation against a key holding the wrong kind of value3) OK127.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)

举个例子:

123456789101112131415
127.0.0.1:6379> set mykey 1OK127.0.0.1:6379> WATCH mykeyOK127.0.0.1:6379> set mykey 2OK127.0.0.1:6379> MULTIOK127.0.0.1:6379> set mykey 3QUEUED127.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++功能了,伪代码如下:

1234567891011
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事务介绍/

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 放逐之城着火了怎么办 城市天际线地价过低怎么办 放逐之城铁采完怎么办 车钥匙反锁车内怎么办 眼镜用热水洗了怎么办 眼镜放平后眼镜腿不平怎么办 瞄准镜十字歪了怎么办 瞄准镜调到底了怎么办 墨镜镜片刮花了怎么办 usb小风扇不转怎么办 金属眼镜压歪了怎么办 眼镜被电焊打了怎么办 电焊闪的眼睛疼怎么办 烧了电焊眼睛疼怎么办 用了电焊眼睛痛怎么办 烧电焊脸上红痛怎么办 眼睛让电焊晃了怎么办 眼被电焊打了怎么办 眼镜弹簧腿坏了怎么办 眼镜框铰链坏了怎么办 金属眼镜框歪了怎么办 眼镜框螺丝断了怎么办 眼镜被压变形了怎么办 金属眼镜腿断了怎么办 眼镜弹簧腿断了怎么办 眼镜腿螺丝太紧怎么办 眼镜金属柄断了怎么办 金属眼镜腿折了怎么办 眼镜腿中间断了怎么办 塑料眼镜腿断了怎么办 眼镜上的螺丝拧不紧怎么办 眼镜的把坏了怎么办 把眼镜坐坏了怎么办 梦见眼镜腿掉了怎么办 眼镜的腿掉了怎么办 眼镜腿的螺丝掉了怎么办 爱大爱眼镜掉腿了怎么办 合金眼镜腿断了怎么办 手关节复位h疼痛怎么办 我叫mt红色卡牌怎么办 星盟冲突qq登录怎么办