Статья включена в GitHubJavaKeeper, N-линия Интернет-разработка основных навыков спектра оружия
Предполагая, что такой бизнес существует, некоторые данные, полученные пользователями, поступают из информации стороннего интерфейса.Чтобы избежать частых запросов сторонних интерфейсов, мы часто добавляем слой кеша, и кеш должен быть чувствительным ко времени. Предположим, что структура, которую мы хотим сохранить, представляет собой хэш (без 'SET anotherkey "will expire in a minute" EX 60'Эта атомарная операция), нам нужно не только помещать ее в кеш пакетами, но и следить за тем, чтобы каждый ключ добавлялся со сроком действия (в случае, если срок действия ключа никогда не истекает), в это время транзакционная операция является лучшим выбором.
Чтобы обеспечить атомарность нескольких последовательных операций, наши часто используемые базы данных будут иметь поддержку транзакций, и Redis не является исключением. Но это не то же самое, что реляционная база данных.
Операция каждой транзакции имеет начало, фиксацию и откат, начало указывает на начало транзакции, фиксация указывает на фиксацию транзакции, а откат указывает на откат транзакции. Его приблизительная форма выглядит следующим образом
begin();
try {
command1();
command2();
....
commit();
} catch(Exception e) {
rollback();
}
Redis выглядит аналогично по форме и разделен на три этапа.
- Открытая транзакция (мульти)
- Командная очередь (бизнес-операция)
- Выполнить транзакцию (exec) или отменить транзакцию (discard)
> multi
OK
> incr star
QUEUED
> incr star
QUEUED
> exec
(integer) 1
(integer) 2
Приведенная выше инструкция демонстрирует полный процесс транзакции. Все инструкции не выполняются до выполнения, а кэшируются в очереди транзакций сервера. После того, как сервер получит инструкцию выполнения, он выполнит всю очередь транзакций, и она будет выполнена один раз. после выполнения Возвращает результат выполнения всех команд.
Транзакция Redis может выполнять несколько команд одновременно и, по сути, представляет собой набор команд. Все команды в транзакции сериализуются и выполняются последовательно, без вставки другими командами, и не допускается застревание.
Он может обеспечить однократное, последовательное и монопольное выполнение ряда команд в очереди (основная функция транзакции Redis — объединить несколько команд, чтобы другие команды не вмешивались в очередь).
В официальной документации так написано
Транзакции могут выполнять несколько команд одновременно с двумя важными гарантиями:
- Транзакция — это отдельная изолированная операция: все команды в транзакции сериализуются и выполняются последовательно. Во время выполнения транзакции она не будет прервана командным запросом, отправленным другими клиентами.
- Транзакция — это атомарная операция: выполняются либо все команды транзакции, либо ни одна из них.
Эта атомарная операция отличается от атомарности реляционной БД тем, что не может полностью гарантировать атомарность, которая будет представлена позже.
Несколько команд для транзакций Redis
Заказ | описывать |
---|---|
MULTI | Отмечает начало блока транзакции |
EXEC | Выполнять команды во всех блоках транзакций |
DISCARD | Отменить транзакцию, отказавшись от выполнения всех команд в блоке транзакции |
WATCH | Отслеживайте один (или несколько) ключ (и), если этот (или несколько) ключи изменены другими командами до выполнения транзакции, то транзакция будет прервана. |
UNWATCH | Снимите все ключи с помощью команды WATCH |
MULTIКоманда используется для запуска транзакции и всегда возвращает OK.
После выполнения MULTI клиент может продолжать посылать на сервер любое количество команд, эти команды не будут выполняться сразу, а будут помещены в очередь.EXECВсе команды в очереди будут выполнены при вызове команды.
С другой стороны, позвонивDISCARD, клиент может очистить очередь транзакций и отказаться от выполнения транзакции.
Не говорите много глупостей, лучше понять результат, когда вы непосредственно работаете с ним~
гладкое плавание
нормальное исполнение(Его можно пакетировать, что очень круто. Если каждая операция пройдет успешно, то возьмет то, что нужно, не затрагивая друг друга)
отказаться от бизнеса(Операция сброса означает отказ от транзакции, предыдущие операции не учитываются)
Рассмотрим вопрос: Предположим, у нас есть ключ со сроком действия, и ключ недействителен во время операции транзакции, будет ли он успешным при выполнении exec?
ошибка в транзакции
Упорядоченная работа наверху выглядит неплохо, ноТранзакции предлагаются для решения операций по обеспечению безопасности данных, когда мы используем транзакции Redis, мы можем столкнуться со следующими двумя ошибками:
- бизнес работает
EXEC
Ранее поставленная в очередь команда могла пойти не так. Например, команда может вызвать синтаксические ошибки (неправильное количество параметров, неправильные имена параметров и т. д.) или другие более серьезные ошибки, такие как нехватка памяти (если сервер используетmaxmemory
если установлен максимальный предел памяти). - команда может быть в
EXEC
Ошибка после звонка. Например, команда в транзакции может обрабатывать ключи неправильного типа, например использовать команду списка для строкового ключа и т. д.
Redis использует разные стратегии обработки для двух вышеуказанных ошибок.EXEC
Ошибка перед выполнением, сервер зафиксирует отказ команды поставить в очередь и вызовет ее на клиентеEXEC
Когда команда выполняется, она отказывается выполняться и автоматически отменяет транзакцию (практика до Redis 2.6.5 заключалась в проверке возвращаемого значения команды enqueue: если команда возвращает QUEUED при постановке команды в очередь, то постановка в очередь выполняется успешно; в противном случае постановка в очередь завершается ошибкой)
для тех, кто вEXEC
Ошибки, возникающие после выполнения команды, специально не обрабатываются: даже если одна или несколько команд в транзакции генерируют ошибки во время выполнения, другие команды в транзакции будут продолжать выполняться.
все сидят вместе(Если запись операции сообщает об ошибке, все операции после exec не завершатся успешно)
плохой кредитор(В примере k1 установлен в тип String, а decr k1 можно поставить в очередь операций, потому что об ошибке оператора можно судить только при его выполнении, а остальные правильные будут выполняться нормально)
Почему Redis не поддерживает откат
Если у вас есть опыт работы с реляционными базами данных, то «Redis не выполняет откат при сбое транзакции, а продолжает выполнять оставшуюся часть команды» может показаться вам немного странным.
Следующее является официальнымхвастаться:
- Команды Redis завершатся ошибкой только из-за неправильного синтаксиса (и эти проблемы не могут быть обнаружены при постановке в очередь) или если команда используется для ключа неправильного типа: то есть, с точки зрения удобства использования, неудачная команда вызвана программированием. ошибки, которые должны быть обнаружены во время разработки, а не в производстве.
- Поскольку поддержка отката не требуется, внутренние компоненты Redis могут быть простыми и быстрыми.
Существует мнение, что способ обработки транзакций Redis будет создавать ошибки, но следует отметить, что в целом откаты не решают проблем, вызванных ошибками программирования. Например, если вы изначально хотели передать
INCR
Команда добавляет 1 к значению ключа, но случайно добавляет 2 или выполняется с неверным типом ключа.INCR
, откат не может справиться с такими ситуациями.Учитывая, что не существует механизма, позволяющего избежать ошибок, вызванных самими программистами, и такие ошибки обычно не возникают в производственной среде, Redis выбирает более простой и быстрый способ обработки транзакций без откатов.
Транзакции с часами
WATCH
Команда используется для мониторинга любого количества ключей до начала транзакции: при вызове команды EXEC для выполнения транзакции, если какой-либо из отслеживаемых ключей был изменен другими клиентами, вся транзакция будет прервана, больше не будет выполняться и возвращенный непосредственно терпит неудачу.
Команда WATCH может вызываться несколько раз. Мониторинг ключей вступает в силу с момента выполнения WATCH до вызова EXEC.
Пользователи также могут отслеживать столько ключей, сколько захотят, с помощью одной команды WATCH, например:
redis> WATCH key1 key2 key3
OK
когдаEXEC
При вызове отменяется мониторинг всех ключей вне зависимости от того, была успешно выполнена транзакция или нет. Кроме того, когда клиент отключается, отслеживание клиентом ключа отменяется.
Давайте рассмотрим простой пример, используйте часы для контроля баланса моего счета (у меня 100 карманных денег в неделю) и потребляйте в обычном режиме.
Но эта карта также привязана к Alipay моей невестки, что будет, если она тоже будет тратить, когда я трачу?
Когда мне хотелось спать, я спустился в 711, чтобы купить пачку сигарет и бутылку воды.В это время моя невестка заплатила прямо в супермаркете 100. В это время я еще выбирал жевательную резинку. потому что баланс был недостаточным.,,,
В это время я подошел к кассе и обнаружил, что считывание карты не удалось (транзакция прервана), смущающая партия
Вы можете не понимать использование часов, давайте посмотрим, если это все та же сцена, мы неwatch balance
, транзакция не сорвется, сберкарта становится отрицательным номером, не очень подходит для бизнеса?
использовать без параметровUNWATCH
команда, чтобы вручную отменить мониторинг всех ключей. Для некоторых транзакций, которым необходимо изменить несколько ключей, иногда программе необходимо заблокировать несколько ключей одновременно, а затем проверить, соответствуют ли текущие значения этих ключей требованиям программы. Когда значение не соответствует требованиям, его можно использоватьUNWATCH
команда, чтобы отменить текущий мониторинг ключа, прервать транзакцию на полпути и дождаться следующей попытки транзакции.
смотрите инструкцию, похожую на оптимистическую блокировку, Когда транзакция отправлена, если значение ключа было изменено другими клиентами, например, список был отправлен/извлечен другими клиентами, вся очередь транзакций не будет выполнена. (Конечно, Redis также можно использовать для реализации распределенных блокировок для обеспечения безопасности, что является пессимистичной блокировкой)
Команда watch отслеживает несколько ключей перед выполнением транзакции.Если значение любого ключа изменится после наблюдения, транзакция, выполненная командой exec, будет отменена, и будет возвращен нулевой ответ, чтобы уведомить вызывающую сторону о том, что выполнение транзакции не удалось. .
пессимистический замок
Пессимистическая блокировка, как следует из названия, очень пессимистична.Каждый раз, когда я иду за данными, я думаю, что другие изменят их, поэтому каждый раз, когда я получаю данные, они будут заблокированы, так что другие будут блокировать, пока они не получат данные на замок. Многие такие механизмы блокировки используются в традиционных реляционных базах данных, например блокировки строк, таблиц и т. д., блокировки чтения, записи и т. д., все из которых блокируются до выполнения операций.
оптимистическая блокировка
Оптимистическая блокировка, как следует из названия, очень оптимистична.Каждый раз, когда я иду за данными, я думаю, что другие не будут их изменять, поэтому они не будут заблокированы, но при обновлении он будет судить, обновили ли другие это в течение этого период данных, вы можете использовать такие механизмы, как номера версий. Оптимистическая блокировка подходит для типов приложений с множественным чтением, которые могут повысить пропускную способность. Оптимистичная стратегия блокировки: версия фиксации должна быть больше, чем текущая версия записи, чтобы выполнить обновление.
Как работает команда НАБЛЮДЕНИЕ
от имени базы данныхserver.h/redisDb
В типе структуры все сохраняютwatched_keys
Dictionary, ключ словаря — это ключ, который нужно отслеживать в этой базе данных, а значение словаря — это связанный список, в котором хранятся все клиенты, отслеживающие этот ключ, как показано на следующем рисунке.
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
unsigned long expires_cursor; /* Cursor of the active expire cycle. */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
WATCH
Функция команды состоит в том, чтобы поместить текущего клиента и ключ, который нужно отслеживать, вwatched_keys
в ассоциации.
Например, если текущий клиентclient99
, то когда клиент выполняетWATCH key2 key3
, показанный ранееwatched_keys
будет изменено, чтобы выглядеть следующим образом:
пройти черезwatched_keys
Словарь, если программа хочет проверить, отслеживается ли ключ, то ей нужно только проверить, существует ли ключ в словаре; если программа хочет, чтобы все клиенты отслеживали ключ, то просто получить значение ключа (a связанный список), затем вы можете перемещаться по связанному списку.
После успешного выполнения любой команды, которая изменяет пространство ключей базы данных (например, FLUSHDB, SET, DEL, LPUSH, SADD и т. д.),multi.c/touchWatchedKey
будет вызвана функция - она перейдет кwatched_keys
словарь, чтобы увидеть, отслеживают ли какие-либо клиенты ключи, которые были изменены командой, и если да, программаREDIS_DIRTY_CAS
Варианты открыты:
void multiCommand(client *c) {
// 不能在事务中嵌套事务
if (c->flags & CLIENT_MULTI) {
addReplyError(c,"MULTI calls can not be nested");
return;
}
// 打开事务 FLAG
c->flags |= CLIENT_MULTI;
addReply(c,shared.ok);
}
/* "Touch" a key, so that if this key is being WATCHed by some client the
* next EXEC will fail. */
void touchWatchedKey(redisDb *db, robj *key) {
list *clients;
listIter li;
listNode *ln;
// 字典为空,没有任何键被监视
if (dictSize(db->watched_keys) == 0) return;
// 获取所有监视这个键的客户端
clients = dictFetchValue(db->watched_keys, key);
if (!clients) return;
// 遍历所有客户端,打开他们的 CLIENT_DIRTY_CAS 标识
listRewind(clients,&li);
while((ln = listNext(&li))) {
client *c = listNodeValue(ln);
c->flags |= CLIENT_DIRTY_CAS;
}
}
Когда клиент отправляет команду EXEC для запуска выполнения транзакции, сервер проверяет статус клиента:
- Если клиент
CLIENT_DIRTY_CAS
Если опция была включена, то это означает, что как минимум один из ключей, отслеживаемых клиентом, был изменен, и безопасность транзакции была нарушена. Сервер откажется от выполнения транзакции и вернет пустой ответ непосредственно клиенту, указывающий, что выполнение транзакции не удалось. - если
CLIENT_DIRTY_CAS
Если опция не включена, это означает, что все ключи мониторинга в безопасности и сервер официально выполняет транзакцию.
Небольшое резюме:
3 этапа
- On: начать транзакцию с MULTI
- Ставить в очередь: поместить в транзакцию несколько команд. Эти команды не будут выполняться немедленно, а будут помещены в очередь транзакций, ожидающих выполнения.
- Execute: транзакция инициируется командой EXEC
3 функции
- Отдельные изолированные операции: все команды в транзакции сериализуются и выполняются последовательно. Во время выполнения транзакции она не будет прервана командным запросом, отправленным другими клиентами.
- Нет понятия уровня изоляции: Команды в очереди не будут фактически выполняться до тех пор, пока они не будут отправлены, потому что никакие команды не будут фактически выполняться до отправки транзакции, поэтому в транзакции нет такого понятия, как запрос. «Не вижу» — это очень раздражает. проблема
- Атомарность не гарантируется: если команду не удастся выполнить в той же транзакции Redis, последующие команды все равно будут выполняться без отката.
В традиционных реляционных базах данных свойства ACID часто используются для проверки безопасности функций транзакций. Транзакции Redis гарантируют согласованность (C) и изоляцию (I), но не гарантируют атомарность (A) и надежность (D).
В конце концов
Транзакция Redis должна проходить чтение и запись по сети при отправке каждой инструкции в очередь кэша транзакций.Когда в транзакции много инструкций, требуемое время сетевого ввода-вывода также будет увеличиваться линейно. Поэтому клиенты Redis обычно используют конвейеры при выполнении транзакций, чтобы несколько операций ввода-вывода можно было сжать в одну операцию ввода-вывода.
Использованная литература:Дизайн и реализация Redis:Redis book.читать документ S.IO/en/latest/post…