Как получить SQL вставку и / или обновление, чтобы не блокировать всю таблицу на MS SQL Server

13

Очень новичок в работе с БД, поэтому оцените ваше терпение по основному вопросу. Я использую SQL Server 2014 на своем локальном компьютере, и у меня есть небольшая таблица и базовое клиентское приложение для тестирования различных подходов. Я получаю то , что , как представляется, блокировка таблицы во время как INSERT INTOи UPDATEзаявления. Клиент является приложением ASP.NET со следующим кодом:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Я запускаю этот код, а затем из управляющей студии SELECT * FROM LAYOUTSv2. В обоих случаях, когда поток клиента приостановлен (т. Е. До фиксации / отката), запрос SELECT зависает до тех пор, пока не произойдет фиксация / откат.

Таблица имеет поле LAYOUTS_key, назначенное в качестве первичного ключа. В окне свойств он показывает, что он уникален и кластеризован, с блокировками страниц и строк. Параметр повышения блокировки для таблицы: «Отключить». Я пробовал оба других доступных параметра «Таблица» и «АВТО» без изменений. Я пытался, SELECT ... WITH (NOLOCK)и это немедленно возвращает результат, но, как хорошо здесь и в других местах, это не то, что я должен делать. Я попытался положить ROWLOCKнамек на обоих INSERTи UPDATEзаявления, но ничего не изменилось.

Я ищу следующее: перед фиксацией an INSERTзапросы из других потоков читают все строки, кроме той, которая INSERTредактируется. Перед принятием UPDATEзапросов из других потоков прочитайте начальную версию редактируемой строки UPDATE. Есть ли способ, которым я могу сделать это? Если мне нужно предоставить другую информацию для уточнения моего варианта использования, пожалуйста, дайте мне знать. Благодарю.

Джон Рил
источник
3
Кстати, WHERE LAYOUTS_key='" + newkey + "'по разным причинам, включая внедрение SQL-кода, полное нет-нет, следует использовать параметризованные запросы.
Мартин Смит
1
@MartinSmith Спасибо за внимание к этому ... никогда не слышал о параметризованных запросах или атаках SQL-инъекций.
Джон Рил
@JohnRiehl, re: Атака с помощью инъекций, представьте, что ваш пользователь настроен newkeyна " something';DELETE FROM LAYOUTSv2 --". Ваше обновление будет успешно завершено, а затем таблица будет очищена, поскольку пользователь манипулирует запросом, вставляя апостроф. Обычно параметризованный запрос выглядит примерно так UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, после чего вы отдельно присваиваете значение ( ?параметры) в своем коде.
Даниэль Хутмахер

Ответы:

10

Скорее всего, это не блокирует "весь стол".

Он блокирует строку в таблице, но вы SELECT * FROM LAYOUTSv2пытаетесь прочитать всю таблицу, поэтому эта блокировка обязательно блокируется.

Для случая вставки вы можете просто указать READPASTподсказку, чтобы пропустить заблокированную строку - однако это не даст желаемого результата для UPDATEслучая (оно снова пропустит строку, не прочитав начальную версию строки).

Если вы сконфигурируете базу данных для изоляции зафиксированных моментальных снимков, это даст желаемый эффект в обоих случаях (за счет более широкого использования tempdb)

Мартин Смит
источник
Я изменил «Is Read Committed Snapshot On» на True, и теперь он работает отлично, без подсказок. Благодарность! Одно продолжение ... Я оставил "Разрешить изоляцию моментального снимка" установленным на Ложь ... это нормально? Благодарю.
Джон Рил
@JohnRiehl - Да, если вы явно не используете SNAPSHOTизоляцию, лучше всего оставить ее отключенной, а затем включить ее, если впоследствии вы решите, что это будет полезно для вас.
Мартин Смит
7

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

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx

Сураджа
источник
Не относится к этому вопросу, так как операторы INSERT и UPDATE пишут одну строку
Мартин Смит