Før vi gennemgår ydelsesproblemet med Forwarded Records og løser det, skal vi gennemgå strukturen af SQL Server-tabellerne.
Tabelstrukturoversigt
I SQL Server er den grundlæggende enhed for datalageret 8-KB-siderne . Hver side starter med en 96-byte header, der gemmer systemoplysningerne om den pågældende side. Derefter vil tabelrækkerne blive lagret på datasiderne serielt efter overskriften. I slutningen af siden vil rækkeforskydningstabellen, der indeholder én indgang for hver række, blive gemt modsat rækkefølgen af rækkerne på siden. Denne rækkeforskydningsindgang viser, hvor langt den første byte i denne række er placeret fra starten af siden.
SQL Server giver os to typer tabeller, baseret på strukturen af den tabel. Den Klyngede tabel gemmer og sorterer dataene på datasiderne baseret på de foruddefinerede Clustered index key-kolonne eller kolonneværdier. Derudover er datasiderne i Clustered-tabellen sorteret og kædet sammen i en sammenkædet liste baseret på Clustered-indeksnøgleværdierne. B-træet strukturen af Clustered-indekset giver en hurtig dataadgangsmetode baseret på Clustered-indeksnøgleværdierne. Hvis en ny række indsættes, eller en eksisterende nøgleværdi opdateres i Clustered-tabellen, gemmer SQL Server den nye værdi i den korrekte logiske position, der passer til den indsatte rækkestørrelse uden at bryde bestillingskriterierne. Hvis den indsatte eller opdaterede værdi er større end den tilgængelige plads på datasiden, opdeles siden i to sider, så den passer til den nye værdi.
Den anden type tabeller er Heap tabel, hvor dataene ikke er sorteret inden for datasiderne i en hvilken som helst rækkefølge, og siderne ikke er kædet sammen, da der ikke er defineret et Clustered index på den tabel, for at håndhæve eventuelle sorteringskriterier. At spore sider, der ikke er sorteret i nogen bestillingskriterier eller kædet sammen i heap-tabellen, er ikke en nem mission. For at forenkle sporingsprocessen for sideallokeringen i heaptabellen bruger SQL Server Index Allocation Map (IAM), den eneste logiske forbindelse mellem datasiderne i heap-tabellen, ved at beholde en post for hver dataside i tabellen eller indekset i IAM-tabellen. For at hente data fra heap-tabellen scanner SQL Server Engine IAM'et for at lokalisere omfanget, som danner 8 sider, der gemmer de anmodede data.
Problem med videresendte poster
Hvis en ny række indsættes i heap-tabellen, vil SQL Server Engine scanne Page Free Space (PFS) sider for at spore allokeringsstatus og pladsforbrug på hver dataside for at finde den første tilgængelige placering på datasiderne, der passer til den indsatte rækkestørrelse. Derefter vil rækken blive tilføjet til den valgte side. Hvis den indsatte værdi er større end den tilgængelige plads på datasiderne, tilføjes en ny side til den tabel for at kunne indsætte den nye værdi.
På den anden side, hvis de eksisterende data i heap-tabellen ændres, for eksempel, opdaterede vi en streng med variabel længde med større datastørrelse, og det aktuelle rum passer ikke til de nye data, vil dataene blive flyttet til en anden fysisk placering og videresendt post vil blive indsat i heap-tabellen på den oprindelige dataplacering for at pege på den nye placering af disse data og for at forenkle sporingsdataplaceringen. Den nye dataplacering indeholder også en pointer, der peger på videresendelsesmarkøren for at holde den opdateret i tilfælde af flytning af data fra den nye placering og for at forhindre den lange videresendelsesmarkørkæde eller slette den. Dette kan også føre til, at videresendelsesposten fjernes.
Selvom omdirigeringsmetoden for Forwarded Records reducerer behovet for genopbygning af de ressourcekrævende tabel- og ikke-klyngede indekser for at opdatere dataadresserne, hver gang placeringen af dataene ændres, fordobler den også antallet af læsninger, der kræves for at hente dataene. SQL Server vil først besøge den gamle placering, hvor den finder den videresendte post, der omdirigerer den til den nye dataplacering. Derefter vil den læse de anmodede data og udføre læseoperationen to gange. Derudover fører problemet med Forwarded Records til ændring af de sekventielle data, der læses til tilfældige data, hvilket påvirker datahentningsoperationens ydeevne over tid negativt.
Lad os oprette følgende ForwardRecordDemo bunke tabel ved hjælp af CREATE TABLE T-SQL-sætningen nedenfor:
CREATE TABLE ForwardRecordDemo ( ID INT IDENTITY (1,1), Emp_Name NVARCHAR (50), Emp_BirthDate DATETIME, Emp_Salary INT )
Udfyld derefter tabellen med 3K-poster til testformål ved hjælp af INSERT INTO T-SQL-sætningen nedenfor:
INSERT INTO ForwardRecordDemo VALUES ('John','2000-05-05',500) GO 1000 INSERT INTO ForwardRecordDemo VALUES ('Zaid','1999-01-07',700) GO 1000 INSERT INTO ForwardRecordDemo VALUES ('Frank','1988-07-04',900) GO 1000
Identifikation af problemet med videresendte poster
Oplysningerne om tabeltypen og antallet af forbrugte sider under lagring af tabeldataene samt indeksfragmenteringsprocenten og antallet af videresendte poster for en specifik tabel kan ses ved at forespørge på sys.dm_db_index_physical_stats systemets dynamiske administrationsfunktion og ved at gå videre til DETALJERET tilstand for at returnere antallet af videresendelsesposter. For at gøre dette skal du bruge T-SQL-scriptet nedenfor:
SELECT OBJECT_NAME(PhysSta.object_id) as DBTableName, PhysSta.index_type_desc, PhysSta.avg_fragmentation_in_percent, PhysSta.forwarded_record_count, PhysSta.page_count FROM sys.dm_db_index_physical_stats (DB_ID(), DEFAULT, DEFAULT, DEFAULT, 'DETAILED') AS PhysSta WHERE OBJECT_NAME(PhysSta.object_id) = 'ForwardRecordDemo' AND forwarded_record_count is NOT NULL
Som du kan se fra forespørgselsresultatet, er den forrige tabel heap-tabellen, der ikke har noget Clustered-indeks oprettet på sig for at sortere dataene på siderne og linke siderne mellem hinanden. De 3K-rækker, der er indsat i tabellen, er tildelt 15 datasider, uden videresendte poster og nul fragmenteringsprocent, som vist i resultatet nedenfor:
Når du definerer datatypen for en kolonne som VARCHAR eller NVARCHAR, er den værdi, der er angivet i datatypedefinitionen, den maksimalt tilladte størrelse for den pågældende streng uden at reservere det beløb fuldstændigt, mens værdierne gemmes på datasiderne. For eksempel John medarbejdernavn indsat i denne tabel vil kun reservere 8 bytes ud af de maksimale 100 bytes til den kolonne, idet der tages højde for, at lagring af NVARCHAR-strengen vil fordoble de nødvendige bytes til VARCHAR-kolonnen, som vist i DATALENGTH funktionsresultat nedenfor:
Hvis du vil opdatere værdien af kolonnen Emp_Name for at inkludere det fulde navn på John-medarbejderen, skal du bruge UPDATE-erklæringen nedenfor:
UPDATE ForwardRecordDemo SET Emp_Name='John David Micheal' WHERE Emp_Name='John'
Tjek længden af den opdaterede kolonne ved hjælp af DATALENGTH fungere. Du vil se, at længden af Emp_Name-kolonnen i de opdaterede rækker er blevet udvidet med 28 bytes pr. hver kolonne, hvilket er omkring 3,5 yderligere datasider til den tabel, som vist i resultatet nedenfor:
Kontroller derefter antallet af videresendte poster efter opdateringshandlingen ved at forespørge på sys.dm_db_index_physical_stats-systemets dynamiske administrationsfunktion. For at gøre dette skal du bruge T-SQL-scriptet nedenfor:
SELECT OBJECT_NAME(PhysSta.object_id) as DBTableName, PhysSta.index_type_desc, PhysSta.avg_fragmentation_in_percent, PhysSta.forwarded_record_count, PhysSta.page_count FROM sys.dm_db_index_physical_stats (DB_ID(), DEFAULT, DEFAULT, DEFAULT, 'DETAILED') AS PhysSta WHERE OBJECT_NAME(PhysSta.object_id) = 'ForwardRecordDemo' AND forwarded_record_count is NOT NULL
Som du kan se, vil opdatering af Emp_Name-kolonnen på 1K-poster med større strengværdier uden at tilføje nogen ny post tildele de ekstra 5 sider til den tabel i stedet for 3,5 sider som forventet tidligere. Dette vil ske på grund af generering af 484 videresendte poster for at pege på de nye placeringer af de flyttede data. Dette kan få tabellen til at være 33 % fragmenteret, som vist tydeligt nedenfor:
Igen, hvis det lykkes dig at opdatere værdien af kolonnen Emp_Name til at inkludere det fulde navn på Zaid-medarbejderen, skal du bruge UPDATE-erklæringen nedenfor:
UPDATE ForwardRecordDemo SET Emp_Name='Zaid Fuad Zreeq' WHERE Emp_Name='Zaid'
Tjek længden af den opdaterede kolonne ved hjælp af DATALENGTH fungere. Du vil se, at længden af Emp_Name-kolonnen i de opdaterede rækker udvidet med 22 bytes pr. hver kolonne, hvilket er omkring 2,7 yderligere datasider tilføjet til denne tabel, som vist i resultatet nedenfor:
Kontroller antallet af videresendte poster efter at have udført opdateringshandlingen. Du kan gøre dette ved at forespørge på sys.dm_db_index_physical_stats-systemets dynamiske administrationsfunktion ved at bruge det samme T-SQL-script nedenfor:
SELECT OBJECT_NAME(PhysSta.object_id) as DBTableName, PhysSta.index_type_desc, PhysSta.avg_fragmentation_in_percent, PhysSta.forwarded_record_count, PhysSta.page_count FROM sys.dm_db_index_physical_stats (DB_ID(), DEFAULT, DEFAULT, DEFAULT, 'DETAILED') AS PhysSta WHERE OBJECT_NAME(PhysSta.object_id) = 'ForwardRecordDemo' AND forwarded_record_count is NOT NULL
Resultatet vil vise dig, at opdatering af Emp_Name-kolonnen på de andre 1K-poster med større strengværdier uden at indsætte en ny række vil tildele yderligere 4 sider til den tabel i stedet for 2,7 sider som forventet. Dette vil ske på grund af generering af yderligere 417 videresendte registreringer for at pege på de nye placeringer af de flyttede data og bevare de samme 33 % fragmenteringsprocent, som vist nedenfor:
Løsning af problemet med videresendte optegnelser
Den enkleste måde at løse problemet med videresendte poster på er at estimere den maksimale længde af strengen, der vil blive gemt i kolonnen og tildele den ved at bruge den faste længde datatype for den pågældende kolonne i stedet for at bruge datatypen med variabel længde. Den optimale permanente måde at løse problemet med videresendte poster på er at tilføje Clustered index til det bord. På denne måde vil tabellen blive fuldstændigt konverteret til en Clustered tabel, der er sorteret baseret på Clustered index nøgleværdierne. Det vil kontrollere rækkefølgen af de eksisterende data, de nyligt indsatte og opdaterede data, der ikke passer til den aktuelle tilgængelige plads på datasiden, som beskrevet tidligere i introduktionen af denne artikel.
Hvis tilføjelse af Clustered-indekset til den tabel ikke er en mulighed for specifikke krav, såsom staging-tabellerne eller ETL-tabellerne, kan du løse problemet med Forwarded Records midlertidigt ved at overvåge de Forwarded Records og genopbygge heap-tabellen for at fjerne den, hvilket vil også opdatere alle ikke-klyngede indekser på den heap-tabel. Funktionaliteten til at genopbygge heaptabellen introduceres i SQL Server 2008 ved at bruge ALTER TABLE...REBUILD T-SQL-kommando.
For at se ydeevnepåvirkningen af de videresendte poster på datahentningsforespørgslerne, lad os køre SELECT-forespørgslen, der udfører søgningen baseret på Emp_Name-kolonnens værdierю Inden du udfører forespørgslen, skal du dog aktivere TIME- og IO-statistikken:
SET STATISTICS TIME ON SET STATISTICS IO ON SELECT * FROM ForwardRecordDemo WHERE Emp_Name like 'John%'
Som et resultat vil du se den 925 logiske læseoperationer udføres for at hente de anmodede data inden for 84ms som vist nedenfor:
For at genopbygge heaptabellen for at fjerne alle videresendte poster, brug kommandoen ALTER TABLE...REBUILD:
ALTER TABLE ForwardRecordDemo REBUILD;
Kør den samme SELECT-sætning igen:
SELECT * FROM ForwardRecordDemo WHERE Emp_Name like 'John%'
TIME- og IO-statistikken viser dig, at kun 21 logiske læseoperationer sammenlignet med 925 logiske læseoperationer med de videresendte poster inkluderet udføres for at hente de anmodede data inden for 79 ms :
For at kontrollere antallet af videresendte poster efter genopbygning af heap-tabellen skal du køre sys.dm_db_index_physical_stats-systemets dynamiske administrationsfunktion, bruge det samme T-SQL-script nedenfor:
SELECT OBJECT_NAME(PhysSta.object_id) as DBTableName, PhysSta.index_type_desc, PhysSta.avg_fragmentation_in_percent, PhysSta.forwarded_record_count, PhysSta.page_count FROM sys.dm_db_index_physical_stats (DB_ID(), DEFAULT, DEFAULT, DEFAULT, 'DETAILED') AS PhysSta WHERE OBJECT_NAME(PhysSta.object_id) = 'ForwardRecordDemo' AND forwarded_record_count is NOT NULL
Du vil kun se 21 sider med de foregående 3 sider, der forbruges til de videresendte poster, tildeles den tabel for at gemme dataene, hvilket svarer til det estimerede resultat, vi har fået under dataindsættelses- og opdateringsoperationerne (15+3.5+2.7). Efter genopbygning af heap-tabellen fjernes alle videresendte poster nu. Som et resultat har vi en tabel uden fragmentering:
Problemet med Forwarded Records er et vigtigt præstationsproblem, som databaseadministratorerne bør overveje, når de planlægger for bunke bord vedligeholdelse. De tidligere resultater er hentet fra vores testtabel, der kun indeholder 3K-poster. Du kan forestille dig antallet af sider, der vil blive spildt af de videresendte poster og forringelsen af I/O-ydelsen på grund af læsning af et stort antal videresendte poster, når du læser fra store tabeller!
Referencer:
- Arkitekturvejledning til sider og udvidelser
- dm_db_index_physical_stats (Transact-SQL)
- ÆNDRINGSTABEL (Transact-SQL)
- Kendskab til "Forwarded Records" kan hjælpe med at diagnosticere svære at finde ydeevneproblemer