Он должен отправить несколько команд , но он не оплачивает задержку за команду; в частности, когда вы вызываете 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
).
Итак; в любом случае будет выдано 6 команд, но с точки зрения задержки: вы платите за 2 передачи в оба конца из-за необходимости принятия решения перед окончательным EXEC
/ DISCARD
. Однако во многих случаях это само по себе может быть дополнительно замаскировано тем фактом, что результат HEXIST
может уже возвращаться к вам еще до того, как мы дойдем до проверки, особенно если у вас есть какие-либо нетривиальные пропускная способность, например большая newValue
.
Однако! Как правило: все, что вы можете делать с redis MULTI
/ EXEC
: можно сделать быстрее, надежнее и с меньшим количеством ошибок , используя Lua сценарий вместо этого. Похоже, что мы на самом деле пытаемся сделать вот что:
для ha sh cacheKey
, если (и только если) поле oldField
существует: remove oldField
и установите newField
на newValue
Мы можем сделать это очень просто в Lua, потому что сценарии Lua выполняются на сервере от начала до конца sh без прерывания из-за конкурирующих соединений. Это означает, что нам не нужно беспокоиться о таких вещах, как атомарность, то есть о других соединениях, изменяющих данные, с которыми мы принимаем решения. Итак:
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
дважды) *. 1054 *
(примечание о выборе KEYS
против ARGV
: различие между ключами и значениями важно для целей маршрутизации, в частности сегментированные среды, такие как redis-cluster; сегментирование выполняется на основе ключа , а единственный ключ здесь cacheKey
; идентификаторы полей в хэшах не влияют на сегментирование , поэтому для маршрутизации они являются значениями , а не ключами - и поэтому вы должны передавать их через ARGV
, а не KEYS
; это выиграет ' t влияет на вас на redis-server
, но на redis-cluster
эта разница очень заметна. ortant, как будто вы ошиблись: сервер, скорее всего, отклонит ваш сценарий, думая, что вы пытаетесь выполнить операцию с перекрестным слотом; Многоклавишные команды на redis-cluster
поддерживаются только тогда, когда все клавиши находятся в одном слоте , обычно достигается с помощью «ha sh тегов»)