Database
 sql >> база данни >  >> RDS >> Database

Поправката на грешки от 2008 R2, която разбива RCSI

Една от корекциите, включени в сборната актуализация 11 за SQL Server 2008 R2 Service Pack 2, адресира "неправилно блокиране", което може да възникне в конкретен сценарий (обяснено по-късно в тази статия). За съжаление, корекцията въвежда нова грешка, при която SELECT заявките под RCSI (изолация за четене на ангажирани моментни снимки) започват да приемат споделени ключалки на ниво таблица. В резултат на това може да видите повишено блокиране (и потенциално блокиране) за RCSI заявки след прилагане на 2008 R2 SP2 CU11 (или по-нова версия).

Това ще дойде като нежелана изненада за всеки, който е свикнал читателите да не блокират писатели (и обратно), когато използват RCSI. Няма поправка за RCSI грешката към момента на писане. Всъщност елементът Connect, създаден от Юджийн Карпович за съобщаване на проблема, е затворен като „Няма да се поправи“, въпреки че разбирам, че това решение в момента се преразглежда.

Обикновено този проблем може да не е толкова голям проблем, тъй като кумулативните актуализации обикновено не се прилагат толкова широко, колкото пълните сервизни пакети. Въпреки това, наскоро Microsoft обяви, че ще има окончателен сервизен пакет 3 за SQL Server 2008 R2. Този сервизен пакет ще представлява просто сбор от съществуващи кумулативни актуализации на SP2 (до и включително CU13), но без нови поправки. Резултатът от всичко това е, че освен ако нещо не се промени междувременно, потребителите, прилагащи SP3, внезапно ще започнат да бъдат засегнати от RCSI грешката, въведена в CU11.

редактиране:Точно преди публикуването на тази статия, Microsoft потвърди, че тази регресия ще бъде коригирана в SP3.

Същата грешка с „неправилно блокиране“ (чиято корекция въвежда новата грешка) също беше коригирана в сборната актуализация 8 за SQL Server 2012 Service Pack 1, както е описано в KB2923460. Корекцията за SQL Server 2012 е различна и не представи новия проблем с RCSI.

SQL Server 2014 никога не е бил засегнат от нито един проблем, доколкото мога да преценя. Със сигурност няма документация, която да посочва друго, а тестовете, които направих на 2014 RTM, CU1 и CU2, не възпроизвеждат нито една грешка.

Бъгът от 2008 R2 RCSI

Заявката SELECT, изпълнявана под RCSI, обикновено приема само заключване за стабилност на схемата (Sch-S), което е съвместимо с всички други ключалки, с изключение на заключване за модифициране на схема (Sch-M). Когато CU11 (или по-нова версия) се приложи към екземпляр на SQL Server 2008 R2, тези заявки започват да приемат заключване на споделено намерение на ниво таблица (Tab-IS). Следният тестов скрипт може да се използва за демонстриране на разликата в поведението:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET NOCOUNT ON;
GO
CREATE DATABASE RCSI;
GO
ALTER DATABASE RCSI
SET READ_COMMITTED_SNAPSHOT ON;
GO
ALTER DATABASE RCSI
SET ALLOW_SNAPSHOT_ISOLATION OFF;
GO
USE RCSI;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1), (2), (3), (4);
GO
-- Show locks
DBCC TRACEON (1200, 3604, -1) WITH NO_INFOMSGS;
SELECT * FROM dbo.Test;
DBCC TRACEOFF (1200, 3604, -1) WITH NO_INFOMSGS;
GO
ALTER DATABASE RCSI
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE RCSI;

Когато се изпълнява срещу екземпляр на SQL Server 2008 R2 без грешката, изходът за отстраняване на грешки показва едно заключване на Sch-S, взето за тестовия оператор, както се очаква:

Процес на придобиване на Sch-S заключване на OBJECT:7:2105058535:0 резултат:OK
Процесът освобождава заключване на OBJECT:7:2105058535:0

Когато се изпълнява срещу SQL Server 2008 R2 build 10.50.4302 (или по-нова), изходът е подобен на:

Процес за придобиване на IS заключване на OBJECT:7:2105058535:0 резултат:OK
Процесът освобождава заключване на OBJECT:7:2105058535:0

Забележете, че заключването Sch-S е заменено с заключване Tab-IS.

Последствия и смекчаване

Заключването със споделено намерение (IS) все още е много съвместимо заключване, но не е толкова удобно за паралелност като Sch-S. Матрицата за съвместимост на заключване показва, че заключването на IS е в конфликт с:

  • Sch-M (модификация на схемата) – според Sch-S
  • BU (групова актуализация)
  • X (изключително)

Несъвместимостта с изключителни (X) заключвания означава, че четенето под RCSI ще блокира, ако паралелен процес държи изключително заключване на същия ресурс. По същия начин, записващо устройство, което се нуждае от изключително заключване, ще блокира, ако едновременен RCSI четец държи IS заключване. Изключителното заключване се получава всеки път, когато данните се модифицират и се задържат до края на транзакцията, така че ефектът от грешката е, че четците под RCSI ще бъдат блокирани от едновременни записващи (и обратно), когато не са били преди прилагането на CU11.

Значителен смекчаващ фактор е, че грешката причинява само ниво на таблица заключване със споделено намерение да бъде взето. Едновременен записващ, който се нуждае от ниво на таблица ексклузивното заключване ще причини блокиране (и потенциално блокиране). Въпреки това, едновременни автори, които изискват изключителни заключвания само на по-ниско ниво (напр. ред, страница или дял), не причини блокиране или задънена улица. На ниво таблица тези писатели ще придобият само заключване с изключителни намерения (IX), което е съвместимо с Tab-IS. Изключителните заключвания, взети при по-ниски нива на детайлност, няма да предизвикат конфликт.

В повечето системи изключителните заключвания на ниво таблица (Tab-X) ще бъдат сравнително необичайни. Освен ако не е изрично поискано с помощта на TABLOCKX намек, някои възможни причини за заключване на Tab-X са:

  • Ескалация на заключване от по-ниска степен на детайлност
  • Използване на SERIALIZABLE без поддържащ индекс за заключвания на диапазон от ключове

Техническо решение е да добавите (излишната) подсказка за таблица WITH (READCOMMITTED) към всяка таблица във всяка заявка, която се изпълнява под RCSI. Това се случва, за да се заобиколи грешката, така че се взема само Sch-S заключване, но едва ли е практично предложение.

Въпреки тези смекчения, приемането на Tab-IS за заявка само за четене под RCSI все още е неправилно поведение. Надявам се, че може да бъде поправен за SQL Server 2008 R2 преди да бъде пуснат Service Pack 3.

Бъгът „Неправилно блокиране“

Както бе споменато по-рано, грешката в RCSI се въвежда като страничен ефект от корекция за грешка в „неправилна застой“. Този по-ранен проблем е документиран за SQL Server 2008 R2 в KB2929464 и за SQL Server 2012 в KB2923460. Нито един документ не е модел на яснота (или точност), но основният проблем е доста интересен, така че искам да отделя малко време, разглеждайки го тук.

По същество блокирането възниква, когато:

  • Три или повече едновременни транзакции, прочетени от една и съща таблица
  • Съветите UPDLOCK и TABLOCK се използват и в трите случая
  • Настройката на базата данни READ_COMMITTED_SNAPSHOT е ВКЛЮЧЕНА

Имайте предвид, че няма значение на кое ниво на изолация се изпълняват транзакциите. За да възпроизведете грешката, първо изпълнете скрипта за настройка по-долу:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CREATE DATABASE IncorrectDeadlock;
GO
ALTER DATABASE IncorrectDeadlock 
SET READ_COMMITTED_SNAPSHOT ON;
GO
USE IncorrectDeadlock;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1);

След това изпълнете следния скрипт в три отделни връзки (обърнете внимание, че транзакцията е оставена отворена):

USE IncorrectDeadlock;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
BEGIN TRANSACTION;
SELECT
    T.id,
    T.col1
FROM dbo.Test AS T
    WITH (UPDLOCK, TABLOCK);

В този момент първата сесия ще върне набор от резултати, а другите две ще бъдат блокирани. „Неправилното блокиране“ възниква, когато първата сесия завърши транзакцията си (или извършване, или връщане назад). Когато това се случи, една от другите две сесии ще докладва за блокиране:

Заключването възниква, защото двете по-рано блокирани сесии държат Tab-IX (изключително намерение на ниво таблица) и двете искат да преобразуват заключването си в Tab-X (изключително на ниво таблица). Tab-IX е съвместим с друг Tab-IX, но не и Tab-X. Това е блокиране на преобразуване (и иронията тук е, че UPDLOCK често се използва за избягване на блокиране на преобразуване).

Чувствайте се свободни да променяте нивото на изолация на транзакциите за трите заявки, както желаете. Заключването ще възникне независимо, стига RCSI да е активиран, със същите заключване. Когато тестовете приключат, премахнете тестовата база данни:

USE IncorrectDeadlock;
 
ALTER DATABASE IncorrectDeadlock
SET SINGLE_USER 
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE IncorrectDeadlock;

Анализ и обяснение

Аз лично не си спомням някога да съм използвал UPDLOCK и TABLOCK заедно в моя код. За мен тази комбинация от подсказки изглежда интуитивно странна, защото SQL Server няма заключване на актуализацията на ниво таблица . И така, какво дори означава да зададете заедно съвети UPDLOCK и TABLOCK?

В документацията има следното:

ЗАКЛЮЧВАНЕ

Указва, че заключванията за актуализиране трябва да бъдат взети и задържани, докато транзакцията завърши. UPDLOCK приема заключвания за актуализиране за операции за четене само на ниво ред или страница. Ако UPDLOCK се комбинира с TABLOCK или е взето заключване на ниво таблица по някаква друга причина, вместо това ще бъде взето изключително заключване (X).

Това предполага, че комбинацията от подсказки трябва да доведе до едно изключително заключване на масата. Всъщност това не е цялата история:

В SQL Server 2000 комбинирането на подсказки UPDLOCK и TABLOCK води до приемане на Tab-S (заключване на споделена таблица), последвано от преобразуване в Tab-X (изключително заключване на таблица) при всички нива на изолация с изключение на READ UNCOMMITTED. Тази последователност от заключвания може да доведе до блокиране, при което участват три или повече сесии:две сесии придобиват Tab-S и двете изчакват от другата, за да се преобразуват в Tab-X. При ПРОЧЕТЕНЕ НЕКОМИТАТНО SQL Server 2000 приема Sch-S, след това Tab-X, който не е склонен към блокиране (просто нормално блокиране).

В SQL Server 2005 нататък (без корекцията на грешки) взетите заключвания зависят само дали RCSI е активиран или не. Ако RCSI е активиран, всички нива на изолация вземете Tab-IX и след това преобразувайте в Tab-X. Тази последователност причинява блокиране на адресите за коригиране на грешки.

Ако RCSI не е активиран, съответстващите нива на изолация се държат както при SQL Server 2000 (вземайки Tab-S и след това преобразувайки в Tab-X). Нивото на изолация (ново за 2005 г.) отнема Sch-S, последвано от Tab-X. Вследствие на това SI и READ UNCOMMITTED са единствените нива на изолация, които не са склонни към тази застой в сценария UPDLOCK, TABLOCK, когато RCSI не е активиран.

Коригирането на безизходица

Корекцията променя заключванията, взети, когато UPDLOCK и TABLOCK са посочени заедно, за всички нива на изолация , и независимо дали RCSI е активиран или не. След като се приложи корекцията, UPDLOCK и TABLOCK карат машината да придобие Tab-SIX (ниво на таблица, споделено с изключителна цел), което след това се преобразува в Tab-X.

По този начин се избягва сценарий за блокиране, тъй като Tab-SIX е несъвместим с друг Tab-SIX. Не забравяйте, че блокирането е възникнало, когато два процеса държат Tab-IX в чакане за конвертиране в Tab-X. С Tab-IX, заменен от Tab-SIX, не е възможно и двете да държат Tab-SIX едновременно. Резултатът е нормален сценарий за блокиране вместо безизходица.

Последни мисли

Корекцията на "неправилно блокиране" разрешава един конкретен сценарий за блокиране, но все още не води до поведението, което си представям хората, посочващи UPDLOCK и TABLOCK, предвидено. Ако SQL Server имаше заключване Tab-U (актуализация на ниво таблица), това би предотвратило едновременни промени в таблицата, но би позволило едновременни четци. Представям си, че това е намерението на хората, които използват тези съвети заедно, и виждам как може да е полезно.

Текущата реализация (където в крайна сметка се взема Tab-X вместо липсващия Tab-U) не отговаря на това очакване, тъй като Tab-X предотвратява едновременното четене (освен ако не се използва ниво на изолация на версиите на редове). Можем също да посочим TABLOCKX в много случаи. Фактът, че корекцията въвежда и нов бъг (само за потребители на SQL Server 2008 R2) също е жалък, особено ако грешката продължи да бъде включена в 2008 R2 SP3.

Имайте предвид, че корекцията на блокиране не е достъпна за версии на SQL Server преди 2008 R2. Тези версии ще продължат да имат сложното поведение на заключване за UPDLOCK и TABLOCK, както е описано по-горе.

Благодаря на Юджийн Карпович, който за първи път обърна вниманието ми на този въпрос в коментар към статията ми относно модификациите на данни под RCSI.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Най-популярни групи за Analytics, Big Data, Data Mining, Hadoop, NoSQL, Data Science

  2. Цената на непрочистването

  3. Предимствата на индексирането на чужди ключове

  4. Грешки при свързване с база данни или удостоверяване с подвижен тип

  5. ScaleGrid вече е наличен в региона на Сидни AWS