Често срещано изискване в ETL и различни сценарии за отчитане е тихо да се зарежда на заден план промеждутъчна таблица на SQL Server, така че потребителите, които заявяват данните, да не бъдат повлияни от записите и обратно. Номерът е как и кога насочвате потребителите към новата, обновена версия на данните.
Опростен пример за поетапна таблица:Аналогия на пазара на фермер
И така, какво е етапна таблица в SQL? Постановката може да бъде разбрана по-лесно, като се използва пример от реалния свят:Да приемем, че имате маса, пълна със зеленчуци, които продавате на местния фермерски пазар. Тъй като вашите зеленчуци се продават и вие внасяте нов инвентар:
- Когато донесете много нови зеленчуци, ще ви отнеме 20 минути, за да изчистите масата и да замените останалия запас с по-нов продукт.
- Не искате клиентите да седят там и да чакат 20 минути, за да се случи смяната, тъй като повечето ще получат зеленчуците си другаде.
Ами сега, ако имате втора празна маса, където зареждате новите зеленчуци, и докато правите това, клиентите все още могат да купуват по-старите зеленчуци от първата маса? (Нека се преструваме, че не е защото по-старите зеленчуци са се развалили или по друг начин са по-малко желани.)
Опресняване на таблици в SQL Server
Има няколко метода за презареждане на цели таблици, докато те са активно заявени; преди две десетилетия се възползвах необуздано от sp_rename
— Бих играл шел игра с празно сенчесто копие на таблицата, щастливо презареждайки сенчестното копие и след това извършвайки само преименуването в транзакция.
В SQL Server 2005 започнах да използвам схеми за задържане на сенчести копия на таблици, които просто прехвърлих наоколо, използвайки същата техника на игра на черупки, за която писах в тези две публикации:
- Уловки:схема Switch-a-Roo
- Схема Switch-a-Roo, част 2
Единственото предимство на прехвърлянето на обекти между схеми пред преименуването им е, че няма предупредителни съобщения за преименуване на обект – което дори не е проблем, само по себе си, освен че предупредителните съобщения запълват дневниците на историята на агента много по-бързо.
И двата подхода все още изискват заключване за промяна на схемата (Sch-M), така че трябва да изчакат всички съществуващи транзакции да освободят собствените си заключване. След като придобият своето Sch-M заключване, те блокират всички следващи заявки, изискващи заключвания за стабилност на схемата (Sch-S)... което е почти всяка заявка. Това може бързо да се превърне в кошмар за блокираща верига, тъй като всички нови заявки, които се нуждаят от Sch-S, трябва да попаднат на опашка зад Sch-M. (И не, не можете да заобиколите това, като използвате RCSI или NOLOCK
навсякъде, тъй като дори тези заявки все още изискват Sch-S. Не можете да закупите Sch-S с Sch-M на място, тъй като те са несъвместими – Майкъл Дж. Суорт говори за това тук.)
Кендра Литъл наистина ми отвори очите за опасностите с прехвърлянето на схемата в нейната публикация „Постановка на данни:Заключване на опасност с ALTER SCHEMA TRANSFER“. Там тя показва защо прехвърлянето на схема може да бъде по-лошо от преименуването. По-късно тя описа подробно трети и много по-малко въздействащ начин за размяна на таблици, който сега използвам изключително:превключване на дялове. Този метод позволява на превключвателя да изчака с по-нисък приоритет, което дори не е опция с техниките за преименуване или прехвърляне на схема. Джо Сак разгледа подробно това подобрение, добавено още в SQL Server 2014:„Изследване на опциите за изчакване на заключване с нисък приоритет в SQL Server 2014 CTP1.“
Пример за превключване на дялове на SQL сървър
Нека разгледаме основен пример, следвайки задълбочената същност на Кендра тук. Първо, ще създадем две нови бази данни:
СЪЗДАВАНЕ НА БАЗА ДАННИ NewWay;СЪЗДАВАНЕ НА БАЗА ДАННИ OldWay;GO
В новата база данни ще създадем таблица, в която да съхраняваме инвентара си от зеленчуци и две копия на таблицата за нашата игра с черупки:
ИЗПОЛЗВАЙТЕ NewWay;GO CREATE TABLE dbo.Vegetables_NewWay( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO -- трябва да създадем две допълнителни копия на таблицата. CREATE TABLE dbo.Vegetables_NewWay_prev( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO CREATE TABLE dbo.Vegetables_NewWay_hold( VegetableID int, Name sysname, WhenPicked datetime, WhenPickSmapretory) GOСъздаваме процедура, която зарежда етапното копие на таблицата, след което използва транзакция за превключване на текущото копие.
СЪЗДАВАНЕ НА ПРОЦЕДУРА dbo.DoTheVeggieSwap_NewWayASBEGIN SET NOCOUNT ON; ТАБЛИЦА ОТСЪЩАНЕ dbo.Vegetables_NewWay_prev; INSERT dbo.Vegetables_NewWay_prev ИЗБЕРЕТЕ TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FROM sys.dm_exec_sessions AS s CROSS JOIN или INN model.sys. JOIN. all_sql_modules AS m ON o.[object_id] =m.[object_id]; -- трябва да вземете Sch-M ключалки тук:ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ; ПРОМЕНИ ТАБЛИЦА dbo.Vegetables_NewWay ПРЕВЪКНЕТЕ КЪМ dbo.Vegetables_NewWay_hold WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION =1 MINUTES, ABORT_AFTER_WAIT =BLOCKERS)); ПРОМЕНИ ТАБЛИЦА dbo.Vegetables_NewWay_prev ПРЕВЪКНЕТЕ КЪМ dbo.Vegetables_NewWay; ИЗВЪРШВАНЕ НА ТРАНЗАКЦИЯ; -- и сега потребителите ще поискат новите данни в dbo -- могат да превключват старото копие обратно и да го съкратят -- без да пречат на други заявки ALTER TABLE dbo.Vegetables_NewWay_hold ПРЕВЪКНЕТЕ КЪМ dbo.Vegetables_NewWay_prev; ОТРЕЗЯНЕ НА ТАБЛИЦА dbo.Vegetables_NewWay_prev;ENDGOКрасотата на
WAIT_AT_LOW_PRIORITY
е, че можете напълно да контролирате поведението сABORT_AFTER_WAIT
опция:
ABORT_AFTER_WAIT настройка | Описание/симптоми |
---|---|
СЕБЕ | Това означава, че превключвателят ще се откаже след n минути. За сесията, която се опитва да извърши превключването, това ще се появи като съобщение за грешка: Времето за изчакване на заявката за заключване е надвишено. |
БЛОКЕРИ | Това диктува, че превключвателят ще изчака до n минути, след което се принуди да застане в предната част на линията, като убие всички блокиращи пред нея . Сесиите, които се опитват да взаимодействат с таблицата, които са засегнати от операцията за превключване, ще видят някаква комбинация от следните съобщения за грешка: Вашата сесия е прекъсната поради DDL операция с висок приоритет.Не може да продължи изпълнението, защото сесията е в състояние на убийство. Възникна сериозна грешка в текущата команда. Резултатите, ако има такива, трябва да бъдат изхвърлени. |
НЯМА | Това казва, че превключвателят с радост ще изчака, докато му дойде реда, независимо от MAX_DURATION .
Това е същото поведение, което бихте получили при преименуване, прехвърляне на схема или превключване на дял без |
BLOCKERS
опцията не е най-приятелският начин за справяне с нещата, тъй като вече казвате, че е добре чрез тази операция за превключване/превключване, за да могат потребителите да виждат данни, които са малко остарели. Вероятно бих предпочел да използвам SELF
и накарайте операцията да опита отново в случаите, когато не може да получи необходимите ключалки в определеното време. Ще следя колко често се проваля обаче, особено последователните неуспехи, защото искате да сте сигурни, че данните никога няма да станат твърде остарели.
В сравнение със стария начин за превключване между схеми
Ето как щях да се справя с превключването преди:
ИЗПОЛЗВАЙТЕ OldWay;GO -- създайте две схеми и две копия на таблицата CREATE SCHEMA prev АВТОРИЗАЦИЯ dbo;GO CREATE SCHEMA задръжте АВТОРИЗАЦИЯ dbo;GO CREATE TABLE dbo.Vegetables_OldWay( VegetableID int, Name sysedtory, When, Back max));GO CREATE TABLE prev.Vegetables_OldWay( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO CREATE PROCEDURE dbo.DoTheVeggieSwap_OldWayASBEGIN SET NOCOUNT ON; TRUNCATE TABLE prev.Vegetables_OldWay; INSERT prev.Vegetables_OldWay SELECT TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FROM sys.dm_exec_sessions AS s CROSS JOIN model.sys.all model.sys.all o INNER JOIN model.sys.all. all_sql_modules AS m ON o.[object_id] =m.[object_id]; -- трябва да вземете Sch-M ключалки тук:ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ; ALTER SCHEMA hold TRANSFER dbo.Vegetables_OldWay; ALTER SCHEMA dbo TRANSFER prev.Vegetables_OldWay; ИЗВЪРШВАНЕ НА ТРАНЗАКЦИЯ; -- и сега потребителите ще заявят новите данни в dbo -- могат да прехвърлят старото копие обратно и да го съкратят, без -- да пречат на други заявки:ALTER SCHEMA prev TRANSFER hold.Vegetables_OldWay; TRUNCATE TABLE prev.Vegetables_OldWay;ENDGO
Проведох тестове за паралелност, като използвах два прозореца на SQLQueryStress на Erik Ejlskov Jensen:единият за повтаряне на извикване на процедурата всяка минута, а другият за стартиране на 16 нишки по този начин, хиляди пъти:
ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ; UPDATE TOP (1) dbo.
Продължителност и проценти на грешки | Прехвърляне на схема | ABORT_AFTER_WAIT: СЕБЕ | ABORT_AFTER_WAIT: БЛОКЕРИ |
---|---|---|---|
Ср. продължителност – прехвърляне/превключване | 96,4 секунди | 68,4 секунди | 20,8 секунди |
Ср. продължителност – DML | 18,7 секунди | 2,7 секунди | 2,9 секунди |
Изключения – Прехвърляне/Превключване | 0 | 0,5/минута | 0 |
Изключения – DML | 0 | 0 | 25,5/минута |
Имайте предвид, че продължителността и броят на изключенията ще зависят силно от спецификациите на вашия сървър и какво друго се случва във вашата среда. Също така имайте предвид, че въпреки че няма изключения за тестовете за прехвърляне на схема, когато използвате SQLQueryStress, може да ударите някои по-строги изтичания в зависимост от използващото приложение. И средно беше много по-бавно, защото блокирането се натрупваше много по-агресивно. Никой никога не иска изключения, но когато има компромис като този, може да предпочетете няколко изключения тук-там (в зависимост от честотата на операцията за опресняване) пред всички, които чакат по-дълго през цялото време.
Превключване на дял срещу преименуване/прехвърляне на схема за опресняване на таблици на SQL Server
Превключването на дял ви позволява да изберете коя част от вашия процес поема разходите за едновременност. Можете да дадете предпочитание на процеса на превключване, така че данните са по-надеждно свежи, но това означава, че някои от вашите заявки ще се провалят. Обратно, можете да дадете приоритет на заявките с цената на по-бавен процес на опресняване (и случайни неуспехи там). Основната насока е превключването на дялове на SQL Server е превъзходен метод за опресняване на таблиците на SQL Server в сравнение с предишните техники за преименуване/трансфер на схема в почти всички точки и можете да използвате по-стабилна логика за повторни опити или да експериментирате с толеранси на продължителност, за да стигнете до най-доброто място за вашето натоварване.