zoukankan      html  css  js  c++  java
  • redis事务(转载)

    原文地址:http://blog.csdn.net/hechurui/article/details/49508749

    Redis事务

    首先,Redis本身是单线程的。

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

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

     

    1. 127.0.0.1:6379> MULTI
    2. OK
    3. 127.0.0.1:6379> set url http://qifuguang.me
    4. QUEUED
    5. 127.0.0.1:6379> set title winwill2012
    6. QUEUED
    7. 127.0.0.1:6379> set desc java
    8. QUEUED
    9. 127.0.0.1:6379> EXEC
    10. 1) OK
    11. 2) OK
    12. 3) OK
    13. 127.0.0.1:6379>
    14. 127.0.0.1:6379> get url
    15. "http://qifuguang.me"
    16. 127.0.0.1:6379> get title
    17. "winwill2012"
    18. 127.0.0.1:6379> get desc
    19. "java"
    20. 127.0.0.1:6379>

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

     

    1. 127.0.0.1:6379> MULTI
    2. OK
    3. 127.0.0.1:6379> set a a
    4. QUEUED
    5. 127.0.0.1:6379> sett b b
    6. (error) ERR unknown command 'sett'
    7. 127.0.0.1:6379> set c c
    8. QUEUED
    9. 127.0.0.1:6379> EXEC
    10. (error) EXECABORT Transaction discarded because of previous errors.
    11. 127.0.0.1:6379> get a
    12. (nil)
    13. 127.0.0.1:6379> get b
    14. (nil)
    15. 127.0.0.1:6379> get c
    16. (nil)
    17. 127.0.0.1:6379>

     

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

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

    Redis事务错误处理

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

    1. 语法错误 就像上面的例子一样,语法错误表示命令不存在或者参数错误
      这种情况需要区分Redis的版本,Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行,就比如上面的例子(使用的Redis版本是2.8的)
    2. 运行错误 运行错误表示命令在执行过程中出现错误,比如用GET命令获取一个散列表类型的键值。
      这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果食物里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。比如下例:
    1. 127.0.0.1:6379> MULTI
    2. OK
    3. 127.0.0.1:6379> set key 1
    4. QUEUED
    5. 127.0.0.1:6379> SADD key 2
    6. QUEUED
    7. 127.0.0.1:6379> set key 3
    8. QUEUED
    9. 127.0.0.1:6379> EXEC
    10. 1) OK
    11. 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    12. 3) OK
    13. 127.0.0.1:6379> get key
    14. "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命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行

    有了WATCH命令,我们就可以自己实现i++功能了。

      

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

    redis> SET key 1

    OK

    redis> WATCH key

    OK

    redis> SET key 2

    OK

    redis> MULTI

    OK

    redis> SET key 3

    QUEUED

    redis> EXEC

    (nil)

    redis> GET key

    "2"

    上例中在执行WATCH命令后、事务执行前修改了key的值(即SET key 2),所以最后事务中的命令SET key 3没有执行,EXEC命令返回空结果。

     

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

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

    WATCH这种方式的锁叫乐观锁。

    当然,可以通过redis本身的轮询setnx来实现悲观加锁。

    类似于CAS。

    MULTI 一次发送多个命令,也可以用来提升性能。这种方式也叫Pipelining。

    redis是乐观锁,在高并发的情况下不宜使用,应该使用队列来说实现。

  • 相关阅读:
    10、代码块、构造代码块、静态代码块及main方法之间的关系
    2.0、Hibernate框架的简单搭建
    1.0、Struts2的简单搭建方法
    5、Servlet的使用
    angular组件之间的通信
    angular项目中遇到的问题
    ng-zorro-mobile中遇到的问题
    angular管道操作符的使用
    angular路由配置以及使用
    搭建Angular环境
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7226083.html
Copyright © 2011-2022 走看看