sql >> Database teknologi >  >> RDS >> Database

Minimering af virkningen af ​​at udvide en IDENTITY-søjle – del 1

[ Del 1 | Del 2 | Del 3 | Del 4 ]

Et problem, som jeg har set dukke op et par gange for nylig, er scenariet, hvor du har oprettet en IDENTITY-kolonne som en INT, og nu nærmer dig den øvre grænse og skal gøre den større (BIGINT). Hvis dit bord er stort nok til, at du rammer den øvre grænse af et heltal (over 2 milliarder), er dette ikke en operation, du kan gennemføre mellem frokost og kaffepause på en tirsdag. Denne serie vil udforske mekanikken bag en sådan ændring og forskellige måder at få det til at ske med varierende indvirkning på oppetiden. I den første del ville jeg se nærmere på den fysiske virkning af at ændre en INT til en BIGINT uden nogen af ​​de andre variabler.

Hvad sker der faktisk, når du udvider en INT?

INT og BIGINT er datatyper med fast størrelse, derfor skal en konvertering fra den ene til den anden røre siden, hvilket gør dette til en datastørrelsesoperation. Dette er kontraintuitivt, fordi det ser ud til, at det ikke ville være muligt for en datatypeændring fra INT til BIGINT at kræve ekstra plads på siden med det samme (og for en IDENTITY-kolonne, nogensinde). Når man tænker logisk, er dette plads, der umuligt kunne være nødvendig før senere, hvor en eksisterende INT-værdi blev ændret til en værdi> 4 bytes. Men sådan fungerer det ikke i dag. Lad os lave en simpel tabel og se:

CREATE TABLE dbo.FirstTest( RowID int IDENTITY(1,1), Filler char(2500) NOT NULL DEFAULT 'x');GO INSERT dbo.FirstTest WITH (TABLOCKX) (Filler)SELECT TOP (20) 'x ' FROM sys.all_columns AS c;GO

En simpel forespørgsel kan fortælle mig den lave og høje side, der er allokeret til dette objekt, såvel som det samlede sideantal:

SELECT lo_page =MIN(allocated_page_page_id), hi_page =MAX(allocated_page_page_id), page_count =COUNT(*)FROM sys.dm_db_database_page_allocations( DB_ID(), OBJECT_ID(N'dbo.FirstTest'), NULL, NULL); 

Hvis jeg nu kører den forespørgsel før og efter at have ændret datatypen fra INT til BIGINT:

ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint;

Jeg ser disse resultater:

-- før:lo_page hi_page pagecount------- ------- ----------243 303 17 -- after:lo_page hi_page page_count------ - ------- ----------243 319 33

Det er tydeligt, at 16 nye sider blev tilføjet for at give plads til den ekstra plads, der kræves (selvom vi ved, at ingen af ​​værdierne i tabellen faktisk kræver 8 bytes). Men dette blev faktisk ikke opnået, som du måske tror – i stedet for at udvide kolonnen på de eksisterende sider, blev rækkerne flyttet til nye sider, med pegepinde efterladt i deres plads. Ser på side 243 før og efter (med den udokumenterede DBCC PAGE ):

-- ******** Side 243, før:******** Slot 0 Offset 0x60 Længde 12 Record Type =PRIMARY_RECORD Record Attributes =NULL_BITMAP Record Size =12 Memory Dump @0x000000E34B9FA060 00000000000:10000900 01000000 78020000 .. .....x... Slot 0 Kolonne 1 Offset 0x4 Længde 4 Længde (fysisk) 4 RowID =1 Slot 0 Kolonne 2 Offset 0x8 Længde 1 Længde (fysisk) 1 fyldstof =x -- ****** Side 243, efter:******** Slot 0 Offset 0x60 Længde 9 Record Type =FORWARDING_STUB Record Attributes =Record Size =9 Memory Dump @0x000000E34B9FA060 00000000000000000:04280010 0.(0.7000100 ..x.Videresendelse til =fil 1 side 296 slot 376

Hvis vi så ser på målet for markøren, side 296, slot 376, ser vi:

meget ..000000000000000014:01002280 0004f300 00000100 0000 .."...ó.......Videresendes fra =fil 1 side 243 spalte 0 Slot 376 Kolonne 67108865 Længde 04 PPRO 3 Længde 04 PPRO Længde 04 PPRO 04 Længde DRO Længde 3 2 Offset 0x8 Længde 1 Længde (fysisk) 1 fyldstof =x Slot 376 Kolonne 1 Offset 0x9 Længde 8 Længde (fysisk) 8 RowID =1

Dette er naturligvis en meget forstyrrende ændring af bordets struktur. (Og en interessant sideobservation:den fysiske rækkefølge af kolonnerne, RowID og filler, er blevet vendt på siden.) Reserveret plads hopper fra 136 KB til 264 KB, og den gennemsnitlige fragmentering stiger beskedent fra 33,3 % til 40 %. Denne plads bliver ikke genvundet af en genopbygning, online eller ej, eller en reorganisering, og – som vi snart vil se – det er ikke fordi bordet er for lille til at gavne.

Bemærk:dette er sandt selv i de seneste builds af SQL Server 2016 – mens flere og flere operationer som denne er blevet forbedret til kun at blive metadata-operationer i moderne versioner, er denne endnu ikke blevet rettet, men det er klart det kunne være – igen, især i det tilfælde, hvor kolonnen er en IDENTITY-kolonne, som ikke kan opdateres pr. definition.

At udføre operationen med den nye ALTER COLUMN / ONLINE syntaks, som jeg talte om sidste år, giver nogle forskelle:

-- drop / re-create hereALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint WITH (ONLINE =ON);

Nu bliver før og efter:

-- før:lo_page hi_page pagecount------- ------- ----------243 303 17 -- after:lo_page hi_page page_count------ - ------- ----------307 351 17

I dette tilfælde var det stadig en datastørrelsesoperation, men de eksisterende sider blev kopieret og genskabt på grund af ONLINE-indstillingen. Du undrer dig måske over, hvorfor, når vi ændrede kolonnestørrelsen som en ONLINE-operation, tabellen er i stand til at proppe flere data ind på det samme antal sider? Hver side er nu tættere (færre rækker, men flere data pr. side) på bekostning af spredning – fragmentering fordobles fra 33,3 % til 66,7 %. Brugt plads viser flere data på den samme reserverede plads (fra 72 KB / 136 KB til 96 KB / 136 KB).

Og i større skala?

Lad os droppe tabellen, genskabe den og udfylde den med mange flere data:

CREATE TABLE dbo.FirstTest( RowID INT IDENTITY(1,1), filler CHAR(1) NOT NULL DEFAULT 'x');GO INSERT dbo.FirstTest WITH (TABLOCKX) (filler) SELECT TOP (5000000) 'x ' FRA sys.all_columns AS c1 CROSS JOIN sys.all_columns AS c2;

Fra begyndelsen har vi nu 8.657 sider, et fragmenteringsniveau på 0,09 %, og den brugte plads er 69.208 KB / 69.256 KB.

Hvis vi ændrer datatypen til bigint, springer vi til 25.630 sider, fragmentering reduceres til 0,06%, og pladsforbruget er 205.032 KB / 205.064 KB. En online genopbygning ændrer intet, og heller ikke en omorganisering. Hele processen, inklusive en genopbygning, tager omkring 97 sekunder på min maskine (datapopulationen tog hele 2 sekunder).

Hvis vi ændrer datatypen til bigint ved hjælp af ONLINE, er bumpet kun på 11.140 sider, fragmenteringen går til 85,5%, og den brugte plads er 89.088 KB / 89160 KB. Online genopbygninger og reorganiseringer ændrer stadig intet. Denne gang tager hele processen kun omkring et minut. Så den nye syntaks fører bestemt til hurtigere operationer og mindre ekstra diskplads, men høj fragmentering. Jeg tager den.

Næste

Jeg er sikker på, at du kigger på mine test ovenfor og undrer dig over et par ting. Vigtigst af alt, hvorfor er bordet en bunke? Jeg ønskede at undersøge, hvad der rent faktisk sker med sidestrukturen og sideantallet, uden at indekser, nøgler eller begrænsninger blander detaljerne. Du kan også undre dig over, hvorfor denne ændring var så let - i et scenarie, hvor du skal ændre en sand IDENTITY-kolonne, er det sandsynligvis også den klyngede primærnøgle og har fremmednøgleafhængigheder i andre tabeller. Dette introducerer helt sikkert nogle hikke til processen. Vi vil se nærmere på disse ting i det næste indlæg i serien.

[ Del 1 | Del 2 | Del 3 | Del 4 ]


  1. Hvordan kan jeg liste ALLE tilskud en bruger har modtaget?

  2. Nulstil AutoIncrement i SQL Server efter sletning

  3. Reference:Hvad er et perfekt kodeeksempel ved hjælp af MySQL-udvidelsen?

  4. Hent billede fra databasen i asp.net