Имеется задача: вставить N элементов в таблицу, но перед этим удостовериться, не добавлены ли уже такие элементы.
Т.е. сначала делаем что-то вроде:
SELECT COUNT(*) FROM xxx WHERE x IN (x1,x2,x3,x4,x5,x6…… x1000);
Если результат равен 0, то значит можно делать такой же массовый INSERT.
Но есть проблема — как сделать это секурно при многопоточности?
Т.е., допустим, как избежать ситуации, когда одновременно получаются 2 потока и порядок действий получается таким:
П1: SELECT COUNT(*) — получает «0»
П2: SELECT COUNT(*) — получает «0»
П1: делает INSERT
П2: т.к. получил «ноль» в предыдущем селекте, тоже делает INSERT дублирующих записей
Есть ли решение для такой задачи?
Идея выставлять какой-то глобальный флаг кажется очень кривой и глупой.
Хранимки не предлагайте, т.к. опять-таки — они не спасут от одновременности. Как вообще такие вещи делаются?
Вставлять предполагается порой большие массивы данных по несколько десятков тысяч, так что вероятно, что запросы будут выполняться не слишком быстро и есть вероятность словить баг с одновременной вставкой.
PS: x — не является уникальным полем. Оно приведено здесь для простоты. В реальности же проверка на возможность добавить запись в базу — куда более сложная.
если x не уникальное (ну и вообще нарушений целостности и т. п. не будет), то вторая вставка не сфейлит, а нормально продублирует записи — транзакции тут не спасут.
LOCK TABLES ххх WRITE;
SELECT COUNT(*) FROM xxx WHERE x IN (x1,x2,x3,x4,x5,x6…… x1000);
…
INSERT INTO xxx ...;
UNLOCK TABLES;
Заблокирует таблицу для других сессий. Если в запросах между блокировкой и разблокировкой будут использоваться на чтение другие таблицы например WHERE x IN (SELECT * FROM xxx1), то их тоже надо, если не ошибаюсь блокировать: LOCK TABLES ххх WRITE, xxx1 READ
INSERT ON DUPLICATE UPDATE и другие тригеры на вставку.
также — LOCK таблиц. Также лок мозгов на сервере и другие мьютексы.
Вариант 3 — работа через промежуточную инстацию