Синхронизация запросов к базе данных MariaDB - PullRequest
1 голос
/ 20 апреля 2020

Из-за некоторых соображений высокой доступности я проектирую систему, в которой несколько процессов будут связываться / синхронизироваться через базу данных (скорее всего, MariaDB, но я открыт для изучения вариантов PostgreSQL и MySQL).

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

В частности, вот условие гонки, которое я имею в обратите внимание:

  1. Процесс A запускает транзакцию SQL и запускает SELECT * FROM requests WHERE ReservedTS IS NULL ORDER BY CreatedTS LIMIT 100. Здесь ReservedTS и CreatedTS представляют собой DATETIME столбцы, в которых хранится время, когда часть работы была создана процессом отправителя работы и зарезервирована процессом исполнителя работы соответственно.
  2. Процесс B запускает транзакцию, запускает тот же запрос и получает тот же набор результатов.
  3. Процесс A запускает UPDATE requests WHERE id IN (<list of IDs selected above>) AND ReservedTS IS NULL SET ReservedTS=NOW()
  4. Однако процесс B выполняет тот же запрос, поскольку его транзакция имеет собственный снимок данных, ReservedTS будет отображаться не равным NULL для процесса B, поэтому элементы резервируются дважды.
  5. Процесс A фиксирует транзакцию.
  6. Процесс B фиксирует транзакцию, перезаписывая значения процесса A.

Не могли бы вы помочь решить вышеупомянутую гонку данных?

1 Ответ

1 голос
/ 20 апреля 2020

Вы можете легко сделать это, используя эксклюзивные блокировки:

Для упрощения тестовой таблицы:

CREATE TABLE t1 (id int not null auto_increment primary key, reserved int);
INSERT INTO t1 VALUES (0,0), (1,0);

Процесс A:

BEGIN
SELECT id, reserved from t1 where id=2 and reserved=0 FOR UPDATE;
UPDATE t1 SET reserved=1 WHERE id=2 and reserved=0;
COMMIT

Если попытается Процесс B чтобы обновить ту же запись до того, как процесс А завершил транзакцию, он должен дождаться снятия блокировки (или тайм-аута):

update t1 set reserved=1 where id=2 and reserved=0;
Query OK, 0 rows affected (12.04 sec)
Rows matched: 0  Changed: 0  Warnings: 0

И, как вы можете видеть, процесс Б ничего не обновлял.

...