Советы по минимизации блокировки таблицы добавления только в MS SQL Server?

Я пишу некоторый код ведения журнала / аудита, который будет работать в производстве (не только при возникновении ошибок или при разработке). После прочтения опыт кодирования ужасов с блокировкой и протоколированием, Я решил обратиться за советом. (Решение Джеффа "не регистрировать" не будет работать для меня, это юридически уполномоченный аудит безопасности)

есть ли подходящий уровень изоляции для минимизации разногласий и блокировки? Любые подсказки запроса, которые я могу добавить к insert оператор или хранимая процедура?

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

Я могу войти в базу данных или файл, хотя вход в файл менее привлекателен, потому что мне нужно как-то отображать результаты. Вход в файл (почти) гарантирует однако ведение журнала не будет мешать другому коду.

4 ответов


обычная транзакция (т. е. READ COMMITTED) insert уже выполняет "минимальную" блокировку. Insert интенсивные приложения не будут взаимоблокировки на insert, независимо от того, как вставка смешивается с другими операциями. В худшем случае интенсивная система вставки может вызвать конфликт защелки страницы в горячей точке, где происходит вставка, но не взаимоблокировки.

чтобы вызвать тупики, как описано Джеффом, должно быть больше в игре, как и любой из следующий:

  • система использует более высокий уровень изоляции (они это заслужили то и заслужили)
  • они читали из таблицы журнала во время транзакции (так что больше не "добавлять только")
  • цепь взаимоблокировки включала блокировки уровня приложения (т. е. .Сети lock операторы в рамках log4net), что приводит к необнаруживаемым тупикам(т. е. приложение зависает). Учитывая, что решение проблемы связано с просмотром технологических свалок, я думаю это сценарий, который у них был.

пока вы вставляете только вход в транзакции уровня изоляции Read COMMITTED, вы в безопасности. Если вы ожидаете той же проблемы, я подозреваю, что так было (т. е. deadlocks с блокировками уровня приложения), то никакое количество волшебства базы данных не может спасти вас, так как проблема все еще может проявляться, даже если вы входите в отдельную транзакцию или в отдельное соединение.


Если вы Не заботьтесь о последовательности в вашей таблице ведения журнала, почему бы не выполнить все ведение журнала из отдельном потоке.

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

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

шаги к минимизации блокировки:

  • (ключ) выполните все добавления к таблице ведения журнала вне основного потока/соединения/транзакции.
  • убедитесь, что ваша таблица журналов имеет монотонно увеличивающийся кластеризованный индекс (например. int identity), который увеличивается при каждом добавлении сообщения журнала. Это гарантирует вставку страниц into обычно находятся в памяти и избегают попаданий производительности, которые вы получаете с таблицами кучи.
  • выполните несколько добавлений к журналу в транзакции (10 вставок в транзакции быстрее, чем 10 вставок из транзакции и обычно приобретают/освобождают меньше блокировок)
  • дайте ему перерыв. Вход в БД выполняется только каждые N миллисекунд. Пакетные вверх по битам работ.
  • Если вам нужно сообщить о материале исторически, вы можете рассмотреть возможность разбиения вашего журнала таблица. Пример: вы можете создавать новую таблицу журналов каждый месяц и в то же время иметь представление журнала, которое является объединением всех старых таблиц журналов. Выполнение отчетности по наиболее подходящему источнику.

вы получите лучшую производительность, сбросив несколько сообщений журнала в одной (небольшой) транзакции, и имеете то преимущество, что если 10 потоков выполняют работу и регистрируют материал, только один поток сбрасывает материал в таблицу регистрации. Этот конвейеризация на самом деле улучшает масштабирование.


поскольку вас не волнует транзакционная целостность таблицы аудита, вы, очевидно, можете выполнять ведение журнала вне транзакции (т. е. после ее завершения). Это минимизирует влияние на сделку.

кроме того, если вы хотите минимизировать блокировку, вы должны попытаться обеспечить, чтобы как можно большая часть рабочей нагрузки запроса охватывала некластеризованные индексы. (SQL Server 2005 и выше, использование INCLUDE заявление в индексах NC может иметь большое значение)


один простой способ предотвратить проблемы с блокировкой вашей "обычной" базы данных-не использовать одну и ту же базу данных. Просто создайте другую базу данных для ведения журнала. В качестве бонуса быстрый рост вашей базы данных журналов не приведет к фрагментации в вашей основной БД. Лично я обычно предпочитаю регистрироваться в файле , но опять же, я привык к тяжелым текстовым манипуляциям в моем редакторе - VIM. Вход в отдельную БД должен помочь избежать проблем с блокировкой.

просто убедитесь, что если вы попытаетесь написать свой собственный приложение базы данных для структуры ведения журнала вы используете, вы будете очень осторожны с вашими замками (что я предполагаю, что споткнулся Джефф в блоге вы ссылаетесь). Правильно написанный (см. Несколько комментариев в сообщении Джеффа), у вас не должно быть проблем с блокировкой вашей структуры ведения журнала, если они не делают что-то странно.