Керування паралельністю при використанні схеми SELECT-UPDATE


25

Скажімо, у вас є такий код (будь ласка, ігноруйте, що це жахливо):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

На мій погляд, це НЕ належним чином керувати сумісністю. Тільки тому, що у вас є транзакція, це не означає, що хтось ще не прочитає те саме значення, яке ви робили, перш ніж потрапити до вашої заяви про оновлення.

Тепер, залишаючи код таким, який є (я розумію, що це краще обробляється як одне твердження або ще краще, використовуючи стовпчик з автоматичним підвищенням / ідентифікацією), які є безпечними способами, щоб змусити його правильно обробляти параметри та запобігати перегоновим умовам, які дозволяють двом клієнтам отримати однаковий значення id?

Я майже впевнений, що додавання WITH (UPDLOCK, HOLDLOCK)до SELECT зробить трюк. SERIALIZABLE рівень ізоляції транзакцій буде схоже на роботу, а так як він заперечує , хто -то ще , щоб читати те , що ви зробили , поки тран не закінчиться ( UPDATE :. Це невірно відповідь знайомства Мартіна). Це правда? Вони працюватимуть однаково добре? Чи один віддає перевагу іншому?

Уявіть, що ви робите щось більш законне, ніж оновлення ID - деякий розрахунок, заснований на прочитаному, який потрібно оновити. Можливо, буде задіяно багато таблиць, деякі з яких ви будете писати, а інші не будете. Яка тут найкраща практика?

Написавши це запитання, я вважаю, що підказки щодо блокування є кращими, оскільки тоді ви фіксуєте лише потрібні вам таблиці, але я вдячний для будь-кого.

PS І ні, я не знаю найкращої відповіді і дійсно хочу краще зрозуміти! :)


Тільки для уточнення: чи хочете ви не завадити двом клієнтам читати однакове значення або видавати, updateщо може базуватися на застарілих даних? Якщо останнє, ви можете використовувати rowversionстовпчик, щоб перевірити, чи рядок, що підлягає оновленню, не змінено з моменту його читання.
a1ex07

Ми не хочемо, щоб другий клієнт отримував старе значення id перед тим, як його оновить до нового значення перший клієнт. Він повинен блокувати.
ЕрікЕ

Відповіді:


11

Просто вирішення SERIALIZABLEаспекту рівня ізоляції. Так, це спрацює, але загрожує тупиком.

Дві транзакції зможуть одночасно читати рядок. Вони не блокуватимуть один одного, оскільки вони або приймуть Sблокування об'єкта, або блокування покажчиків RangeS-Sзалежно від структури таблиці, і ці блоки сумісні . Але вони будуть блокувати один одного при спробі придбати блоки, необхідні для оновлення ( IXблокування об'єкта або індекс RangeS-Uвідповідно), що призведе до тупикової ситуації.

Використання явного UPDLOCKпідказки замість цього дозволить серіалізувати показання, тим самим уникнути ризику тупикової ситуації.


+1, але: для таблиць купи ви все ще можете отримати тупик конверсій навіть із блокуваннями оновлення: sqlblog.com/blogs/alexander_kuznetsov/archive/2009/03/11/…
AK

Химерний, @alex. Я думаю, це пов'язане з умовами перегонів двигуна, що намагається знайти, що заблокувати, перш ніж насправді ПОВЕРНУТИСЯ ...
ErikE

@ErikE - Тупик перетворення в статті Алекса є перетворення IXв Xсамісінькому купі. Цікаво, що жоден рядок не кваліфікується, тому жодних блоків рядків не знімається. Не впевнений, чому він взагалі бере Xзамок.
Мартін Сміт

11

Я думаю, що найкращим підходом для вас було б насправді піддавати свій модуль високій конкурентоспроможності та переконатися в цьому. Іноді UPDLOCK одного достатньо, і немає потреби в HOLDLOCK. Іноді sp_getapplock працює дуже добре. Я б не робив тут жодної заяви про ковдру - іноді додавання ще одного індексу, тригера чи індексованого перегляду змінює результат. Нам потрібно накреслити код тесту на стрес і переконатися в кожному конкретному випадку.

Я написав кілька прикладів стрес - тестування тут

Редагувати: для кращого знання внутрішніх справ ви можете прочитати книги Кален Делані. Однак книги можуть не синхронізуватися, як і будь-яка інша документація. Крім того, існує занадто багато комбінацій, які слід враховувати: шість рівнів ізоляції, багато типів замків, кластерні / некластеризовані індекси та хто знає що ще. Це багато комбінацій. Крім цього, SQL Server є закритим вихідним кодом, тому ми не можемо завантажувати вихідний код, налагоджувати його та інше - це було б остаточним джерелом знань. Інше може бути неповним або застарілим після наступного випуску чи пакета обслуговування.

Отже, вам не слід вирішувати, що працює для вашої системи без власних стресових тестів. Що б ви не прочитали, це може допомогти вам зрозуміти, що відбувається, але вам доведеться довести, що прочитані вами поради працюють для вас. Я не думаю, що ніхто може зробити це за тебе.


9

У цьому конкретному випадку додавання UPDLOCKблокування до блоку SELECTдійсно запобігло б аномалії. Додавання HOLDLOCKне є необхідним, оскільки блокування оновлень зберігається протягом усієї транзакції, але я зізнаюся, що я сам включав його як (можливо, погану) звичку в минулому.

Уявіть, що ви робите щось більш законне, ніж оновлення ідентифікатора, якийсь розрахунок на основі прочитаного, який потрібно оновити. Можливо, буде задіяно багато таблиць, деякі з яких ви будете писати, а інші не будете. Яка тут найкраща практика?

Не існує найкращої практики. Вибір контролю за сумісністю повинен базуватися на вимогах програми. Деякі програми / транзакції потрібно виконувати так, ніби вони володіли ексклюзивним правом власності на базу даних, уникаючи аномалій та неточностей за будь-яку ціну. Інші програми / транзакції можуть терпіти певну ступінь втручання один в одного.

  • Отримання обмеженого рівня запасів (<5, 10+, 50+, 100+) для товару у веб-магазині = брудне читання (неточне значення не має значення).
  • Перевірка та зменшення рівня запасів на тому веб-магазині, що перевіряється = повторне читання (у нас ОБОВ'ЯЗКОВЕ мати запас, перш ніж ми продаємо, ми НЕ повинні закінчуватись негативним рівнем запасів).
  • Переміщення готівкових коштів між моїм поточним та ощадним рахунком у банку = серіалізаційне (не прорахуйте чи не пропустіть мою готівку!).

Редагувати: @ коментар AlexKuznetsov спонукав мене перечитати питання та усунув дуже очевидну помилку у моїй відповіді. Зауважте, що ви робите пізню нічну публікацію

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