Mam tabelę z 64-metrowymi wierszami zajmującymi 4,3 GB na dysku dla danych.
Każdy wiersz ma około 30 bajtów kolumn liczb całkowitych oraz zmienną NVARCHAR(255)
kolumnę dla tekstu.
Dodałem kolumnę NULLABLE o typie danych Datetimeoffset(0)
.
Następnie zaktualizowałem tę kolumnę dla każdego wiersza i upewniłem się, że wszystkie nowe wstawki umieszczają wartość w tej kolumnie.
Kiedy nie było żadnych NULL wpisów, uruchomiłem to polecenie, aby moje nowe pole było obowiązkowe:
ALTER TABLE tblCheckResult
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL
Rezultatem był OGROMNY wzrost wielkości dziennika transakcji - z 6 GB do ponad 36 GB, aż zabrakło miejsca!
Czy ktoś ma pojęcie, co u licha robi SQL Server 2008 R2, aby to proste polecenie spowodowało tak ogromny wzrost?
sql-server
sql-server-2008-r2
transaction-log
alter-table
null
PapillonUK
źródło
źródło
NOT NULL
kolumny z wartością domyślną jako operacji metadanych. Zobacz także „Dodawanie kolumn NOT NULL jako operacji online” w dokumentacji .Odpowiedzi:
Po zmianie kolumny na NOT NULL SQL Server musi dotykać każdej strony, nawet jeśli nie ma wartości NULL. W zależności od współczynnika wypełnienia może to prowadzić do wielu podziałów stron. Oczywiście każda dotknięta strona musi zostać zarejestrowana i podejrzewam, że ze względu na podziały w przypadku wielu stron konieczne może być zarejestrowanie dwóch zmian. Ponieważ wszystko dzieje się w jednym przejściu, dziennik musi uwzględniać wszystkie zmiany, aby po anulowaniu wiedział dokładnie, co należy cofnąć.
Przykład. Prosty stół:
Teraz spójrzmy na szczegóły strony. Najpierw musimy dowiedzieć się, z jaką stroną i identyfikatorem DB_ID mamy do czynienia. W moim przypadku utworzyłem bazę danych o nazwie
foo
, a zdarzeniem DB_ID było 5.Dane wyjściowe wskazują, że byłem zainteresowany stroną 159 (jedynym wierszem w
DBCC IND
danych wyjściowych zPageType = 1
).Teraz przyjrzyjmy się wybranym szczegółom strony, gdy przechodzimy przez scenariusz PO.
Teraz nie mam na to wszystkich odpowiedzi, ponieważ nie jestem facetem od głębokich chorób wewnętrznych. Ale jasne jest, że - chociaż zarówno operacja aktualizacji, jak i dodanie ograniczenia NOT NULL niezaprzeczalnie zapisują się na stronie - ta ostatnia robi to w zupełnie inny sposób. Wygląda na to, że faktycznie zmienia strukturę rekordu, zamiast po prostu bawić się bitami, zamieniając kolumnę null na kolumnę, która nie ma wartości null. Dlaczego tak się dzieje, nie jestem do końca pewien - wydaje mi się, że to dobre pytanie dla zespołu silnika pamięci masowej . Wierzę, że SQL Server 2012 radzi sobie z niektórymi z tych scenariuszy o wiele lepiej, FWIW - ale muszę jeszcze przeprowadzić wyczerpujące testy.
źródło
Podczas wykonywania polecenia
Wydaje się, że jest to zaimplementowane jako operacja dodawania kolumny, aktualizacji, upuszczania kolumny.
sys.sysrscols
reprezentujący nową kolumnę.status
Bit128
jest ustawiony wskazuje kolumnę nie pozwalaNULL
ssys.sysrscols
.rscolid
Zaktualizowana do dużej liczby całkowitej istatus
ustawiony bit 2 na wskazany upuszczony)sys.sysrscols
nowej kolumny jest zmieniany, aby nadać jejrscolid
poprzednią kolumnę.Operacja, która może powodować wiele rejestrowania, dotyczy
UPDATE
wszystkich wierszy w tabeli, ale nie oznacza to, że zawsze tak się stanie. Jeśli obrazy „przed” i „po” wiersza są identyczne, będzie to traktowane jako aktualizacja nie aktualizująca i do tej pory nie będzie logowane z moich testów.Wyjaśnienie, dlaczego otrzymujesz dużo rejestrowania, będzie zależeć od tego, dlaczego dokładnie wersje wiersza „przed” i „po” nie są takie same.
W przypadku kolumn o zmiennej długości przechowywanych w
FixedVar
formacie stwierdziłem, że ustawienieNOT NULL
zawsze powoduje zmianę w wierszu, który należy zarejestrować. Liczba kolumn i liczba kolumn o zmiennej długości są zwiększane, a nowa kolumna jest dodawana na końcu sekcji o zmiennej długości, powielając dane.datetimeoffset(0)
ma jednak ustaloną długość, a dla kolumn o stałej długości przechowywanych wFixedVar
formacie zarówno stare, jak i nowe kolumny wydają się mieć tę samą szczelinę w części danych o stałej długości wiersza i ponieważ oba mają tę samą długość i wartość „przed” oraz wersje „po” wiersza są takie same . Można to zobaczyć w odpowiedzi @ Aarona. Obie wersje wiersza przed i poALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
sąTo nie jest zalogowane.
Logicznie z mojego opisu zdarzeń wiersz powinien być tutaj inny, ponieważ
02
należy zwiększyć liczbę kolumn,03
ale w praktyce taka zmiana nie ma miejsca.Oto niektóre możliwe powody, dla których może to wystąpić w kolumnie o stałej długości
SPARSE
wówczas nowa kolumna byłaby przechowywana w innej części wiersza niż oryginał, powodując, że obrazy przed i po wierszu będą inne.ALTER TABLE
operacja została zaimplementowana jako zmiana tylko metadanych i nie została jeszcze zastosowana do wiersza. Na przykład, jeśli dodano nową zerowalną kolumnę o zmiennej długości, wówczas jest ona pierwotnie stosowana jako zmiana tylko metadanych i jest faktycznie zapisywana do wierszy, gdy są one następnie aktualizowane (zapis, który faktycznie występuje w tej ostatniej instancji, to tylko aktualizacje sekcja liczby kolumn iNULL_BITMAP
jakoNULL
varchar
kolumna na końcu wiersza nie zajmuje miejsca)źródło
Ten sam problem napotkałem w przypadku tabeli mającej 200 000 000 wierszy. Początkowo dodałem kolumnę nullable, następnie zaktualizowałem wszystkie wiersze, a na koniec zmieniłem kolumnę
NOT NULL
za pomocąALTER TABLE ALTER COLUMN
instrukcji. Spowodowało to dwie ogromne transakcje niewiarygodnie wysadzające plik dziennika (wzrost o 170 GB).Najszybszy sposób, jaki znalazłem, to:
Dodaj kolumnę, używając wartości domyślnej
Usuń domyślne ograniczenie, używając dynamicznego SQL, ponieważ ograniczenie nie było wcześniej nazywane:
Czas wykonania skrócił się z> 30 minut do 10 minut, w tym replikacja zmian za pomocą replikacji transakcyjnej. Korzystam z instalacji programu SQL Server 2008 (SP2).
źródło
Przeprowadziłem następujący test:
Uważam, że ma to związek z zarezerwowanym miejscem w dzienniku na wypadek wycofania transakcji. Zajrzyj do funkcji fn_dblog w kolumnie „Log Reserve” dla wiersza LOP_BEGIN_XACT i zobacz, ile miejsca próbuje zarezerwować.
źródło
select * FROM fn_dblog(null, null) where AllocUnitName='dbo.tblCheckResult' AND Operation = 'LOP_MODIFY_ROW'
, zobaczysz aktualizacje 10000 wierszy.Zachowanie to jest inne w SQL Server 2012. Zobacz http://rusanu.com/2011/07/13/online-non-null-with-values-column-add-in-sql-server-11/
Liczba rekordów dziennika wygenerowanych dla SQL Server 2008 R2 i niższych wersji będzie znacznie wyższa niż liczba rekordów dziennika dla SQL Server 2012.
źródło
NOT NULL
powoduje rejestrowanie. Zmiana w 2012 r. Dotyczy dodania nowejNOT NULL
kolumny z domyślną.