redis - StackExchange.Redis:事务是否多次命中服务器?
问题描述
当我通过 SE.Redis 执行事务 (MULTI/EXEC) 时,它会多次访问服务器吗?例如,
ITransaction tran = Database.CreateTransaction();
tran.AddCondition(Condition.HashExists(cacheKey, oldKey));
HashEntry hashEntry = GetHashEntry(newKeyValuePair);
Task fieldDeleteTask = tran.HashDeleteAsync(cacheKey, oldKey);
Task hashSetTask = tran.HashSetAsync(cacheKey, new[] { hashEntry });
if (await tran.ExecuteAsync())
{
await fieldDeleteTask;
await hashSetTask;
}
这里我在事务中执行两个任务。这是否意味着我打了服务器 4 次?1 表示 MULTI,1 表示删除,1 表示设置,1 表示执行?或者 SE.Redis 是否足够聪明,可以在本地内存中缓冲任务并在我们调用时一次性发送所有内容ExecuteAsync
?
解决方案
它必须发送多个命令,但不会为每个命令支付延迟成本;具体来说,当您调用Execute[Async]
(而不是之前)时,它会发出一个管道(全部一起,不等待回复):
WATCH cacheKey // observes any competing changes to cacheKey
HEXIST cacheKey oldKey // see if the existing field exists
MULTI // starts the transacted commands
HDEL cacheKey oldKey // delete the existing field
HSET cachKey newField newValue // assign the new field
然后它需要支付延迟成本才能从HEXIST
.离开()。EXEC
WATCH
DISCARD
所以; EXEC
无论哪种方式,都会发出 6 个命令,但就延迟而言:由于需要在最终/之前的决策点,您需要支付 2 次往返费用DISCARD
。但是,在许多情况下,这本身可能会被以下事实进一步掩盖,即HEXIST
在我们进行检查之前,结果可能已经在返回给您的路上,特别是如果您有任何重要的带宽,因为例如一个大的newValue
.
然而!作为一般规则:通过使用 Lua 脚本,你可以用 redis MULTI
/ EXEC
:做的任何事情都可以更快、更可靠、更少错误地完成。看起来我们实际上在这里尝试做的是:
对于 hash
cacheKey
,如果(且仅当)该字段oldField
存在:删除oldField
并设置newField
为newValue
我们可以在 Lua 中非常简单地做到这一点,因为 Lua 脚本从头到尾在服务器上执行,不会因竞争连接而中断。这意味着我们不需要担心诸如原子性之类的事情,即其他连接会改变我们正在做出决策的数据。所以:
var success = (bool)await db.ScriptEvaluateAsync(@"
if redis.call('hdel', KEYS[1], ARGV[1]) == 1 then
redis.call('hset', KEYS[1], ARGV[2], ARGV[3])
return true
else
return false
end
", new RedisKey[] { cacheKey }, new RedisValue[] { oldField, newField, newValue });
这里的逐字字符串文字是我们的 Lua 脚本,注意我们不需要再单独执行HEXISTS
/HDEL
了——我们可以根据HDEL
. 在幕后,库会SCRIPT LOAD
根据需要执行操作,因此:如果您多次执行此操作,则无需多次通过网络发送脚本本身。
从客户端的角度来看:您现在只需支付一次延迟费用,我们不会重复发送相同的内容(原始代码发送cacheKey
了四次和oldKey
两次)。
(关于KEYS
vs选择的注意事项:键和值ARGV
之间的区别对于路由目的很重要,特别是在诸如 redis-cluster 之类的分片环境中;分片是根据键完成的,这里唯一的键是; 字段标识符in hashes不会影响分片,因此出于路由的目的,它们是值,而不是键- 因此,您应该通过而不是传递它们;这不会影响您,而是cacheKey
ARGV
KEYS
redis-server
redis-cluster
这个区别非常重要,就好像你弄错了一样:服务器很可能会拒绝你的脚本,认为你正在尝试跨槽操作;仅当所有键都在同一个插槽redis-cluster
上时才支持多键命令,通常通过“哈希标签”实现)
推荐阅读
- php - 导致 zip 无法下载和创建的标头问题
- angular - 提交后如何清除表格
- python - 我想用条件运行一个循环并将所有输出保存为具有不同名称的数据框
- angular - null altough 变量的属性已初始化
- java - 在 Oracle 网站上对字符流示例代码感到困惑
- python - 如何跟踪 1 个对象而不是多个对象?
- jboss7.x - 如何在 JBoss AS 7.1 Infinispan 中增加 transaction.completedTxTimeout?
- regex - 正则表达式仅在符合条件时才捕获两个单词之间的所有内容
- arrays - 如何遍历在 Java 8 中具有 JSONArray 和 JSONObject 的 JSONArray
- neo4j - 我只想显示 2 个节点之间的双向关系一次