Як отримати вставку та / або оновлення 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, призначене в якості основного ключа. У вікні властивостей видно, що він унікальний і кластеризований, із блокуванням сторінок та блокуванням рядків. Налаштування ескалації блокування для таблиці заборонено. Я випробував і інші доступні налаштування таблиці та AUTO без змін. Я спробував, SELECT ... WITH (NOLOCK)і це негайно повертає результат, але як це добре застережено тут і в інших місцях, це не те, що я повинен робити. Я спробував поставити ROWLOCKпідказку і на, INSERTі на UPDATEтвердження, але нічого не змінилося.

Поведінка, яку я шукаю, така: перед введенням INSERTзапиту з інших потоків читаються всі рядки, крім тієї, що INSERTредагується. Перед тим, як здійснити UPDATEзапит з інших потоків, прочитайте початкову версію рядка, що UPDATEредагується. Чи я можу це зробити? Якщо мені потрібно надати іншу інформацію для роз'яснення мого випадку використання, будь ласка, дайте мені знати. Спасибі.


3
До речі WHERE LAYOUTS_key='" + newkey + "', це повне "ні-ні" з різних причин, включаючи введення SQL, ви повинні використовувати параметризовані запити.
Мартін Сміт

1
@MartinSmith Дякую за те, що з цього приводу ... ніколи не чули ні параметризованих запитів, ні атак SQL Injection.
Джон Ріель

@JohnRiehl, re: атаки ін'єкцій, уявіть, чи користувач встановить newkeyзначення " something';DELETE FROM LAYOUTSv2 --". Ваше оновлення буде завершено успішно, а потім випорожнить таблицю, оскільки користувач маніпулював запитом, вставивши апостроф. Зазвичай параметризований запит виглядає приблизно так UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, після чого ви окремо присвоюєте значення (значення) ?(параметру) у своєму коді.
Даніель Хатмахер

Відповіді:


10

Швидше за все, це не блокування "цілої таблиці".

Це блокування рядка в таблиці, але ваші SELECT * FROM LAYOUTSv2спроби прочитати всю таблицю, тому обов'язково блокується цим блокуванням.

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

Якщо ви налаштуєте базу даних для ізоляції знімків, зроблених зчитуванням, це дасть бажаний ефект для обох випадків (за рахунок більшого використання tempdb)


Я змінив функцію "Читає здійснений знімок ввімкнено" на "Істинну", і тепер вона прекрасно працює без жодних підказок. Спасибі! Одне подальше сповіщення ... Я залишив "Дозволити ізоляцію знімків" встановити значення "Неправильно" ... це добре? Спасибі.
John Riehl

@JohnRiehl - Так, якщо ви явно не використовуєте SNAPSHOTізоляцію, краще залишити її відключеною, а потім увімкнути її, якщо згодом вирішите, що це буде корисно для вас.
Мартін Сміт

7

Виписки вставлення та оновлення повинні створювати блокування на рівні рядків. Однак, коли кількість блокувань у будь-якій транзакції становить 5000 або більше, то відбувається ескалація блокування, і це створює блокування рівня таблиці. Дивіться нижче.

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


Це питання не стосується цього питання, оскільки як заяви INSERT, так і UPDATE пишуть один ряд
Martin Smith
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.