Database
 sql >> Baza danych >  >> RDS >> Database

Minimalizowanie wpływu poszerzenia kolumny TOŻSAMOŚĆ – część 1

[ Część 1 | Część 2 | Część 3 | Część 4 ]

Problem, który pojawił się ostatnio kilka razy, to scenariusz, w którym utworzyłeś kolumnę IDENTITY jako INT, a teraz zbliżasz się do górnej granicy i musisz ją powiększyć (BIGINT). Jeśli Twój stół jest na tyle duży, że osiągasz górną granicę liczby całkowitej (ponad 2 miliardy), nie jest to operacja, którą możesz wykonać między obiadem a przerwą na kawę we wtorek. W tej serii omówimy mechanikę takiej zmiany i różne sposoby jej wprowadzenia z różnym wpływem na czas pracy bez przestojów. W pierwszej części chciałem przyjrzeć się z bliska fizycznemu wpływowi zmiany INT na BIGINT bez innych zmiennych.

Co się właściwie dzieje, gdy poszerzasz INT?

INT i BIGINT są typami danych o stałym rozmiarze, dlatego konwersja z jednego do drugiego musi dotykać strony, co czyni tę operację wielkością danych. Jest to sprzeczne z intuicją, ponieważ wydaje się, że nie byłoby możliwe, aby zmiana typu danych z INT na BIGINT wymagała natychmiastowego dodatkowego miejsca na stronie (i kiedykolwiek dla kolumny IDENTITY). Myśląc logicznie, jest to przestrzeń, która prawdopodobnie nie będzie potrzebna do czasu, gdy istniejąca wartość INT zostanie zmieniona na wartość> 4 bajty. Ale dziś tak to nie działa. Stwórzmy prostą tabelę i zobaczmy:

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

Proste zapytanie może mi podać dolną i górną stronę przydzieloną do tego obiektu, a także całkowitą liczbę stron:

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, NULL
);

Teraz, jeśli uruchomię to zapytanie przed i po zmianie typu danych z INT na BIGINT:

ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint;

Widzę te wyniki:

-- before:
 
lo_page    hi_page    page_count
-------    -------    ----------
243        303        17
 
-- after:
 
lo_page    hi_page    page_count
-------    -------    ----------
243        319        33

Oczywiste jest, że dodano 16 nowych stron, aby zrobić miejsce na wymagane dodatkowe miejsce (mimo że wiemy, że żadna z wartości w tabeli nie wymaga w rzeczywistości 8 bajtów). Ale nie udało się tego osiągnąć w sposób, w jaki można by sądzić – zamiast poszerzyć kolumnę na istniejących stronach, wiersze zostały przeniesione na nowe strony, z pozostawionymi wskaźnikami. Patrząc na stronę 243 przed i po (z nieudokumentowaną DBCC PAGE) ):

-- ******** Page 243, before: ********
 
Slot 0 Offset 0x60 Length 12
 
Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP    Record Size = 12
 
Memory Dump @0x000000E34B9FA060
 
0000000000000000:   10000900 01000000 78020000                    ..	.....x...
 
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4
 
RowID = 1                           
 
Slot 0 Column 2 Offset 0x8 Length 1 Length (physical) 1
 
filler = x                          
 
 
-- ******** Page 243, after: ********
 
Slot 0 Offset 0x60 Length 9
 
Record Type = FORWARDING_STUB       Record Attributes =                 Record Size = 9
 
Memory Dump @0x000000E34B9FA060
 
0000000000000000:   04280100 00010078 01                          .(.....x.
Forwarding to  =  file 1 page 296 slot 376

Następnie, jeśli spojrzymy na cel wskaźnika, strona 296, szczelina 376, zobaczymy:

Slot 376 Offset 0x8ca Length 34
 
Record Type = FORWARDED_RECORD      Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 34                    
Memory Dump @0x000000E33BBFA8CA
 
0000000000000000:   32001100 01000000 78010000 00000000 00030000  2.......x...........
0000000000000014:   01002280 0004f300 00000100 0000               .."...ó.......
Forwarded from  =  file 1 page 243 slot 0                                
 
Slot 376 Column 67108865 Offset 0x4 Length 0 Length (physical) 4
 
DROPPED = NULL                      
 
Slot 376 Column 2 Offset 0x8 Length 1 Length (physical) 1
 
filler = x                          
 
Slot 376 Column 1 Offset 0x9 Length 8 Length (physical) 8
 
RowID = 1

To oczywiście bardzo destrukcyjna zmiana w strukturze tabeli. (I ciekawa obserwacja poboczna:fizyczna kolejność kolumn, RowID i wypełniacz, zostały odwrócone na stronie.) Zarezerwowane miejsce skacze z 136 KB do 264 KB, a średnia fragmentacja wzrasta nieznacznie z 33,3% do 40%. Ta przestrzeń nie jest odzyskiwana przez przebudowę, online lub nie, ani przez reorg, i – jak wkrótce zobaczymy – nie dzieje się tak dlatego, że tabela jest zbyt mała, aby z niej skorzystać.

Uwaga:dotyczy to nawet najnowszych kompilacji SQL Server 2016 – chociaż coraz więcej takich operacji zostało ulepszonych, aby w nowoczesnych wersjach stały się operacjami tylko na metadanych, ta nie została jeszcze naprawiona, choć wyraźnie może tak być – znowu, szczególnie w przypadku, gdy kolumna jest kolumną IDENTITY, która z definicji nie może być aktualizowana.

Wykonanie operacji z nową składnią ALTER COLUMN / ONLINE, o której mówiłem w zeszłym roku, daje pewne różnice:

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

Teraz „przed” i „po” staje się:

-- before:
 
lo_page    hi_page    page_count
-------    -------    ----------
243        303        17
 
-- after:
 
lo_page    hi_page    page_count
-------    -------    ----------
307        351        17

W tym przypadku była to nadal operacja rozmiaru danych, ale istniejące strony zostały skopiowane i odtworzone dzięki opcji ONLINE. Możesz się zastanawiać, dlaczego, kiedy zmieniliśmy rozmiar kolumny jako operację ONLINE, tabela jest w stanie upchnąć więcej danych na tej samej liczbie stron? Każda strona jest teraz gęstsza (mniej wierszy, ale więcej danych na stronie), kosztem rozproszenia – fragmentacja podwaja się z 33,3% do 66,7%. Wykorzystana przestrzeń pokazuje więcej danych w tej samej zarezerwowanej przestrzeni (od 72 KB / 136 KB do 96 KB / 136 KB).

A na większą skalę?

Upuśćmy tabelę, utwórzmy ją ponownie i wypełnijmy dużo większą ilością danych:

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' FROM sys.all_columns AS c1
  CROSS JOIN sys.all_columns AS c2;

Od początku mamy teraz 8657 stron, poziom fragmentacji 0,09%, a wykorzystane miejsce to 69 208 KB / 69 256 KB.

Jeśli zmienimy typ danych na bigint, przeskoczymy do 25 630 stron, fragmentacja zostanie zmniejszona do 0,06%, a wykorzystane miejsce to 205 032 KB / 205 064 KB. Przebudowa online niczego nie zmienia, podobnie jak reorg. Cały proces, w tym odbudowa, zajmuje na moim komputerze około 97 sekund (populacja danych zajęła całe 2 sekundy).

Jeśli zmienimy typ danych na bigint za pomocą ONLINE, podbicie wynosi tylko 11 140 stron, fragmentacja sięga 85,5%, a wykorzystane miejsce to 89 088 KB / 89160 KB. Przebudowy i reorganizacje online nadal niczego nie zmieniają. Tym razem cały proces zajmuje tylko około minuty. Tak więc nowa składnia zdecydowanie prowadzi do szybszych operacji i mniejszej ilości dodatkowego miejsca na dysku, ale dużej fragmentacji. Wezmę to.

Następny

Jestem pewien, że patrzysz na moje testy powyżej i zastanawiasz się nad kilkoma rzeczami. Co najważniejsze, dlaczego stół jest kupą? Chciałem zbadać, co faktycznie dzieje się ze strukturą strony i liczbą stron bez indeksów, kluczy lub ograniczeń, które rozmywają szczegóły. Możesz również zastanawiać się, dlaczego ta zmiana była tak łatwa — w scenariuszu, w którym musisz zmienić kolumnę true IDENTITY, prawdopodobnie jest to również klastrowany klucz podstawowy i ma zależności kluczy obcych w innych tabelach. To zdecydowanie wprowadza pewne czkawki do tego procesu. Przyjrzymy się tym bliżej w następnym poście z tej serii.

[ Część 1 | Część 2 | Część 3 | Część 4 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Trace Flag 2389 i nowy estymator kardynalności

  2. Czy firma Intel jest skazana na zagładę w przestrzeni procesora serwera?

  3. Korzystanie z danych chronionych za pomocą Azure Key Vault z systemu Linux

  4. Błąd szacowania liczebności podzapytania

  5. Twój kompletny przewodnik po SQL Join:CROSS JOIN – część 3