【译】StackExchange.Redis中文使用文档--事务

Redis中的事务

Redis中的事务不像SQL数据库中的事务。
完整的文档在这里,这里稍微借用一下:

redis中的事务包括放置在 MULTIEXEC 之间的一组命令(或者用于回滚的 DISCARD)。
一旦遇到 MULTI,该连接相关的命令不会被执行 - 它们会进入一个队列(并且 每一个命令调用者得到一个答复 QUEUED)。

当遇到一个 EXEC 时,它们都被应用在一个单元中(即在操作期间没有其他连接获得时间去做任何事)。
如果看到 DISCARD 而不是 EXEC,则一切都被抛弃。因为事务中的命令会排队,你不能在事务里面 操作。

例如,在SQL数据库中,您可以执行以下操作(伪代码 - 仅供说明):

// assign a new unique id only if they don't already
// have one, in a transaction to ensure no thread-races
var newId = CreateNewUniqueID(); // optimistic
using(var tran = conn.BeginTran())
{
    var cust = GetCustomer(conn, custId, tran);
    var uniqueId = cust.UniqueID;
    if(uniqueId == null)
    {
        cust.UniqueId = newId;
        SaveCustomer(conn, cust, tran);
    }
    tran.Complete();
}

如何在Redis中实现事务呢?

这在redis事务中是不可能的:一旦事务被打开你不能获取数据 - 你的操作被排队。 幸运的是,还有另外两个命令帮助我们: WATCHUNWATCH

WATCH {key} 告诉Redis,我们对用于事务目的的特定的键感兴趣。
Redis会自动跟踪这个键,任何变化基本上都会使我们的事务回滚 - EXECDISCARD 一样(调用者可以检测到这一点,并从头开始重试)。
所以你可以做的是: WATCH 一个键,以正常的方式检查该键的数据,然后 MULTI / EXEC 你的更改。

如果,当你检查数据,你发现你实际上不需要事务,你可以使用 UNWATCH 来取消关注所有关注的键。
注意,关注的键在 EXECDISCARD 期间也被复位。 所以在Redis层,事务是从概念上讲的。

WATCH {custKey}
HEXISTS {custKey} "UniqueId"
(check the reply, then either:)
MULTI
HSET {custKey} "UniqueId" {newId}
EXEC
(or, if we find there was already an unique-id:)
UNWATCH

这可能看起来很奇怪 - 有一个 MULTI / EXEC 只跨越一个操作 - 但重要的是,我们现在也从其他所有连接跟踪对 {custKey} 的更改 - 如果任何人更改键 ,事务将被中止。

在 StackExchange.Redis 中如何实现事务?

说实话,StackExchange.Redis 使用多路复用器方法实现事务更复杂。
我们不能简单地让并发的调用者发出 WATCH / UNWATCH / MULTI / EXEC / DISCARD:它会全部混在一起。

因此,StackExchange.Redis 提供了额外的抽象来使事情更简单的变得正常:constraints

Constraints 基本上是预先测试涉及 WATCH ,一些测试,以及结果的检查。
如果所有约束都通过,则触发 MULTI / EXEC , 否则触发 UNWATCH

这是以防止与其他调用者混合在一起的命令的方式完成的。 所以我们的例子变成了:

var newId = CreateNewId();
var tran = db.CreateTransaction();
tran.AddCondition(Condition.HashNotExists(custKey, "UniqueID"));
tran.HashSetAsync(custKey, "UniqueID", newId);
bool committed = tran.Execute();
// ^^^ if true: it was applied; if false: it was rolled back

注意,从 CreateTransaction 返回的对象只能访问 async 方法 - 因为每个操作的结果在 Execute(或 ExecuteAsync )完成之前都不会知道。
如果操作不应用,所有的任务将被标记为已取消 - 否则,命令执行,您可以正常获取每个的结果。

可用条件 的集合不是广泛的,而是涵盖最常见的情况;如果你还想看到其他条件,请与我联系(或更好的方式:提交 pull-request)。

借助 When 的内置操作

还应该注意的是,Redis 预期已经了许多常见的情况(特别是:密钥/散列 存在,如上所述),所以存在单次操作原子命令。

这些是通过When参数访问的 - 所以我们前面的例子可以也可以写成:

var newId = CreateNewId();
bool wasSet = db.HashSet(custKey, "UniqueID", newId, When.NotExists);

(这里,When.NotExists 导致使用 HSETNX 命令,而不是 HSET

Lua 脚本

你还应该知道,Redis 2.6及以上版本支持Lua脚本,用于在服务器端执行多个作为单个原子单元的操作的通用工具。由于在Lua脚本中没有服务于其他连接,它的行为很像一个事务,但没有 MULTI / EXEC 等这样复杂。
这也避免了在调用者和服务器之间的带宽和延迟等问题
,但是需要与脚本垄断服务器的持续时间之间权衡。

在Redis层(假设 HSETNX 不存在),这可以实现为:

EVAL "if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end" 1 {custKey} {newId}

这可以在 StackExchange.Redis 中使用:

var wasSet = (bool) db.ScriptEvaluate(@"if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
        new RedisKey[] { custKey }, new RedisValue[] { newId });

(注意 ScriptEvaluateScriptEvaluateAsync 的响应是可变的,这取决于你确切的脚本,响应可以被强制(类型)转换- 在这种情况下为 bool

查看原文

More

作者水平有限,若有疏漏或错误还望提醒,十分感谢。

您可以在这里 提出问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容

  • redis事务 Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务...
    全能程序猿阅读 2,153评论 0 11
  • Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务功能, 本章首先...
    binge1024阅读 518评论 0 2
  • PHP-redis中文文档 phpredis是php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务...
    神秘者007阅读 2,713评论 0 2
  • phpredis是PHP的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系很有用;以下是redis...
    史史小子阅读 327评论 0 2
  • “姐姐,这几天常常会去思考婚姻,我们都不是离开家庭就无处安放自己的人,只是不知道这份缘该如何去了,尽可能避免伤害。...
    止弌阅读 127评论 0 0