Управление транзакциями и оптимистической блокировкой

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Транзакции базы данных обеспечивают безопасную и предсказуемую модель программирования для работы с одновременными изменениями данных. В традиционных реляционных базах данных, таких как SQL Server, вы можете создавать бизнес-логику на основе хранимых процедур и (или) триггеров, которые отправляются на сервер для выполнения непосредственно в ядре СУБД. В традиционных реляционных базах данных вы вынуждены иметь дело с двумя языками программирования — обычный (нетранзакционный) язык приложений, например JavaScript, Python, C#, Java, и отдельный язык для транзакций, например T-SQL, который поддерживается базой данных.

Ядро СУБД Azure Cosmos DB поддерживает транзакции с полной совместимостью с ACID (атомарность, согласованность, изоляция, устойчивость) и изоляцией моментального снимка. Все операции базы данных в области действия логических разделов контейнера выполняются транзакционно в ядре СУБД, которое размещено в реплике раздела. К ним относятся операции записи (обновление одного или нескольких элементов в логическом разделе) и операции чтения. В следующей таблице перечислены различные операции и типы транзакций:

Операция Тип операции Транзакция для одного или нескольких элементов
INSERT (без предшествующего и последующего триггера) запись Транзакция для одного элемента
INSERT (с предшествующим или последующим триггером) Запись и чтение Транзакции для нескольких элементов
REPLACE (без предшествующего и последующего триггера) запись Транзакция для одного элемента
REPLACE (с предшествующим или последующим триггером) Запись и чтение Транзакции для нескольких элементов
UPSERT (без предшествующего и последующего триггера) запись Транзакция для одного элемента
UPSERT (с предшествующим или последующим триггером) Запись и чтение Транзакции для нескольких элементов
DELETE (без предшествующего и последующего триггера) запись Транзакция для одного элемента
DELETE (с предшествующим или последующим триггером) Запись и чтение Транзакции для нескольких элементов
Выполнение хранимой процедуры Запись и чтение Транзакции для нескольких элементов
Выполнение процедуры слияния, инициируемое системой запись Транзакции для нескольких элементов
Удаление элементов, у которых истек срок жизни (TTL), инициируемое системой запись Транзакции для нескольких элементов
Чтение Чтение Транзакция для одного элемента
Канал изменений Чтение Транзакции для нескольких элементов
Чтение с разбивкой на страницы Чтение Транзакции для нескольких элементов
Запрос с разбивкой на страницы Чтение Транзакции для нескольких элементов
Выполнение определяемой пользователем функции в составе запроса с разбивкой на страницы Чтение Транзакции для нескольких элементов

Транзакции для нескольких элементов

Azure Cosmos DB позволяет создавать хранимые процедуры, предшествующие и последующие триггеры, определяемые пользователем функции и процедуры слияния на языке JavaScript. Ядро СУБД Azure Cosmos DB имеет встроенную поддержку выполнения JavaScript. Вы можете регистрировать хранимые процедуры, триггеры до и после, определяемые пользователем функции (UDF) и процедуры слияния в контейнере, а затем выполнять их транзакционно в ядре СУБД Azure Cosmos DB. Создание логики приложения на JavaScript допускает естественное выражение потока управления, области видимости переменных, присвоения, а также интеграцию примитивов обработки исключений с транзакциями базы данных непосредственно на JavaScript.

Хранимые процедуры, триггеры, определяемые пользователем функции и процедуры слияния на JavaScript помещаются в контекстные транзакции ACID с изоляцией моментального снимка для всех элементов в логическом разделе. Если при выполнении такой транзакции код JavaScript создает исключение, то вся транзакция прерывается и откатывается к прежнему состоянию. Полученная модель программирования является простой, но эффективной. Разработчики JavaScript получают надежную модель программирования, сохраняя привычные языковые конструкции и библиотечные примитивы.

Возможность выполнить код JavaScript непосредственно в ядре СУБД повышает производительность операций над элементами контейнера в базе данных и обеспечивает поддержку транзакций. Кроме того, поскольку ядро СУБД Azure Cosmos DB изначально поддерживает JSON и JavaScript, несоответствие импеды между системами типов приложения и базы данных отсутствует.

Управление оптимистическим параллелизмом

Оптимистическая блокировка позволяет предотвратить потери операций обновления и удаления. Для конфликтующих одновременных операций применяется обычная пессимистическая блокировка ядра СУБД, размещенного в том же логическом разделе, что и обрабатываемый элемент. Когда две параллельные операции пытаются обновить элемент в логическом разделе до последней версии, одна из них будет успешной, а вторая завершится ошибкой. Но если одна или обе из таких операций ранее считали старое значение обновляемого элемента, база данных не может проверить актуальность считанных значений для любой из конфликтующих операций. К счастью, с этой ситуацией помогает справиться оптимистическая блокировка, которая не позволит двум операциям одновременно выполнять транзакции в ядре СУБД. Оптимистическая блокировка защищает от случайной перезаписи данные, внесенные другими пользователями. Она также не позволит другим процессам случайно перезаписать ваши изменения.

Реализация управления оптимистической блокировкой с помощью заголовков ETag и HTTP

Каждый элемент, хранящийся в контейнере Azure Cosmos DB, имеет системное _etag свойство. Значение _etag создается и обновляется на сервере автоматически при каждом обновлении элемента. Вы можете включить _etag в заголовок if-match клиентского запроса, чтобы сервер контролировал возможность условного обновления элемента. Если значение заголовка if-match совпадает со значением _etag на сервере, элемент обновляется. Если значение в заголовке запроса if-match потеряло актуальность, сервер отклоняет операцию и выводит ответное сообщение "HTTP 412: необходимое условие не выполнено". Клиент в этом случае может повторно получить от сервера текущую версию элемента или переопределить сохраненную на сервере версию элемента своим значением _etag для этого элемента. Кроме того, _etag в сочетании с заголовком if-none-match позволяет определить, требуется ли повторное извлечение ресурса.

Значение _etag изменяется для элемента при каждом его обновлении. В операциях замены элемента значение if-match следует явным образом включить в параметры запроса. Пример такого кода можно найти на GitHub. Значения _etag неявно проверяются для всех записываемых элементов, которые затрагиваются в хранимой процедуре. При любом конфликте хранимая процедура выполнит откат транзакции и создаст исключение. Такой метод гарантирует, что операции записи хранимой процедуры применяются атомарным образом, то есть "все или ничего". Приложение получает сигнал о необходимости повторно применить обновления и повторить исходный запрос клиента.

Управление оптимистической блокировкой и глобальное распределение

Для одновременных обновлений элемента Azure Cosmos DB применяет оптимистическую блокировку на уровне протокола связи. Для учетных записей Azure Cosmos DB, настроенных для записи в один регион, Azure Cosmos DB гарантирует, что клиентская версия обновляемого (или удаляемого) элемента совпадает с версией элемента в контейнере Azure Cosmos DB. Это обеспечивает защиту операций записи от случайного изменения чужими записями, и наоборот. В среде с несколькими пользователями оптимистическая блокировка не позволит вам случайно удалить или изменить элемент с неправильной версией. Это обеспечивает защиту от печально известных проблем "потерянного обновления" и "потерянного удаления".

В учетной записи Azure Cosmos DB, настроенной для операций записи в несколько регионов, данные можно зафиксировать независимо в дополнительных регионах, если они _etag совпадают с данными в локальном регионе. После того как новые данные будут зафиксированы локально в дополнительном регионе, они объединяются с центральным или основным регионом. Если политика разрешения конфликтов выполнит объединение новых данных с центральным регионом, эти данные будут затем реплицированы глобально с новым значением _etag. Если политика разрешения конфликтов отклонит новые данные, дополнительный регион будет восстановлен до исходных данных и значения _etag.

Дальнейшие действия

Дополнительные сведения о транзакциях базы данных и оптимистической блокировке вы можете получить в следующих статьях.