文档章节

事务(redis transaction)

这些年了1990
 这些年了1990
发布于 2016/12/12 15:26
字数 1984
阅读 15
收藏 0
点赞 0
评论 0

用法

以下是一个事务例子, 它原子地增加了 foo 和 bar 两个键的值: 

> MULTI
OK

> INCR foo
QUEUED

> INCR bar
QUEUED

> EXEC
1) (integer) 1
2) (integer) 1

        MULTI 命令用于开启一个事务,它总是返回 OK 。

        MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC 命令被调用时, 所有队列中的命令才会被执行。

        当客户端处于事务状态时, 所有传入的命令都会返回一个内容为 QUEUED 的状态回复(status reply), 这些被入队的命令将在 EXEC 命令被调用时执行。

        EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。

事务中的错误

MULTI
+OK

SET a 3
abc

+QUEUED
LPOP a

+QUEUED
EXEC

*2
+OK
-ERR Operation against a key holding the wrong kind of val

        EXEC 返回两条批量回复(bulk reply): 第一条是 OK ,而第二条是 -ERR 。 至于怎样用合适的方法来表示事务中的错误, 则是由客户端自己决定的。

        最重要的是记住这样一条, 即使事务中有某条/某些命令执行失败了, 事务队列中的其他命令仍然会继续执行 —— Redis 不会停止执行事务中的命令。

使用事务时可能会遇上以下两种错误:

  • 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
  • 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类

        对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。

        在 Redis 2.6.5 以前, Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。 而新的处理方式则使得在流水线(pipeline)中包含事务变得简单,因为发送事务和读取事务的回复都只需要和服务器进行一次通讯。

        至于那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。

为什么 Redis 不支持回滚(roll back)

以下是这种做法的优点:

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
  • 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

鉴于没有任何机制能避免程序员自己造成的错误, 并且这类错误通常不会在生产环境中出现, 所以 Redis 选择了更简单、更快速的无回滚方式来处理事务。

放弃事务

redis> SET foo 1
OK

redis> MULTI
OK

redis> INCR foo
QUEUED

redis> DISCARD
OK

redis> GET foo
"1"

        当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出.

使用 check-and-set 操作实现乐观锁

        WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。

        被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空多条批量回复(null multi-bulk reply)来表示事务已经失败。

        举个例子, 假设我们需要原子性地为某个值进行增 1 操作(假设 INCR 不存在)。

首先我们可能会这样做:

val = GET mykey
val = val + 1
SET mykey $val

        上面的这个实现在只有一个客户端的时候可以执行得很好。 但是, 当多个客户端同时对同一个键进行这样的操作时, 就会产生竞争条件。

        举个例子, 如果客户端 A 和 B 都读取了键原来的值, 比如 10 , 那么两个客户端都会将键的值设为 11 , 但正确的结果应该是 12 才对。

        有了 WATCH , 我们就可以轻松地解决这类问题了:

WATCH mykey

val = GET mykey
val = val + 1

MULTI
SET mykey $val
EXEC

        使用上面的代码, 如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 mykey 的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。

        这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。

了解 WATCH

        WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。

        WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。

用户还可以在单个 WATCH 命令中监视任意多个键, 就像这样:

redis> WATCH key1 key2 key3
OK

        当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。

        另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。

        使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 UNWATCH 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。

使用 WATCH 实现 ZPOP

        WATCH 可以用于创建 Redis 没有内置的原子操作。

        举个例子, 以下代码实现了原创的 ZPOP 命令, 它可以原子地弹出有序集合中分值(score)最小的元素:

WATCH zset
element = ZRANGE zset 0 0
MULTI
    ZREM zset element
EXEC

程序只要重复执行这段代码, 直到 EXEC 的返回值不是空多条回复(null multi-bulk reply)即可。

本文转载自:程序只要重复执行这段代码, 直到 EXEC 的返回值不是空多条回复(null multi-bulk reply)即可。

共有 人打赏支持
这些年了1990
粉丝 9
博文 49
码字总数 11621
作品 0
徐汇
程序员
1991wangliang/tx-lcn

LCN分布式事务框架v4.0 "LCN并不生产事务,LCN只是本地事务的搬运工" 官方网址 www.txlcn.org 框架特点 支持各种基于spring的db框架 兼容SpringCloud、Dubbo、motan 使用简单,低依赖,代码完...

1991wangliang ⋅ 2017/09/22 ⋅ 0

使用jedis集群同时对一条记录进行读写事务控制

服务器:redis-3.0.0. 客户端:jedis-2.6.2 目前我的代码实现是在一台redis服务器上面可以,但是如果是集群的情况下,JedisCluster类里面没有提供事务控制相关方法,请问怎么在集群下对一个k...

段英杰 ⋅ 2015/04/22 ⋅ 2

Redis的事务(transaction)

Redis事务和传统事务的区别 事务我们都不陌生,对于关系型数据库来说,事务必须满足: 1.作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行 2.一个逻辑工作单元要成为...

激情的狼王丶21 ⋅ 02/24 ⋅ 0

LCN 分布式事务 - tx-lcn

LCN分布式事务 "LCN并不生产事务,LCN只是本地事务的搬运工" 框架介绍 LCN分布式事务框架是一款事务协调性的框架,框架本身并不创建事务,只是对本地事务做协调控制。因此该框架与其他第三方...

oswl ⋅ 2017/09/22 ⋅ 3

redis源码分析之事务Transaction(下)

接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令。 看本篇文章前需要先对上面文章有所了解: redis源码分析之事务Transaction(上) 一、redis事务核心命令简...

凌风郎少 ⋅ 2017/11/12 ⋅ 0

redis-game-transaction

README.md Redis-分布式-事务 在大型游戏中经常使用分布式,分布式中因为游戏逻辑会经常游戏事务,借助redis某些特性我们可以实现分布式锁和分布式事务。很多redis集群不支持redis的事务特性...

snowcattle ⋅ 2016/12/15 ⋅ 0

Redis——事务

Redis通过MULTI, EXEC, WATCH等命令来实现事务(transaction)功能。事务提供了一种将多个请求命令打包,然后一次性,按顺序地执行多个命令的机制,并且在事务执行期间,服务器 不会中断事务而...

nao ⋅ 2016/05/27 ⋅ 0

php-redis中文参考手册_multi_exec_discard_watch_unwatc...

multi, exec, discard. Description Enter and exit transactional mode. 进入、退出事务处理模式。 Parameters (optional) or . Defaults to . A block of commands runs as a single tran......

四云麒麟 ⋅ 2012/07/17 ⋅ 0

Python client for Redis 官翻文档2.10.1(三)

Pipelines 管道 管道是Redis的子类,它支持缓冲多个命令,一次性发送到服务器去执行。可以大大的提高性能,减少服务器到客户端之间的TCP来回数据包。 管道的简单使用: >>> r = redis.Redis(....

山下狮子 ⋅ 2014/06/20 ⋅ 0

python的redis简单使用

安装的Python版本 3.6.1 redis安装的2.8 安装redis模块 pip install redis 简单使用 redis-test.py import redisr=redis.Redis(host='127.0.0.1',port=6609,db=0)r.set('name','baby')print(......

鱼煎 ⋅ 05/31 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

istio 文档

https://istio.io/docs/concepts/ https://istio.io/docs/concepts/traffic-management/handling-failures/ https://istio.io/docs/concepts/traffic-management/rules-configuration/......

xiaomin0322 ⋅ 18分钟前 ⋅ 0

编程语言的作用及与操作系统和硬件的关系

一、编程语言的作用及与操作系统和硬件的关系 作用:编程语言是计算机语言,是一种程序员与计算机之间沟通的介质,通过编程语言可以使得计算机能够根据人的指令一步一步去工作,完成某种特定...

slagga ⋅ 28分钟前 ⋅ 0

runtime实现按钮点击事件

也不能说是实现吧,,,就是有点类似于RAC里边的写法,不用给btn添加另外的点击事件,就那个add...select...这样子很不友好,来看下代码: [self.btn handleControlEvent:UIControlEventTou...

RainOrz ⋅ 28分钟前 ⋅ 0

Windows系统运维转linux系统运维的经历

开篇之前,首先介绍一下我的背景把:我是一个三线城市的甲方运维。最近,在《Linux就该这么学》书籍的影响下和朋友小A(Linux运维已经三年了,工资也比我的高很多)的影响下,决定转行。最近...

linux-tao ⋅ 29分钟前 ⋅ 0

zip压缩工具,tar打包工具

zip压缩工具 zip打包工具跟前面说到的gzip,bz2,xz 工具最大的不一样是zip可以压缩目录。如果没有安装,需要使用yum install -y zip 来安装。安装完之后就可以直接使用了,跟之前提到的压缩...

李超小牛子 ⋅ 37分钟前 ⋅ 0

使用npm发布自己的npm组件包

一、注册npm账号 官网:https://www.npmjs.com/signup 注册之后需要进行邮箱验证,否则后面进行组件包发布时候会提示403错误,让进行邮箱核准。 二、本地新建一个文件夹,cd进入后使用npm i...

灰白发 ⋅ 39分钟前 ⋅ 0

010. 深入JVM学习—垃圾收集策略概览

1. 新生代可用GC策略 1. 串行GC(Serial Copying) 算法:复制(Copying)清理算法; 操作步骤: 扫描年轻代中所有存活的对象; 使用Minor GC进行垃圾回收,同时将存活对象保存到“S0”或“S...

影狼 ⋅ 39分钟前 ⋅ 0

JVM性能调优实践——JVM篇

在遇到实际性能问题时,除了关注系统性能指标。还要结合应用程序的系统的日志、堆栈信息、GClog、threaddump等数据进行问题分析和定位。关于性能指标分析可以参考前一篇JVM性能调优实践——性...

Java小铺 ⋅ 40分钟前 ⋅ 0

误关了gitlab sign-in 功能的恢复记录

本想关sign-up的,误点了sign-in 退出后登录界面提示: No authentication methods configured 一脸懵逼.. 百度后众多方案说修改application_settings 的 signin_enabled字段; 实际上新版本字段...

铂金蛋蛋 ⋅ 41分钟前 ⋅ 0

登录后,后续请求接口没有带登录cookie可能原因

1.XMLHttpRequest.withCredentials没设置好,参考https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials...

LM_Mike ⋅ 42分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部