Po zadaniu tego pytania porównując sekwencyjne i niesekwencyjne identyfikatory GUID, próbowałem porównać wydajność INSERT na 1) tabeli z kluczem podstawowym GUID inicjowanym sekwencyjnie newsequentialid()
, oraz 2) tabeli z kluczem podstawowym INT inicjowanym sekwencyjnie identity(1,1)
. Spodziewałbym się, że ta ostatnia będzie najszybsza z powodu mniejszej szerokości liczb całkowitych, a także wydaje się łatwiejsze wygenerowanie sekwencyjnej liczby całkowitej niż sekwencyjny identyfikator GUID. Ale ku mojemu zdziwieniu, WSTAWKI na stole z kluczem całkowitym były znacznie wolniejsze niż sekwencyjna tabela GUID.
To pokazuje średni czas użycia (ms) dla testów:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
Czy ktoś może to wyjaśnić?
Zastosowano następujący eksperyment:
SET NOCOUNT ON
CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000
WHILE (@BatchCounter <= 20)
BEGIN
BEGIN TRAN
DECLARE @LocalCounter INT = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @LocalCounter = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestInt (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @BatchCounter +=1
COMMIT
END
DBCC showcontig ('TestGuid2') WITH tableresults
DBCC showcontig ('TestInt') WITH tableresults
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber
DROP TABLE TestGuid2
DROP TABLE TestInt
AKTUALIZACJA: Modyfikując skrypt w celu wykonywania wstawień w oparciu o tabelę TEMP, jak w przykładach autorstwa Phila Sandlera, Mitcha Wheata i Martina poniżej, stwierdzam również, że TOŻSAMOŚĆ jest szybsza, jak powinna. Ale to nie jest konwencjonalny sposób wstawiania wierszy i nadal nie rozumiem, dlaczego eksperyment zawiódł na początku: nawet jeśli pominę GETDATE () w moim oryginalnym przykładzie, TOŻSAMOŚĆ () jest nadal znacznie wolniejsza. Wydaje się więc, że jedynym sposobem na osiągnięcie lepszej wydajności IDENTITY () NEWSEQUENTIALID () jest przygotowanie wierszy do wstawienia do tabeli tymczasowej i wykonanie wielu wstawień jako wsadowego wsadu przy użyciu tej tabeli temp. Podsumowując, nie sądzę, że znaleźliśmy wyjaśnienie tego zjawiska, a TOŻSAMOŚĆ () wydaje się być wolniejsza w przypadku większości praktycznych zastosowań. Czy ktoś może to wyjaśnić?
źródło
INT IDENTITY
IDENTITY
nie wymaga blokady stołu. Pod względem koncepcyjnym widziałem, że możesz oczekiwać, że zajmie MAX (id) + 1, ale w rzeczywistości kolejna wartość jest przechowywana. Powinno to faktycznie być szybsze niż znalezienie następnego identyfikatora GUID.Odpowiedzi:
Zmodyfikowałem kod @Phil Sandler, aby usunąć efekt wywołania GETDATE () (mogą występować efekty / przerwania sprzętowe ??), i ustawiłem wiersze tej samej długości.
[Od czasu SQL Server 2000 pojawiło się kilka artykułów dotyczących problemów z timerem i timerów o wysokiej rozdzielczości, więc chciałem zminimalizować ten efekt.]
W prostym modelu odzyskiwania z danymi i plikiem dziennika, zarówno o rozmiarach przekraczających wymagane, oto czasy (w sekundach): (Zaktualizowano o nowe wyniki na podstawie dokładnego kodu poniżej)
Zastosowany kod:
Po przeczytaniu śledztwa @ Martina w obu przypadkach ponownie uruchomiłem sugerowany TOP (@num), tj
a oto wyniki pomiaru czasu:
Nie byłem w stanie uzyskać rzeczywistego planu wykonania, ponieważ zapytanie nigdy nie powróciło! Wygląda na to, że błąd jest prawdopodobny. (Z systemem Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64))
źródło
SORT
operatora dla GUID?NEWSEQUENTIALID
. Sprawi, że indeks będzie głębszy, zużyje o 20% więcej stron danych w przypadku PO, a gwarantuje się, że będzie się zwiększał aż do ponownego uruchomienia komputera, co ma wiele wad w stosunku doidentity
. Wydaje się, że w tym przypadku plan zapytań dodaje kolejny niepotrzebny!Na świeżej bazie danych w prostym modelu odzyskiwania z plikiem danych o wielkości 1 GB i plikiem dziennika o wielkości 3 GB (laptop, oba pliki na tym samym dysku) i interwałem odzyskiwania ustawionym na 100 minut (aby uniknąć wypaczenia wyników przez punkt kontrolny) Widzę wyniki podobne do ciebie w jednym rzędzie
inserts
.Przetestowałem trzy przypadki: Dla każdego przypadku wykonałem 20 partii wstawiając 100 000 wierszy indywidualnie do poniższych tabel. Pełne skrypty można znaleźć w historii zmian tej odpowiedzi .
W przypadku trzeciej tabeli test wstawił wiersze o
Id
wartości rosnącej, ale została ona obliczona samodzielnie przez zwiększenie wartości zmiennej w pętli.Uśrednianie czasu 20 partii dało następujące wyniki.
Wniosek
Zdecydowanie wydaje się, że to narzut związany z
identity
procesem tworzenia odpowiedzialny za wyniki. W przypadku samodzielnie obliczanej inkrementującej liczby całkowitej wyniki są znacznie bardziej zgodne z tym, czego można się spodziewać, biorąc pod uwagę tylko koszt IO.Po umieszczeniu opisanego powyżej kodu wstawiania w procedurach przechowywanych i przejrzeniu
sys.dm_exec_procedure_stats
daje on następujące wynikiTak więc w tych wynikach
total_worker_time
jest o około 30% wyższy. To reprezentujeWygląda więc po prostu tak, jakby kod generujący
IDENTITY
wartość był bardziej obciążający procesor niż ten, który generujeNEWSEQUENTIALID()
(Różnica między 2 liczbami wynosi 10231308, co wynosi średnio około 5µs na wkładkę.) I że dla tej definicji tabeli ten stały koszt procesora był wystarczająco wysoki, aby przewyższyć dodatkowe logiczne odczyty i zapisy powstałe z powodu większej szerokości klucza. (Uwaga: Itzik Ben Gan zrobił tutaj podobne testy i znalazł karę 2µs za wkładkę)Dlaczego więc wymaga
IDENTITY
więcej procesoraUuidCreateSequential
?Wierzę, że wyjaśniono to w tym artykule . Dla każdej dziesiątej
identity
wygenerowanej wartości SQL Server musi zapisać zmianę w tabelach systemowych na dyskuCo z wkładkami MultiRow?
Kiedy wstawiono 100 000 wierszy w jednym stwierdzeniu, zauważyłem, że różnica zniknęła, co może być nadal niewielką korzyścią dla
GUID
sprawy, ale nie jest tak bliskie rezultatu. Średnia dla 20 partii w moim teście wyniosłaPowodem, dla którego nie ma widocznej kary w kodzie Phila i pierwszym zestawie wyników Mitcha jest to, że tak się złożyło, że kod, który użyłem do wykonania wielowierszowej wstawki
SELECT TOP (@NumRows)
. Uniemożliwiło to optymalizatorowi prawidłowe oszacowanie liczby wierszy, które zostaną wstawione.Wydaje się to być korzystne, ponieważ istnieje pewien punkt krytyczny, w którym doda on dodatkową operację sortowania dla (podobno sekwencyjnych!)
GUID
S.Ta operacja sortowania nie jest wymagana z tekstu objaśniającego w BOL .
Wydawało mi się więc, że błąd lub brak optymalizacji polega na tym, że SQL Server nie rozpoznaje, że dane wyjściowe skalaru obliczeniowego będą już wstępnie posortowane, tak jak najwyraźniej już dla
identity
kolumny. ( Edytuj Zgłosiłem to, a problem niepotrzebnego sortowania został rozwiązany w Denali )źródło
Całkiem proste: z GUID tańsze jest wygenerowanie następnego numeru w wierszu niż w przypadku TOŻSAMOŚCI (bieżąca wartość GUID nie musi być przechowywana, TOŻSAMOŚĆ musi być). Dotyczy to nawet NEWSEQUENTIALGUID.
Możesz sprawić, by test był bardziej uczciwy i użyć SEKWENCERA z dużym DACHEM - który jest tańszy niż TOŻSAMOŚĆ.
Ale jak mówi MR, istnieją pewne główne zalety GUID. W rzeczywistości są one DUŻO bardziej skalowalne niż kolumny TOŻSAMOŚCI (ale tylko wtedy, gdy NIE są one sekwencyjne).
Zobacz: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
źródło
IDENTITY
. stąd skargi tutajFascynuje mnie tego rodzaju pytanie. Dlaczego musiałeś to opublikować w piątek wieczorem? :)
Myślę, że nawet jeśli twój test ma WYŁĄCZNIE mierzyć wydajność WSTAWIANIA, możesz (prawdopodobnie) wprowadziłeś wiele czynników, które mogą wprowadzać w błąd (zapętlenie, długotrwała transakcja itp.)
Nie jestem do końca przekonany, że moja wersja coś udowadnia, ale tożsamość działa lepiej niż zawarte w niej identyfikatory GUID (3,2 sekundy w porównaniu z 6,8 sekundy na komputerze domowym):
źródło
Uruchomiłem twój przykładowy skrypt kilka razy, wprowadzając kilka drobnych poprawek do liczby i wielkości partii (i bardzo dziękuję za jej dostarczenie).
Najpierw powiem, że mierzysz tylko jeden aspekt wydajności klawiszy -
INSERT
szybkość. Tak więc, chyba że zajmujesz się tylko szybkim wprowadzaniem danych do tabel, zwierzę to ma znacznie więcej.Moje ustalenia były ogólnie podobne do twoich. Chciałbym jednak wspomnieć, że wariancja
INSERT
prędkości pomiędzyGUID
iIDENTITY
(int) jest nieco większa w przypadkuGUID
niż w przypadkuIDENTITY
- może +/- 10% między biegami. Użyte partie zaIDENTITY
każdym razem różniły się mniej niż 2–3%.Warto również zauważyć, że moje pole testowe jest wyraźnie słabsze niż twoje, więc musiałem użyć mniejszej liczby wierszy.
źródło
Odniosę się do innej konwekcji dotyczącej stackoverflow dla tego samego tematu - https://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid
Wiem tylko, że sekwencyjne identyfikatory GUID polegają na tym, że użycie indeksu jest lepsze ze względu na bardzo niewielki ruch liści, a tym samym ograniczenie wyszukiwania HD. Pomyślałbym, że z tego powodu wstawki również byłyby szybsze, ponieważ nie musiałby rozdzielać kluczy na dużej liczbie stron.
Moje osobiste doświadczenie polega na tym, że gdy wdrażasz bazę danych o dużym ruchu, lepiej jest używać identyfikatorów GUID, ponieważ sprawia, że jest ona znacznie bardziej skalowalna w celu integracji z innymi systemami. Dotyczy to w szczególności replikacji i limitów int / bigint ... nie dlatego, że zabraknie bigintów, ale w końcu to zrobisz i cofniesz się.
źródło