Et almindeligt krav i ETL og forskellige rapporteringsscenarier er stille og roligt at indlæse en SQL Server-staging-tabel i baggrunden, så brugere, der forespørger på dataene, ikke påvirkes af skrivningerne og omvendt. Tricket er, hvordan og hvornår du peger brugere på den nye, opdaterede version af dataene.
Forenklet eksempel på en iscenesættelsestabel:A Farmer's Market Analogy
Så hvad er en iscenesættelsestabel i SQL? Et mellembord kan lettere forstås ved at bruge et eksempel fra den virkelige verden:Lad os sige, at du har et bord fyldt med grøntsager, du sælger på det lokale landmandsmarked. Efterhånden som dine grøntsager sælges, og du bringer nyt lager ind:
- Når du medbringer et læs nye grøntsager, vil det tage dig 20 minutter at rydde af bordet og erstatte det resterende lager med det nyere produkt.
- Du ønsker ikke, at kunderne skal sidde der og vente 20 minutter på, at skiftet sker, da de fleste vil få deres grøntsager andre steder.
Hvad nu, hvis du havde et andet tomt bord, hvor du læsser de nye grøntsager, og mens du gør det, kan kunderne stadig købe de ældre grøntsager fra det første bord? (Lad os lade som om, det ikke er fordi de ældre grøntsager blev dårlige eller på anden måde er mindre ønskværdige.)
Opdatering af tabeller i SQL Server
Der er flere metoder til at genindlæse hele tabeller, mens de aktivt bliver forespurgt; for to årtier siden udnyttede jeg uhæmmet sp_rename
— Jeg ville spille et shell-spil med en tom skyggekopi af bordet, glad for at genindlæse skyggekopien og derefter kun udføre omdøbningen i en transaktion.
I SQL Server 2005 begyndte jeg at bruge skemaer til at holde skyggekopier af tabeller, jeg simpelthen overførte rundt med den samme shell-spilteknik, som jeg skrev om i disse to indlæg:
- Trickshots:Skema Switch-a-Roo
- Skema Switch-a-Roo, del 2
Den eneste fordel ved at overføre objekter mellem skemaer i forhold til at omdøbe dem er, at der ikke er nogen advarselsmeddelelser om at omdøbe et objekt - hvilket ikke engang er et problem i sig selv, bortset fra at advarselsmeddelelserne fylder agenthistorikloggen så meget hurtigere.
Begge tilgange kræver stadig en skemamodifikationslås (Sch-M), så de skal vente på, at eventuelle eksisterende transaktioner frigiver deres egne låse. Når de først har erhvervet deres Sch-M-lås, blokerer de alle efterfølgende forespørgsler, der kræver skemastabilitetslåse (Sch-S) ... hvilket er næsten alle forespørgsler. Det kan hurtigt blive et blokerende kædemareridt, da alle nye forespørgsler, der kræver Sch-S, skal stå i kø bag Sch-M. (Og nej, du kan ikke komme uden om dette ved at bruge RCSI eller NOLOCK
overalt, da selv disse forespørgsler stadig kræver Sch-S. Du kan ikke erhverve Sch-S med en Sch-M på plads, da de er inkompatible – Michael J. Swart taler om det her.)
Kendra Little åbnede virkelig mine øjne om farerne ved skemaoverførsel i hendes indlæg, "Staging Data:Locking Danger with ALTER SCHEMA TRANSFER." Der viser hun, hvorfor skemaoverførsel kan være værre end at omdøbe. Hun beskrev senere en tredje og meget mindre virkningsfuld måde at bytte tabeller ud på, som jeg nu udelukkende bruger:partitionsskift. Denne metode tillader skiftet at vente med en lavere prioritet, hvilket ikke engang er en mulighed med omdøbnings- eller skemaoverførselsteknikkerne. Joe Sack gik i detaljer om denne forbedring, der blev tilføjet tilbage i SQL Server 2014:"Udforsker indstillinger for lav prioritet låsevent i SQL Server 2014 CTP1."
Eksempel på skift af SQL-serverpartition
Lad os se på et grundlæggende eksempel, efter Kendras grundige kerne her. Først opretter vi to nye databaser:
CREATE DATABASE NewWay;CREATE DATABASE OldWay;GO
I den nye database opretter vi en tabel til vores grøntsagsbeholdning og to kopier af bordet til vores shell-spil:
BRUG NewWay;GO CREATE TABLE dbo.Vegetables_NewWay( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO -- vi skal oprette to ekstra kopier af tabellen. 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, BackStory datetime, BackStory)Vi opretter en procedure, der indlæser den mellemliggende kopi af tabellen og derefter bruger en transaktion til at skifte den aktuelle kopi ud.
OPRET PROCEDURE dbo.DoTheVeggieSwap_NewWayASBEGIN SÆT NOCOUNT ON; TRUNCATE TABEL dbo.Vegetables_NewWay_prev; INSERT dbo.Vegetables_NewWay_prev VÆLG TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FRA sys.dm_exec_sessions AS s CROSS JOIN-model.sys. all_sql_modules AS m ON o.[objekt_id] =m.[objekt_id]; -- nødt til at tage Sch-M låse her:BEGIN TRANSAKTION; ÆNDRINGSTABEL dbo.Vegetables_NewWay SKIFT TIL dbo.Vegetables_NewWay_hold MED (WAIT_AT_LOW_PRIORITY (MAX_DURATION =1 MINUTTER, ABORT_AFTER_WAIT =BLOCKERS)); ÆNDRINGSTABEL dbo.Vegetables_NewWay_prev SKIFT TIL dbo.Vegetables_NewWay; FORBAGE TRANSAKTION; -- og nu vil brugere forespørge på de nye data i dbo -- kan skifte den gamle kopi tilbage og afkorte den -- uden at forstyrre andre forespørgsler ÆNDRINGSTABEL dbo.Vegetables_NewWay_hold SKIFT TIL dbo.Vegetables_NewWay_prev; TRUNCATE TABEL dbo.Vegetables_NewWay_prev;ENDGOSkønheden ved
WAIT_AT_LOW_PRIORITY
er, at du fuldstændigt kan kontrollere adfærden medABORT_AFTER_WAIT
mulighed:
ABORT_AFTER_WAIT indstilling | Beskrivelse/symptomer |
---|---|
SELV | Dette betyder, at kontakten vil give op efter n minutter. For den session, der forsøger at udføre skiftet, vises dette som fejlmeddelelsen: Timeout-periode for låseanmodning er overskredet. |
BLOKERERE | Dette dikterer, at switchen vil vente op til n minutter, så tvinge sig selv til fronten ved at dræbe alle blokeringerne foran den . Sessioner, der forsøger at interagere med tabellen, som bliver stødt af switch-operationen, vil se en kombination af disse fejlmeddelelser: Din session er blevet afbrudt på grund af en højprioritet DDL-handling.Kan ikke fortsætte udførelsen, fordi sessionen er i kill-tilstand. Der opstod en alvorlig fejl på den aktuelle kommando. Resultaterne, hvis nogen, bør kasseres. |
INGEN | Dette siger, at kontakten med glæde vil vente, indtil den bliver sin tur, uanset MAX_DURATION .
Dette er den samme adfærd, som du ville få med omdøbning, skemaoverførsel eller partitionsskift uden |
BLOCKERS
option er ikke den venligste måde at håndtere tingene på, da du allerede siger, at det er okay gennem denne iscenesættelse/switch-operation for brugere at se data, der er lidt forældede. Jeg vil sandsynligvis foretrække at bruge SELF
og få operationen til at prøve igen i tilfælde, hvor den ikke kunne få de nødvendige låse inden for den tildelte tid. Jeg vil dog holde styr på, hvor ofte det fejler, især på hinanden følgende fejl, fordi du vil sikre dig, at dataene aldrig bliver for forældede.
Sammenlignet med den gamle måde at skifte mellem skemaer på
Sådan ville jeg have håndteret skiftet før:
BRUG OldWay;GO -- opret to skemaer og to kopier af tabellen CREATE SCHEMA prev AUTHORIZATION dbo;GO CREATE SCHEMA hold AUTHORIZATION dbo;GO CREATE TABLE dbo.Vegetables_OldWay( VegetableID int, Name sysname, WhenStory date, WhenPicked dat 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 VÆLG TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FRA sys.dm_exec_sessions AS s CROSS JOIN model.sys.all IN model.sys. all_sql_modules AS m ON o.[objekt_id] =m.[objekt_id]; -- nødt til at tage Sch-M låse her:BEGIN TRANSAKTION; ALTER SCHEMA hold TRANSFER dbo.Vegetables_OldWay; ALTER SCHEMA dbo TRANSFER prev. Vegetables_OldWay; FORBAGE TRANSAKTION; -- og nu vil brugere forespørge på de nye data i dbo -- kan overføre den gamle kopi tilbage og afkorte den uden -- at forstyrre andre forespørgsler:ALTER SCHEMA prev TRANSFER hold.Vegetables_OldWay; TRUNCATE TABLE prev.Vegetables_OldWay;ENDGO
Jeg kørte samtidighedstest ved at bruge to vinduer af Erik Ejlskov Jensens SQLQueryStress:det ene til at gentage et kald til proceduren hvert minut, og det andet til at køre 16 tråde som denne, tusindvis af gange:
BEGIN TRANSAKTIONEN; OPDATERING TOP (1) dbo.
Varighed og fejlrater | Skemaoverførsel | ABORT_AFTER_WAIT: SELV | ABORT_AFTER_WAIT: BLOCKERS |
---|---|---|---|
Gns. varighed – Overfør/Switch | 96,4 sekunder | 68,4 sekunder | 20,8 sekunder |
Gns. varighed – DML | 18,7 sekunder | 2,7 sekunder | 2,9 sekunder |
Undtagelser – Overfør/Switch | 0 | 0,5/minut | 0 |
Undtagelser – DML | 0 | 0 | 25,5/minut |
Bemærk, at varigheden og antallet af undtagelser vil være meget afhængig af dine serverspecifikationer og hvad der ellers foregår i dit miljø. Bemærk også, at selvom der ikke var nogen undtagelser for skemaoverførselstesten, når du bruger SQLQueryStress, kan du muligvis ramme nogle mere strenge timeouts afhængigt af den forbrugende applikation. Og det var så meget langsommere i gennemsnit, fordi blokeringen hobede sig meget mere aggressivt op. Ingen ønsker nogensinde undtagelser, men når der er en afvejning som denne, foretrækker du måske nogle få undtagelser hist og her (afhængigt af hyppigheden af opdateringsoperationen) frem for at alle venter længere hele tiden.
Partitionsskift vs. omdøb/skemaoverførsel til opdatering af SQL Server-tabeller
Partitionsskift giver dig mulighed for at vælge, hvilken del af din proces, der bærer omkostningerne ved samtidighed. Du kan give fortrinsret til skifteprocessen, så dataene er mere pålidelige, men det betyder, at nogle af dine forespørgsler vil mislykkes. Omvendt kan du prioritere forespørgslerne på bekostning af en langsommere opdateringsproces (og lejlighedsvis fejl der). Hovedindsatsen er SQL Server-partitionsskiftning er en overlegen metode til at opdatere SQL Server-tabeller sammenlignet med de tidligere omdøbnings-/skemaoverførselsteknikker på næsten alle punkter, og du kan bruge mere robust genforsøgslogik eller eksperimentere med varighedstolerancer for at lande på sweet spot for din arbejdsbyrde.