#sql #.net #multithreading #transactions #isolation-level
#sql #.net #многопоточность #транзакции #уровень изоляции
Вопрос:
Я ищу некоторые рекомендации относительно транзакций. Случай:
У меня есть таблица с несколькими обычными полями и 2 внешними ключами. Существует уникальный индекс для 2 внешних ключей. В таблице содержится несколько записей 100.000. Два потока: поток 1 — это некоторый процесс обслуживания, который собирает довольно большой объем данных, удаляет все записи, где FK1 =, а затем повторно вставляет несколько тысяч записей с тем же FK1 = . Удаление и вставка этих записей занимает более минуты. В течение этой минуты несколько десятков раз запускается 2-й поток (никогда третий), который также может обновлять или вставлять запись, где FK1 = . Когда это происходит во время работы потока 1, иногда происходит нарушение уникального индекса.
Что я могу / должен делать в отношении транзакций для потоков 1 и 2, какой уровень изоляции. Любые рекомендации приветствуются!
Заранее спасибо, Ян.
Комментарии:
1. После прочтения еще немного кажется, что установка TransactionIsolationLevel на ‘Serializable в потоке 1, должна защитить поток 1 от любых действий, выполняемых 2-м потоком. Я прав?
2. Serializable предотвращает совместное использование нескольких tx любой ценой, что часто не идеально. В вашем случае read-comitted должно быть в порядке. Поскольку оба потока выполняют обновления, если первый tx получит FK = ? блокировка записи для выполнения обновления, второй tx пытается обновить FK = ? будет ждать, пока эта блокировка не будет снята.
3. @gerrytan: Спасибо за ваш комментарий. В более ранней попытке как поток-1, так и 2-й потоки использовали транзакции с фиксацией чтения. Тем не менее, 2-й поток смог вставить конфликтующую запись, в то время как поток-1 был занят вставкой нескольких записей 1000. Итак, read-committed казался недостаточно сильным. По-видимому, действие удаления в потоке-1 недостаточно блокируется, чтобы помешать 2-му потоку выполнить вставку.
4. Тогда я предлагаю вам просмотреть свой SQL, была ли у вас правильная граница транзакции? В большинстве СУБД это должно быть заключено в
begin transaction
иcommit
. Распространенная ошибка заключается в том, что вы забыли эту границу транзакции, и у вас есть режим автоматической фиксации — следовательно, каждый отдельный оператор является отдельной транзакцией. Помните, что запуск (или сбой запуска) одной транзакции никогда не должен переводить ваши данные в несогласованное состояние.
Ответ №1:
Поскольку как поток 1, так и поток 2 выполняют обновление, они никогда не должны выполняться вместе (используйте изоляцию, связанную с чтением). Вместо этого я бы сосредоточился на разбивке транзакции потока 1 (tx) на несколько меньших / более быстрых tx.
Например, используйте ограничение для потока 1 tx таким образом, чтобы вы выполняли обслуживание только над первыми X строками FK = ?. Делайте это в цикле, пока все FK = ? строки обрабатываются. Если одному из этих tx требуется всего 2-3 секунды, то другой tx из потока 2 может быть запущен до запуска следующей партии потоков 1 tx.
Выберите спокойное время для выполнения обслуживания, чтобы уменьшить влияние на пользовательский интерфейс.
Комментарии:
1. Еще раз спасибо. Задание ~ 1 минута уже составляет 1/100-ю от общей задачи 🙂 Я мог бы легко разделить общую задачу на еще большее количество блоков, но это никогда не решит проблему полностью, потому что постоянно выполняются десятки вызовов «2-го потока». И да, обычно эта большая задача выполняется в спокойное время, но иногда требуется экстренное обслуживание в пиковое время: (