#mysql #concurrency #transactions #mariadb #race-condition
#mysql #параллелизм #транзакции #mariadb #условие гонки
Вопрос:
Допустим, у меня есть две таблицы InnoDB — TableA
и TableB
. В нем есть одна запись, TableB
и TableA
она пуста.
У меня также есть два параллельных процесса.
Process1
ищет строку в любом TableA
или TableB
. Он выполняет два оператора SQL — select * from TableA
и затем select * from TableB
.
Process2
хочет переместить запись из TableB
в TableA
. Он запускает транзакцию, а затем выполняет запросы delete from TableB
и insert into TableA
.
К сожалению, здесь есть условие гонки. Если Process2
делает свой ход, пока Process1
он останавливается где-то между двумя select
операторами, то Process1
никогда не увидит строку.
Одним из способов решения этой проблемы было бы поменять select
местами операторы и сначала выбрать из TableB
. Вместе с транзакцией и, по крайней мере READ COMMITED
, уровнем изоляции, которого должно быть достаточно для обнаружения записи хотя бы в одной таблице.
Однако мне было интересно, будет ли a select * from TableA union all select * from TableB
также делать трюк? Я думаю, что это уменьшило бы вовлеченные части и уменьшило бы вероятность разрыва при будущих изменениях кода (не нужно беспокоиться о порядке операторов). Но устраняет ли это условие гонки?
Если это имеет значение, предположите последнюю версию MariaDB.
Ответ №1:
Протестируйте каждый из них:
План А: ОБЪЕДИНЕНИЕ, которое вы предлагаете.
План Б: оберните SELECTs
в транзакцию и используйте FOR UPDATE
для каждого.
План C: завершите транзакцию и используйте режим изоляции SERIALIZABLE
. Я предпочитаю это, потому что это указывает на то, что у вас действительно не может быть двух потоков, одновременно затрагивающих эти таблицы.
Предостережение: я не знаю, какие из них будут / не будут работать.
Комментарии:
1. Хм… Объединение довольно сложно протестировать, потому что настройка условия гонки будет во многом зависеть от удачи и факторов, которые я действительно не знаю.
for update
Я думаю, что (плюс транзакция) будет работать (поскольку она будет заблокирована), но в этом случае либо я рискую зайти в тупик, либо порядок выбора снова становится важным. ИSERIALIZABLE
сводится кfor update
делу.2. Вы всегда должны кодировать возможность ошибок, включая взаимоблокировки.
3. Нет транзакций — нет взаимоблокировок! 😁 Хотя вы, конечно, правы. Однако я пытался уменьшить блокировку, придерживаясь простых операторов автоматической фиксации и используя вместо этого оптимистичную блокировку. Столкновения должны быть редкими. Хотя в этом случае, возможно, это не должно иметь значения, и я могу просто использовать обычные транзакции с обычными блокировками? Я буду… нужно подумать об этом.
4. @Vilx- —
LOCK TABLE
не следует использовать с InnoDB. Автоматическая фиксация включает блокировки.5. Нет, нет, вы меня неправильно поняли. Я не имею в виду блокировать всю таблицу! Это было бы катастрофой. Я имею в виду блокировку записи
TableB
. Оба процесса будут начинаться сselect ... from TableB for update
, а затем одновременно будет работать только один из них. Очень простое решение и действительно упрощает многие другие вещи … за исключением того, что иногда записи там нет, и тогда процесс создает ее. Что происходит тогда? Если я смогу решить эту часть, я в порядке.