Найти в Дзене
Блог IT разработчика

Предотвращение коллизий не уникального id при параллельных транзакциях: практики БД

Проблема — дублирование не уникального id при асинхронных операциях; Контекст — работа с реляционной базой данных и столбцами tid (уникальное оборудование), id (уникальный id), cmdid (не уникальный id в рамках оборудования); Цель — обеспечение уникальности значения cmdid в пределах tid (уникальное оборудование). Чтобы гарантированно избежать дублирования не уникального id (cmdid) при асинхронных операциях, нужно использовать механизмы синхронизации и атомарного генерирования значений на уровне СУБД. Ниже — основные подходы. Рекомендуемые решения Используйте встроенную функциональность СУБД для генерации уникальных значений. Но автоинкрементное значение в рамках данной архитектуры не подойдет Для PostgreSQL: CREATE SEQUENCE {tid}_cmdid_seq; INSERT INTO table_name (tid, cmdid) VALUES (605009, nextval('{tid}_{tid}_cmdid_seq')); Для MySQL/MariaDB: ALTER TABLE table_name MODIFY cmdid INT AUTO_INCREMENT; Для SQL Server: ALTER TABLE table_name ADD CONSTRAINT DF_cmdid DEFAULT NEXT VALUE FOR c
Оглавление

Проблема — дублирование не уникального id при асинхронных операциях;

Контекст — работа с реляционной базой данных и столбцами tid (уникальное оборудование), id (уникальный id), cmdid (не уникальный id в рамках оборудования);

Цель — обеспечение уникальности значения cmdid в пределах tid (уникальное оборудование).

Чтобы гарантированно избежать дублирования не уникального id (cmdid) при асинхронных операциях, нужно использовать механизмы синхронизации и атомарного генерирования значений на уровне СУБД. Ниже — основные подходы.

Рекомендуемые решения

1. Серийные (sequence) или автоинкрементные поля

Используйте встроенную функциональность СУБД для генерации уникальных значений.

Но автоинкрементное значение в рамках данной архитектуры не подойдет

Для PostgreSQL:

CREATE SEQUENCE {tid}_cmdid_seq;
INSERT INTO table_name (tid, cmdid) VALUES (605009, nextval('{tid}_{tid}_cmdid_seq'));

Для MySQL/MariaDB:

ALTER TABLE table_name MODIFY cmdid INT AUTO_INCREMENT;

Для SQL Server:

ALTER TABLE table_name ADD CONSTRAINT DF_cmdid DEFAULT NEXT VALUE FOR cmdid_sequence FOR cmdid;

2. Транзакции с блокировкой (pessimistic locking)

Выполняйте операции в транзакции с явной блокировкой:

BEGIN;
SELECT MAX(cmdid) FROM table_name WHERE tid = 605009
FOR UPDATE;

Блокируем строки для изменения

Затем вставляем новую запись с вычисленным cmdid + 1

INSERT INTO table_name (tid, cmdid) VALUES
(605009, (SELECT MAX(cmdid) + 1 FROM table_name WHERE tid = 605009));
COMMIT;

3. Оптимистичная блокировка с проверкой

Проверяйте уникальность при вставке:

INSERT
INTO table_name (tid, cmdid) SELECT 605009, COALESCE(MAX(cmdid), 0) + 1
FROM table_name WHERE tid = 605009 ON CONFLICT (tid, cmdid) DO NOTHING;
-- Для PostgreSQL

Для MySQL:

INSERT
INTO table_name (tid, cmdid) SELECT 605009, IFNULL(MAX(cmdid), 0) + 1
FROM table_name WHERE tid = 605009 ON DUPLICATE KEY UPDATE cmdid =
VALUES(cmdid);

4. Использование UUID или композитных ключей

Если строгое числовое упорядочивание не критично:

Добавляем уникальныйUUID

ALTER TABLE table_name ADD COLUMN unique_id
UUID DEFAULT gen_random_uuid();
Создаём уникальный индекс CREATE
UNIQUE INDEX idx_unique_cmdid ON table_name (tid, unique_id);

Дополнительные рекомендации:

  • Индексы. Создайте индекс по (tid, cmdid) для ускорения поиска:
CREATE INDEX idx_tid_cmdid ON table_name (tid, cmdid);
  • Обработка ошибок. Всегда проверяйте результат вставки и обрабатывайте возможные конфликты.
  • Тестирование. Протестируйте решение при высокой нагрузке с параллельными запросами.
  • Мониторинг. Добавьте логирование попыток вставки с дубликатами для анализа.

Итог

Оптимальный выбор — использовать встроенные механизмы автоинкремента или последовательностей СУБД, так как они:

  • гарантированно обеспечивают уникальность;
  • оптимизированы на уровне движка БД;
  • не требуют дополнительной логики в приложении;
  • работают корректно при параллельных запросах

Подписаться | Канал в дзене | Наш сайт | ВК | YouTube