Почему SQL Server 2008 блокирует SELECT для вставки длинных транзакций? - PullRequest
16 голосов
/ 27 апреля 2009

Мы пытаемся создать транзакционную таблицу, в которую регулярно добавляются только новые записи.

Эта простая таблица требует от нас постоянного добавления новых записей в нее с течением времени. Ожидается, что объем транзакций в этой таблице будет достаточно высоким, а также возможен периодический пакетный импорт транзакций (> 1000), выполнение которого может занять несколько секунд.

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

В ходе нашего первоначального тестирования мы обнаружили узкое место, связанное с SQL Server, которое блокирует наши SELECT в середине транзакции INSERTS.

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

- Простая таблица БД

create table LOCK_TEST (
LOCK_TEST_ID int identity ,
AMOUNT int);

- Запустить это в 1 окне запроса

begin tran
insert into LOCK_TEST (AMOUNT) values (1);
WAITFOR DELAY '00:00:15' ---- 15 Second Delay
insert into LOCK_TEST (AMOUNT) values (1);
commit

- В Query 2 запустить это параллельно

select SUM(AMOUNT)
from LOCK_TEST;

Я ожидаю, что Query 2 сразу же вернется, с 0 до завершения запроса 1, а затем покажет 2. Мы никогда не хотим видеть 1, возвращенный из второго запроса.

Ответ, который мы рассмотрели, относится к WITH (NOLOCK) в операторе select. Но это нарушает границы транзакций, и возвращаемая информация может иметь финансовый характер, и мы не хотим, чтобы в наших запросах были какие-либо незаполненные подробности.

Моя проблема, кажется, на стороне ВСТАВКИ ...
Почему INSERT блокирует инструкцию SELECT, даже если она не изменяет существующие данные?

Вопрос о бонусных баллах: Это «особенность» SQL Server, или мы могли бы найти ее и в других вариантах баз данных?

UPDATE Теперь у меня было время найти локальную базу данных оракула и выполнить тот же простой тест. Этот тестовый проход, как я и ожидал.

Т.е. я могу выполнять запрос столько раз, сколько захочу, и он будет возвращать ноль, пока 1-я транзакция не завершится, а затем вернет 2.

Есть ли способ заставить SQL Server работать так? или нам нужно перейти на Oracle?

Ответы [ 4 ]

12 голосов
/ 27 апреля 2009

это поведение блокировки является функцией SQL Server. Начиная с 2005 года, вы можете использовать управление версиями на уровне строк (что используется по умолчанию в Oracle), чтобы достичь того же результата и не блокировать ваш выбор. Это создает дополнительную нагрузку на базу данных tempdb, поскольку база данных tempdb поддерживает управление версиями на уровне строк, поэтому убедитесь, что вы согласны с этим. Чтобы заставить SQL вести себя так, как вы хотите, запустите:

ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
4 голосов
/ 27 апреля 2009

Это абсолютно стандартное поведение в SQL Server и Sybase (как минимум).

Изменения данных (вставка, обновление, удаление) требуют эксклюзивные блокировки . это приводит к блокировке читателей:

С эксклюзивным (X) замком, никаких других транзакции могут изменять данные; читать операции могут осуществляться только с использование подсказки NOLOCK или читать уровень незафиксированной изоляции.

В SQL Server 2005 и более поздних версиях они внедрили изоляцию моментальных снимков (то есть версионирование строк). Из MS BOL: Понимание уровней изоляции на основе версий строк . Это означает, что считыватель имеет последние подтвержденные данные, но если вы затем захотите записать, скажем, тогда, вы можете обнаружить, что данные неверны, поскольку они изменились в транзакции блокировки.

Вот почему лучшая практика - держать транзакции короткими. Например, обрабатывать только то, что вам нужно, между BEGIN / COMMIT, не отправлять электронные письма от триггеров и т. Д.

Edit:

Подобная блокировка происходит постоянно в SQL Server. Однако блокировка на слишком долгое время становится блокировкой, которая снижает производительность. Слишком долго субъективно, очевидно.

1 голос
/ 19 сентября 2011

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

1 голос
/ 27 апреля 2009

Это существует и в MySQL. Вот логика, лежащая в основе: если вы выполняете SELECT для некоторых данных, вы ожидаете, что он будет работать с современным набором данных. Вот почему INSERT приводит к блокировке на уровне таблицы (то есть, если движок не может выполнить блокировку на уровне строк, как это может сделать innodb). Есть способы отключить это поведение, так что вы можете выполнять «грязное чтение», но все они зависят от программного обеспечения (или даже от движка базы данных).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...