Przykładowa baza danych dla In-Memory OLTP
Dotyczy:programu SQL Server
Azure SQL Database
Przegląd
W tej próbce przedstawiono funkcję In-Memory OLTP. Przedstawia tabele zoptymalizowane pod kątem pamięci oraz natywnie skompilowane procedury składowane, i może służyć do zademonstrowania korzyści wydajnościowych In-Memory OLTP.
Notatka
Aby wyświetlić ten temat dla programu SQL Server 2014 (12.x), zobacz Extensions to AdventureWorks to Demonstrate In-Memory OLTP.
Przykład migruje pięć tabel w bazie danych AdventureWorks2022
na zoptymalizowane pod kątem pamięci i zawiera przykładowe obciążenie przetwarzania zamówień sprzedażowych. Możesz użyć tego obciążenia demonstracyjnego, aby zobaczyć korzyści z wydajności korzystania z In-Memory OLTP na serwerze.
W opisie przykładu omówimy kompromisy dokonane podczas migracji tabel do In-Memory OLTP, aby uwzględnić funkcje, które nie są jeszcze (jeszcze) obsługiwane w przypadku tabel zoptymalizowanych pod kątem pamięci.
Dokumentacja tego przykładu jest ustrukturyzowana w następujący sposób:
Wymagania wstępne dotyczące instalowania próbki i uruchamiania demonstracji obciążenia.
Instrukcje dotyczące Instalowanie przykładu OLTP In-Memory opartego na bazie bazy danych AdventureWorks.
Opis przykładowych tabel i procedur — zawiera opisy tabel i procedur dodanych do
AdventureWorks2022
przez przykład In-Memory OLTP, a także zagadnienia dotyczące migrowania niektórych oryginalnych tabelAdventureWorks2022
do zoptymalizowania pod kątem pamięci.Instrukcje dotyczące wykonywania pomiarów wydajności przy użyciu demonstracyjnego obciążenia — zawiera instrukcje instalacji i uruchamiania narzędzia ostress, służącego do napędzania obciążenia, a także uruchamiania demonstracyjnego obciążenia.
Warunki wstępne
-
SQL Server 2016 (13.x)
Na potrzeby testowania wydajnościowego serwer ze specyfikacjami podobnymi do środowiska produkcyjnego. W przypadku tego konkretnego przykładu należy mieć co najmniej 16 GB pamięci dostępnej dla programu SQL Server. Aby uzyskać ogólne wytyczne dotyczące sprzętu dla In-Memory OLTP, zobacz następujący wpis w blogu: Zagadnienia sprzętowe dla In-Memory OLTP w SQL Server 2014
Instalowanie próbki OLTP In-Memory na podstawie bazy danych AdventureWorks
Wykonaj następujące kroki, aby zainstalować próbkę:
Pobierz
AdventureWorks2016_EXT.bak
iSQLServer2016Samples.zip
z: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks do folderu lokalnego, na przykładC:\Temp
.Przywróć kopię zapasową bazy danych przy użyciu programu Transact-SQL lub PROGRAMU SQL Server Management Studio:
Zidentyfikuj folder docelowy i nazwę pliku danych, na przykład
"h:\DATA\AdventureWorks2022_Data.mdf"
Zidentyfikuj folder docelowy i nazwę pliku dziennika, na przykład
"i:\DATA\AdventureWorks2022_log.ldf"
- Plik dziennika powinien zostać umieszczony na innym dysku niż plik danych, najlepiej w przypadku dysku o małym opóźnieniu, takim jak dysk SSD lub magazyn PCIe, w celu uzyskania maksymalnej wydajności.
Przykładowy skrypt języka T-SQL:
RESTORE DATABASE [AdventureWorks2022] FROM DISK = N'C:\temp\AdventureWorks2022.bak' WITH FILE = 1, MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf', MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf', MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod' GO
Aby wyświetlić przykładowe skrypty i obciążenie, rozpakuj plik SQLServer2016Samples.zip do folderu lokalnego. Aby uzyskać instrukcje dotyczące uruchamiania obciążenia, zapoznaj się z plikiem In-Memory OLTP\readme.txt.
Opis przykładowych tabel i procedur
Przykład tworzy nowe tabele dla produktów i zamówień sprzedaży na podstawie istniejących tabel w AdventureWorks2022
. Schemat nowych tabel jest podobny do istniejących tabel, z kilkoma różnicami, jak wyjaśniono poniżej.
Nowe tabele zoptymalizowane pod kątem pamięci zawierają sufiks "_inmem". Przykład zawiera również odpowiednie tabele zawierające sufiks "_ondisk" — te tabele mogą służyć do porównania jednej do jednego między wydajnością tabel zoptymalizowanych pod kątem pamięci i tabel opartych na dyskach w systemie.
Tabele zoptymalizowane pod względem zużycia pamięci, używane do porównania wydajności obciążeń, są w pełni trwałe i w pełni dziennikowane. Nie poświęcają trwałości ani niezawodności w celu uzyskania korzyści z wydajności.
Obciążeniem docelowym dla tego przykładu jest przetwarzanie zamówień sprzedaży, w którym rozważamy również informacje o produktach i rabatach. W tym celu użyjemy tabel SalesOrderHeader
, SalesOrderDetail
, Product
, SpecialOffer
i SpecialOfferProduct
.
Dwie nowe procedury składowane, Sales.usp_InsertSalesOrder_inmem
i Sales.usp_UpdateSalesOrderShipInfo_inmem
, są używane do wstawiania zamówień sprzedaży oraz aktualizowania informacji o wysyłce danego zamówienia sprzedaży.
Nowy schemat Demo
zawiera tabele pomocnicze i procedury składowane do wykonania demonstracyjnego obciążenia.
Przykład In-Memory OLTP dodaje następujące obiekty do AdventureWorks2022
:
Tabele dodane przez próbkę
Nowe tabele
Sales.SalesOrderHeader_inmem
- Informacje nagłówkowe dotyczące zamówień sprzedaży. Każde zamówienie sprzedaży ma jeden wiersz w tej tabeli.
Sales.SalesOrderDetail_inmem
- Szczegóły zamówień sprzedaży. Każda pozycja zamówienia sprzedaży ma jeden wiersz w tej tabeli.
Sales.SpecialOffer_inmem
- Informacje o ofertach specjalnych, w tym procent rabatu skojarzony z każdą ofertą specjalną.
Sales.SpecialOfferProduct_inmem
- Tabela referencyjna między ofertami specjalnymi i produktami. Każda oferta specjalna może zawierać zero lub więcej produktów, a każdy produkt może być opisywany w zero lub więcej ofert specjalnych.
Production.Product_inmem
- Informacje o produktach, w tym ich cenniku.
Demo.DemoSalesOrderDetailSeed
- Używane w demonstracyjnym obciążeniu roboczym do tworzenia przykładowych zleceń sprzedaży.
Odmiany tabel opartych na dyskach:
Sales.SalesOrderHeader_ondisk
Sales.SalesOrderDetail_ondisk
Sales.SpecialOffer_ondisk
Sales.SpecialOfferProduct_ondisk
Production.Product_ondisk
Różnice między oryginalnymi tabelami opartymi na dyskach i nowymi tabelami zoptymalizowanymi pod kątem pamięci
W większości przypadków nowe tabele wprowadzone w tym przykładzie używają tych samych kolumn i tych samych typów danych co oryginalne tabele. Istnieje jednak kilka różnic. Poniżej wymieniono różnice wraz z uzasadnieniem zmian.
Sales.SalesOrderHeader_inmem
Domyślne ograniczenia są obsługiwane dla tabel zoptymalizowanych pod kątem pamięci, a większość z nich została zmigrowana bez zmian. Jednak oryginalna tabela
Sales.SalesOrderHeader
zawiera dwa domyślne ograniczenia, które pobierają bieżącą datę, dla kolumnOrderDate
iModifiedDate
. W obciążeniu przetwarzania zamówień o wysokiej przepływności z dużą współbieżnością każdy zasób globalny może stać się punktem rywalizacji. Czas systemowy jest globalnym zasobem i zaobserwowaliśmy, że może stać się wąskim gardłem podczas uruchamiania obciążenia OLTP In-Memory, które wstawia zamówienia sprzedaży. Dzieje się tak zwłaszcza wtedy, gdy czas systemowy musi być pobierany dla wielu kolumn zarówno w nagłówku zamówienia sprzedaży, jak i w jego szczegółach. Problem został poruszony w tym przykładzie poprzez pobranie czasu systemowego tylko raz dla każdego wstawionego zamówienia sprzedaży i użycie tej samej wartości dla kolumn daty i godziny wSalesOrderHeader_inmem
iSalesOrderDetail_inmem
w procedurze składowanejSales.usp_InsertSalesOrder_inmem
.Aliasowe zdefiniowane przez użytkownika typy danych (UTD) — oryginalna tabela używa dwóch aliasów UTD, odpowiednio
dbo.OrderNumber
idbo.AccountNumber
, dla kolumnPurchaseOrderNumber
iAccountNumber
. Program SQL Server 2016 (13.x) nie obsługuje aliasu UDT dla tabel zoptymalizowanych pod kątem pamięci, dlatego nowe tabele używają odpowiednio typów danych systemowych nvarchar(25) i nvarchar(15).Kolumny akceptujące wartości null w kluczach indeksu — w oryginalnej tabeli kolumna
SalesPersonID
jest typu nullable, natomiast w nowych tabelach kolumna nie akceptuje wartości null i ma domyślne ograniczenie z wartością (-1). Jest to spowodowane tym, że indeksy w tabelach zoptymalizowanych pod kątem pamięci nie mogą mieć kolumn dopuszczanych do wartości null w kluczu indeksu; -1 jest zastępcą wartości NULL w tym przypadku.kolumny obliczone — obliczone kolumny
SalesOrderNumber
iTotalDue
zostaną pominięte, ponieważ program SQL Server 2016 (13.x) nie obsługuje kolumn obliczanych w tabelach zoptymalizowanych pod kątem pamięci. Nowy widokSales.vSalesOrderHeader_extended_inmem
odzwierciedla kolumnySalesOrderNumber
iTotalDue
. W związku z tym można użyć tego widoku, jeśli te kolumny są potrzebne.-
Dotyczy: SQL Server 2017 (14.x) CTP 1.1.
Począwszy od programu SQL Server 2017 (14.x) CTP 1.1, obliczone kolumny są obsługiwane w tabelach i indeksach zoptymalizowanych pod kątem pamięci.
-
Dotyczy: SQL Server 2017 (14.x) CTP 1.1.
ograniczenia klucza obcego są obsługiwane w przypadku tabel zoptymalizowanych pod kątem pamięci w programie SQL Server 2016 (13.x), ale tylko wtedy, gdy przywoływane tabele są również zoptymalizowane pod kątem pamięci. Klucze obce, które odwołują się do tabel migrowanych do zoptymalizowanych pod kątem pamięci, są przechowywane w migrowanych tabelach, podczas gdy inne klucze obce są pomijane. Ponadto
SalesOrderHeader_inmem
jest gorącą tabelą w przykładowym obciążeniu, a ograniczenia kluczy obcych wymagają dodatkowego przetwarzania dla wszystkich operacji DML, ponieważ wymagają wyszukiwań we wszystkich innych tabelach, do których odwołują się te ograniczenia. W związku z tym zakłada się, że aplikacja zapewnia integralność referencyjną dla tabeliSales.SalesOrderHeader_inmem
, a integralność referencyjna nie jest weryfikowana podczas wstawiania wierszy.rowguid — pominięto kolumnę rowguid. Chociaż funkcja uniqueidentifier jest obsługiwana w przypadku tabel zoptymalizowanych pod kątem pamięci, opcja ROWGUIDCOL nie jest obsługiwana w programie SQL Server 2016 (13.x). Kolumny tego rodzaju są zwykle używane albo do replikacji mieszanej, albo w tabelach posiadających kolumny typu filestream. Ten przykład nie zawiera żadnego z tych elementów.
Sprzedaż.SzczegółyZamówieniaSprzedaży
Domyślne ograniczenia — podobnie jak
SalesOrderHeader
, domyślne ograniczenie wymagające daty/godziny systemowej nie jest migrowane. Zamiast tego, procedura składowana wstawiająca zamówienia sprzedaży dba o to, aby przy pierwszym wstawieniu wprowadzana była bieżąca data/godzina systemu.kolumny obliczane — obliczona kolumna
LineTotal
nie została zmigrowana, ponieważ obliczone kolumny nie są obsługiwane dla tabel zoptymalizowanych pamięciowo w programie SQL Server 2016 (13.x). Aby uzyskać dostęp do tej kolumny, użyj widokuSales.vSalesOrderDetail_extended_inmem
.Rowguid — pominięto kolumnę
rowguid
. Aby uzyskać szczegółowe informacje, zobacz opis tabeliSalesOrderHeader
.
Production.Product
aliasy UDT — oryginalna tabela używa typu danych zdefiniowanego przez użytkownika
dbo.Flag
, który jest odpowiednikiem typu danych systemowych bit. Zmigrowana tabela używa zamiast tego typu danych bitowych.Rowguid — pominięto kolumnę
rowguid
. Aby uzyskać szczegółowe informacje, zobacz opis tabeliSalesOrderHeader
.
Sprzedaż.SpecjalnaOferta
-
Rowguid - pominięto kolumnę
rowguid
. Aby uzyskać szczegółowe informacje, zobacz opis tabeliSalesOrderHeader
.
Sprzedaż.OfertaSpecjalnaProdukt
-
rowguid - kolumna
rowguid
jest pominięta. Aby uzyskać szczegółowe informacje, zobacz opis tabeliSalesOrderHeader
.
Zagadnienia dotyczące indeksów w tabelach zoptymalizowanych pod kątem pamięci
Podstawowy indeks dla tabel zoptymalizowanych pod kątem pamięci to indeks NONCLUSTERED, który obsługuje wyszukiwanie bezpośrednie (wyszukiwanie indeksu w predykacie równości), skanowania zakresów (wyszukiwanie indeksu przy użyciu predykatu nierówności), pełne skanowanie indeksu oraz uporządkowane skanowania. Ponadto indeksy NONCLUSTERED obsługują wyszukiwanie w kolumnach wiodących klucza indeksu. W rzeczywistości indeksy NONCLUSTERED zoptymalizowane pod kątem pamięci obsługują wszystkie operacje obsługiwane przez indeksy NONCLUSTERED oparte na dyskach, przy czym jedynym wyjątkiem jest skanowanie wsteczne. Dlatego użycie indeksów NONCLUSTERED jest bezpiecznym wyborem.
Indeksy haszujące mogą służyć do dalszej optymalizacji obciążenia. Są one zoptymalizowane pod kątem wyszukiwań punktowych i wstawiania wierszy. Należy jednak wziąć pod uwagę, że nie obsługują skanowania zakresów, uporządkowanych skanów ani wyszukiwania w kolumnach indeksu kluczowego. W związku z tym należy zachować ostrożność podczas korzystania z tych indeksów. Ponadto należy określić bucket_count
w czasie tworzenia. Zwykle powinna być ustawiana między jedną a dwiema razy liczbą wartości klucza indeksu, ale przeszacowanie zwykle nie jest problemem.
Aby uzyskać więcej informacji:
- wytyczne dotyczące indeksu
- Wybieranie odpowiedniej liczby kubełków (bucket_count)
- indeksy dla tabel zoptymalizowanych pod kątem pamięci
Indeksy w zmigrowanych tabelach zostały dostosowane do obciążenia przetwarzania zamówień sprzedaży demonstracyjnej. Obciążenie opiera się na wstawianiu i wyszukiwaniu punktów w tabelach Sales.SalesOrderHeader_inmem
i Sales
.SalesOrderDetail_inmem
, a także opiera się na wyszukiwaniach punktów w kolumnach klucza podstawowego w tabelach Production.Product_inmem
i Sales.SpecialOffer_inmem
.
Sales.SalesOrderHeader_inmem
ma trzy indeksy skrótów dla wydajności oraz dlatego, że nie są potrzebne żadne uporządkowane ani zakresowe skanowania w ramach obciążenia.
Indeks HASH w dniu (
SalesOrderID
): bucket_count ma rozmiar 10 milionów (zaokrąglony do 16 milionów), ponieważ oczekiwana liczba zamówień sprzedaży wynosi 10 milionówIndeks skrótu (
SalesPersonID
): bucket_count wynosi 1 milion. Podany zestaw danych nie zawiera wielu sprzedawców. Ale ten duży bucket_count pozwala na przyszły wzrost. Dodatkowo, nie ponosisz kary za wydajność przy wyszukiwaniach punktowych, jeśli bucket_count jest zbyt duża.Indeks skrótu (
CustomerID
): bucket_count wynosi 1 milion. Podany zestaw danych nie ma wielu klientów, ale pozwala to na przyszły wzrost.
Sales.SalesOrderDetail_inmem
ma trzy indeksy haszujące, które zostały wybrane ze względu na wydajność, a także ponieważ dla danego obciążenia nie są potrzebne żadne uporządkowane lub zakresowe skanowania.
Indeks HASH w (
SalesOrderID
,SalesOrderDetailID
): jest to indeks klucza podstawowego, a wyszukiwania po (SalesOrderID
,SalesOrderDetailID
) będą rzadkie, ponieważ użycie indeksu skrótu dla klucza przyspiesza wstawianie wierszy. Rozmiar bucket_count wynosi 50 milionów (zaokrąglony do 67 milionów): oczekiwana liczba zamówień sprzedaży wynosi 10 milionów, a wielkość ta wynosi średnio pięć pozycji na zamówienieIndeks hash na (
SalesOrderID
): wyszukiwania według zamówień sprzedaży są częste, dlatego warto znaleźć wszystkie pozycje wiersza odpowiadające pojedynczemu zamówieniu. bucket_count ma rozmiar 10 milionów (zaokrąglony do 16 milionów), ponieważ oczekiwana liczba zamówień sprzedaży wynosi 10 milionówIndeks skrótu na (
ProductID
): "bucket_count" wynosi 1 milion. Podany zestaw danych nie zawiera dużo produktów, ale pozwala to na przyszły wzrost.
Production.Product_inmem
ma trzy indeksy
Indeks skrótu na (
ProductID
): wyszukiwania wProductID
są w ścieżce krytycznej dla obciążenia demonstracyjnego, dlatego jest to indeks skrótu.Indeks NONCLUSTERED na (
Name
): umożliwi to uporządkowane skanowanie nazw produktówIndeks NONCLUSTERED na (
ProductNumber
): umożliwi to uporządkowane skanowanie numerów produktów
Sales.SpecialOffer_inmem
ma jeden indeks HASH na (SpecialOfferID
): wyszukiwanie punktów ofert specjalnych znajduje się w krytycznej części obciążenia demonstracyjnego.
bucket_count
ma rozmiar wynoszący 1 milion, aby umożliwić przyszły wzrost.
Sales.SpecialOfferProduct_inmem
nie jest przywoływane w obciążeniu testowym, dlatego nie ma wyraźnej potrzeby używania indeksów mieszających na tej tabeli w celu zoptymalizowania obciążenia — indeksy na (SpecialOfferID
, ProductID
) i (ProductID
) są NIEZGRUPOWANE.
Zwróć uwagę, że w powyższych niektóre liczby zasobników są zbyt duże, ale nie dla liczby koszy dla indeksów na SalesOrderHeader_inmem
i SalesOrderDetail_inmem
: są one przystosowane jedynie do 10 milionów zamówień sprzedaży. Zostało to zrobione, aby umożliwić zainstalowanie przykładu w systemach o niskiej dostępności pamięci, chociaż w takich przypadkach obciążenie demonstracyjne zakończy się niepowodzeniem z braku pamięci. Jeśli chcesz skalować znacznie więcej niż 10 milionów zamówień sprzedaży, możesz odpowiednio zwiększyć liczbę zasobników.
Zagadnienia dotyczące wykorzystania pamięci
Wykorzystanie pamięci w przykładowej bazie danych, zarówno przed, jak i po uruchomieniu obciążenia demonstracyjnego, zostało omówione w sekcji wykorzystanie pamięci dla tabel zoptymalizowanych pod kątem pamięci.
Procedury składowane dodane przez przykład
Dwie kluczowe procedury składowane dotyczące dodawania zleceń sprzedaży i aktualizowania szczegółów wysyłki są następujące:
Sales.usp_InsertSalesOrder_inmem
Wprowadza nowe zamówienie sprzedaży do bazy danych i generuje numer
SalesOrderID
dla tego zamówienia sprzedaży. Jako parametry wejściowe przyjmuje dane nagłówka zamówienia sprzedaży, a także pozycje na zamówieniu.Parametr wyjściowy:
-
@SalesOrderID int —
SalesOrderID
dla zamówienia sprzedaży, które dopiero co zostało wstawione
-
@SalesOrderID int —
Parametry wejściowe (wymagane):
@DueDate datetime2
@CustomerID int
@BillToAddressID [int]
@ShipToAddressID [int]
@ShipMethodID [int]
@SalesOrderDetails
Sales.SalesOrderDetailType_inmem
— parametr o wartości tabeli (TVP), który zawiera elementy wiersza zamówienia
Parametry wejściowe (opcjonalnie):
@Status [tinyint]
@OnlineOrderFlag [bit]
@PurchaseOrderNumber [nvarchar](25)
@AccountNumber [nvarchar](15)
@SalesPersonID [int]
@TerritoryID [int]
@CreditCardID [int]
@CreditCardApprovalCode [varchar](15)
@CurrencyRateID [int]
@Comment nvarchar(128)
Sales.usp_UpdateSalesOrderShipInfo_inmem
Zaktualizuj informacje o wysyłki dla danego zamówienia sprzedaży. Spowoduje to również zaktualizowanie informacji o wysyłki dla wszystkich pozycji zamówienia sprzedaży.
Jest to procedura opakowująca dla natywnie skompilowanych procedur składowanych
Sales.usp_UpdateSalesOrderShipInfo_native
z logiką ponawiania, aby radzić sobie z nieoczekiwanymi potencjalnymi konfliktami z równoczesnymi transakcjami aktualizującymi to samo zamówienie. Aby uzyskać więcej informacji, zobacz logikę ponawiania prób.
Sales.usp_UpdateSalesOrderShipInfo_native
- Jest to natywnie skompilowana procedura składowana, która rzeczywiście przetwarza aktualizację informacji o wysyłce. Jest to przeznaczone do wywoływania z procedury składowanej typu otoka
Sales.usp_UpdateSalesOrderShipInfo_inmem
. Jeśli klient może radzić sobie z błędami i implementuje logikę ponawiania, możesz wywołać tę procedurę bezpośrednio, bez użycia procedury składowanej opakowania.
- Jest to natywnie skompilowana procedura składowana, która rzeczywiście przetwarza aktualizację informacji o wysyłce. Jest to przeznaczone do wywoływania z procedury składowanej typu otoka
Poniższa procedura składowana jest używana dla obciążenia demonstracyjnego.
Demo.usp_DemoReset
- Resetuje pokaz, opróżniając i ponownie wysyłając tabele
SalesOrderHeader
iSalesOrderDetail
.
- Resetuje pokaz, opróżniając i ponownie wysyłając tabele
Poniższe procedury składowane są używane do dodawania do i usuwania z tabel zoptymalizowanych pod kątem pamięci, jednocześnie zapewniając integralność domenową i referencyjną.
Production.usp_InsertProduct_inmem
Production.usp_DeleteProduct_inmem
Sales.usp_InsertSpecialOffer_inmem
Sales.usp_DeleteSpecialOffer_inmem
Sales.usp_InsertSpecialOfferProduct_inmem
Na koniec poniższa procedura składowana służy do weryfikowania domeny i integralności referencyjnej.
dbo.usp_ValidateIntegrity
Opcjonalny parametr: @object_id — identyfikator obiektu w celu zweryfikowania integralności dla
Ta procedura opiera się na tabelach
dbo.DomainIntegrity
,dbo.ReferentialIntegrity
idbo.UniqueIntegrity
dla reguł integralności, które należy zweryfikować — przykład wypełnia te tabele na podstawie kontroli, klucza obcego i unikatowych ograniczeń istniejących dla oryginalnych tabel w bazie danychAdventureWorks2022
.Opiera się on na procedurach pomocnika
dbo.usp_GenerateCKCheck
,dbo.usp_GenerateFKCheck
idbo.GenerateUQCheck
generowania języka T-SQL potrzebnego do przeprowadzania kontroli integralności.
Pomiary wydajności przy użyciu obciążenia pokazowego
Ostress to narzędzie wiersza polecenia opracowane przez zespół pomocy technicznej programu MICROSOFT CSS SQL Server. To narzędzie może służyć do równoległego wykonywania zapytań lub uruchamiania procedur składowanych. Można skonfigurować liczbę wątków do równoległego uruchamiania danej instrukcji języka T-SQL i określić, ile razy instrukcja ma być wykonywana w tym wątku; ostress uruchomi wątki i wykona instrukcję na wszystkich wątkach równolegle. Po zakończeniu wykonywania wszystkich wątków program ostress zgłosi czas potrzebny do ich zakończenia.
Instalowanie narzędzia ostress
Program Ostress jest instalowany jako część narzędzi języka RML (Report Markup Language); nie ma autonomicznej instalacji narzędzia ostress.
Kroki instalacji:
Pobierz i uruchom pakiet instalacyjny x64 dla narzędzi RML z następującej strony: Pobierz narzędzia RML dla programu SQL Server
Jeśli istnieje okno dialogowe z informacją, że niektóre pliki są używane, wybierz pozycję "Kontynuuj"
Uruchamianie narzędzia ostress
Ostress jest uruchamiany z wiersza polecenia. Najwygodniej jest uruchamiać narzędzie z poziomu wiersza polecenia języka RML, który jest instalowany jako część narzędzi RML.
Aby otworzyć wiersz polecenia RML, wykonaj następujące instrukcje:
W systemie Windows otwórz menu Start, wybierając klucz systemu Windows i wpisz rml
. Wybierz pozycję "RML Cmd Prompt", która będzie znajdować się na liście wyników wyszukiwania.
Upewnij się, że wiersz polecenia znajduje się w folderze instalacyjnym RML Utilities.
Opcje wiersza polecenia dla narzędzia ostress można zobaczyć podczas zwykłego uruchamiania ostress.exe bez żadnych opcji wiersza polecenia. Główne opcje, które należy wziąć pod uwagę podczas uruchamiania narzędzia ostress z tym przykładem, to:
-S nazwa wystąpienia programu Microsoft SQL Server do nawiązania połączenia
-E używa uwierzytelniania systemu Windows do nawiązywania połączenia (ustawienie domyślne); Jeśli używasz uwierzytelniania programu SQL Server, użyj opcji -U i -P, aby określić odpowiednio nazwę użytkownika i hasło
-d nazwa bazy danych, na potrzeby tego przykładu
AdventureWorks2022
-Q instrukcja języka T-SQL do wykonania
-n liczba połączeń przetwarzających każdy plik wejściowy/zapytanie
-r określa liczbę iteracji dla każdego połączenia w celu wykonania każdego pliku wejściowego/zapytania.
Pokazowe obciążenie
Główną procedurą składowaną używaną w obciążeniu demonstracyjnym jest Sales.usp_InsertSalesOrder_inmem/ondisk
. Skrypt poniżej konstruuje parametr o typie tabelarycznym (TVP) z przykładowymi danymi i wywołuje procedurę wstawiania zamówienia sprzedaży z pięcioma pozycjami.
Narzędzie ostress służy do równoległego wykonywania wywołań procedury składowanej, aby symulować jednoczesne wstawianie zamówień sprzedaży przez klientów.
Zresetuj pokaz po każdym uruchomieniu obciążenia wykonującym Demo.usp_DemoReset
. Ta procedura usuwa wiersze z tabel zoptymalizowanych pod kątem pamięci, przycina tabele oparte na dyskach i wykonuje punkt kontrolny bazy danych.
Następujący skrypt jest wykonywany współbieżnie w celu symulowania obciążenia przetwarzania zamówień sprzedaży:
DECLARE
@i int = 0,
@od Sales.SalesOrderDetailType_inmem,
@SalesOrderID int,
@DueDate datetime2 = sysdatetime(),
@CustomerID int = rand() * 8000,
@BillToAddressID int = rand() * 10000,
@ShipToAddressID int = rand() * 10000,
@ShipMethodID int = (rand() * 5) + 1;
INSERT INTO @od
SELECT OrderQty, ProductID, SpecialOfferID
FROM Demo.DemoSalesOrderDetailSeed
WHERE OrderID= cast((rand()*106) + 1 as int);
WHILE (@i < 20)
BEGIN;
EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od;
SET @i += 1
END
W przypadku tego skryptu każde skonstruowane zamówienie próbki jest wstawiane 20 razy, przez 20 procedur przechowywanych wykonywanych w pętli WHILE. Pętla służy do uwzględniania faktu, że baza danych jest używana do konstruowania przykładowej kolejności. W typowych środowiskach produkcyjnych aplikacja warstwy pośredniej skonstruuje zamówienie sprzedaży, które ma zostać wstawione.
Powyższy skrypt wstawia zamówienia sprzedaży do tabel zoptymalizowanych pod kątem pamięci. Skrypt do wstawiania zamówień sprzedaży do tabel przechowywanych na dysku jest tworzony poprzez zamianę dwóch wystąpień "_inmem" na "_ondisk".
Użyjemy narzędzia ostress do wykonywania skryptów przy użyciu kilku współbieżnych połączeń. Użyjemy parametru "-n", aby kontrolować liczbę połączeń, a parametr "r", aby kontrolować, ile razy skrypt jest wykonywany na każdym połączeniu.
Uruchamianie zadania
Aby przetestować w dużej skali, wprowadzamy 10 milionów zamówień sprzedaży przy użyciu 100 połączeń. Ten test działa rozsądnie na skromnym serwerze (na przykład 8 rdzeni fizycznych, 16 rdzeni logicznych) i podstawowej pamięci SSD dla logu. Jeśli test nie działa dobrze na sprzęcie, zapoznaj się z sekcją Rozwiązywanie problemów z powolnym uruchamianiem testów. Jeśli chcesz zmniejszyć poziom obciążenia dla tego testu, zmniejsz liczbę połączeń, zmieniając parametr "-n". Na przykład aby zmniejszyć liczbę połączeń do 40, zmień parametr "-n100" na "-n40".
Jako miara wydajności obciążenia używamy czasu, który upłynął zgodnie z raportem ostress.exe po uruchomieniu obciążenia.
Następujące instrukcje i pomiary dotyczą obciążenia, które wstawia 10 milionów zamówień sprzedaży. Aby uzyskać instrukcje dotyczące uruchamiania zredukowanego obciążenia związanego z wstawieniem 1 miliona zamówień sprzedaży, zobacz temat "In-Memory OLTP\readme.txt' that is part of the SQLServer2016Samples.zip archive.
Tabele zoptymalizowane pod kątem pamięci
Zaczniemy od uruchomienia obciążenia w tabelach zoptymalizowanych pod kątem pamięci. Następujące polecenie otwiera 100 wątków, z których każdy wykonuje 5000 iteracji. Każda iteracja wstawia 20 zamówień sprzedaży w oddzielnych transakcjach. Istnieje 20 wstawek na iterację, aby zrekompensować fakt, że baza danych jest używana do generowania danych do wstawienia. Daje to łącznie 20 * 5000 * 100 = 10 000 000 wstawień zamówień sprzedaży.
Otwórz wiersz polecenia RML i wykonaj następujące polecenie:
Wybierz przycisk Kopiuj, aby skopiować polecenie i wkleić je do wiersza polecenia RML Utilities.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Na jednym serwerze testowym z całkowitą liczbą 8 rdzeni fizycznych (16 logicznych) zajęło to 2 minuty i 5 sekund. Na drugim serwerze testowym z 24 rdzeniami fizycznymi (48 logicznymi) zajęło to 1 minutę i 0 sekund.
Obserwuj użycie procesora CPU, gdy obciążenie jest uruchomione, na przykład przy użyciu menedżera zadań. Zobaczysz, że użycie CPU jest zbliżone do 100%. Jeśli tak nie jest, może występować wąskie gardło I/O dziennika, sprawdź również Rozwiązywanie problemów z powolnym uruchamianiem testów.
Tabele oparte na dyskach
Następujące polecenie spowoduje uruchomienie obciążenia w tabelach opartych na dyskach. Wykonanie tego obciążenia może zająć trochę czasu, co jest w dużej mierze spowodowane rywalizacją o zatrzaśanie w systemie. Tabele zoptymalizowane pod kątem pamięci są wolne od zatrzaśnięć, dlatego nie występuje u nich ten problem.
Otwórz wiersz polecenia RML i wykonaj następujące polecenie:
Wybierz przycisk Kopiuj, aby skopiować polecenie i wklej go w wierszu polecenia RML Utilities.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Na jednym serwerze testowym z całkowitą liczbą 8 rdzeni fizycznych (16 rdzeni logicznych) zajęło to 41 minut i 25 sekund. Na drugim serwerze testowym z 24 rdzeniami fizycznymi (48 logicznymi) zajęło to 52 minuty i 16 sekund.
Głównym czynnikiem różnicy wydajności między tabelami zoptymalizowanym pod kątem pamięci a tabelami opartymi na dyskach w tym teście jest fakt, że w przypadku korzystania z tabel opartych na dyskach program SQL Server nie może w pełni wykorzystać procesora CPU. Przyczyną jest rywalizacja wynikająca z zatrzasków: transakcje współbieżne próbują zapisać na tej samej stronie danych; zatrzaski są stosowane, aby zapewnić, że tylko jedna transakcja naraz może zapisywać na stronie. Silnik OLTP In-Memory jest bez blokad, a wiersze danych nie są zorganizowane w stronach. W związku z tym równoczesne transakcje nie blokują wstawiania nawzajem, co umożliwia SQL Server pełne wykorzystanie CPU.
Możesz obserwować wykorzystanie procesora CPU, gdy obciążenie jest uruchomione, na przykład przy użyciu menedżera zadań. W przypadku tabel opartych na dyskach użycie procesora CPU jest dalekie od 100%. W przypadku konfiguracji testowej z 16 procesorami logicznymi, wykorzystanie będzie się utrzymywać w okolicach 24%.
Opcjonalnie możesz wyświetlić liczbę oczekiwań na zatrzask na sekundę przy użyciu narzędzia Performance Monitor, z licznikiem wydajności \SQL Server:Latches\Latch Waits/sec
.
Resetowanie demo
Aby zresetować demonstrację, otwórz wiersz polecenia RML i wykonaj następujące polecenie:
ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"
W zależności od sprzętu może to potrwać kilka minut.
Zalecamy zresetowanie po każdym uruchomieniu demonstracji. Ponieważ to obciążenie jest tylko do wstawiania, każde uruchomienie będzie zużywać więcej pamięci, a tym samym resetowanie jest wymagane, aby zapobiec wyczerpaniu pamięci. Ilość pamięci zużywanej po uruchomieniu została omówiona w sekcji wykorzystanie pamięci po uruchomieniu obciążenia.
Rozwiązywanie problemów z powolnymi testami
Wyniki testów zwykle różnią się w zależności od sprzętu, a także poziomu współbieżności używanej w przebiegu testu. Kilka rzeczy, na które należy zwrócić uwagę, jeśli wyniki nie są takie, jak oczekiwano.
Liczba współbieżnych transakcji: w przypadku uruchamiania obciążenia w jednym wątku wydajność z In-Memory OLTP prawdopodobnie będzie mniejsza niż 2X. Rywalizacja o opóźnienie jest tylko istotnym problemem, jeśli występuje wysoki poziom współbieżności.
Niska liczba rdzeni dostępnych dla programu SQL Server: oznacza to, że w systemie będzie niski poziom współbieżności, ponieważ może istnieć tylko tyle współbieżnych transakcji, ile rdzeni jest dostępnych dla programu SQL.
- Objaw: jeśli wykorzystanie procesora CPU jest wysokie, gdy uruchamiane jest obciążenie na tabelach opartych na dyskach, oznacza to, że nie ma dużo konkurencji, co wskazuje na brak współbieżności.
Szybkość dysku dziennika: jeśli dysk dziennika nie może nadążyć za poziomem przepustowości transakcji w systemie, obciążenie stanie się wąskim gardłem dla operacji we/wy dziennika. Chociaż rejestrowanie jest bardziej wydajne w przypadku In-Memory OLTP, jeśli I/O dziennika jest wąskim gardłem, potencjalny wzrost wydajności jest ograniczony.
- Objaw: jeśli wykorzystanie procesora CPU nie zbliża się do 100% lub wykazuje duże wahania podczas uruchamiania obciążenia na tabelach zoptymalizowanych dla pamięci, może istnieć wąskie gardło operacji I/O dziennika. Można to potwierdzić, otwierając monitor zasobów i sprawdzając długość kolejki dla dysku dziennika.
Wykorzystanie pamięci i miejsca na dysku w przykładzie
Poniżej opisano, czego można oczekiwać w odniesieniu do wykorzystania pamięci i miejsca na dysku dla przykładowej bazy danych. Pokazujemy również wyniki widoczne na serwerze testowym z 16 rdzeniami logicznymi.
Wykorzystanie pamięci dla tabel zoptymalizowanych pod kątem pamięci
Ogólne wykorzystanie bazy danych
Poniższe zapytanie może służyć do uzyskania całkowitego wykorzystania pamięci dla In-Memory OLTP w systemie.
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
Migawka po utworzeniu bazy danych:
typ | nazwa | strony_MB |
---|---|---|
MEMORYCLERK_XTP | Domyślny | 94 |
MEMORYCLERK_XTP | DB_ID_5 | 877 |
MEMORYCLERK_XTP | Domyślny | 0 |
MEMORYCLERK_XTP | Domyślny | 0 |
Domyślne urzędy pamięci zawierają struktury pamięci dla całego systemu i są stosunkowo małe. Mechanizm zarządzania pamięcią dla bazy danych użytkownika, w tym przypadku bazy danych o identyfikatorze 5 (database_id
może się różnić w twoim przypadku), zużywa około 900 MB.
Wykorzystanie pamięci na tabelę
Następujące zapytanie może służyć do przechodzenia do szczegółów wykorzystania pamięci poszczególnych tabel i ich indeksów:
SELECT object_name(t.object_id) AS [Table Name]
, memory_allocated_for_table_kb
, memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t
ON dms.object_id=t.object_id
WHERE t.type='U';
W poniższej tabeli przedstawiono wyniki tego zapytania dotyczącego nowej instalacji przykładu:
Nazwa tabeli | memory_allocated_for_table_kb | pamięć_przydzielona_na_indeksy_kb |
---|---|---|
SpecialOfferProduct_inmem | 64 | 3840 |
DemoSalesOrderHeaderSeed | 1984 | 5504 |
SalesOrderDetail_inmem | 15316 | 663552 |
ZamówienieDemoDetaleSeed | 64 | 10432 |
SpecialOffer_inmem | 3 | 8192 |
SalesOrderHeader_inmem | 7168 | 147456 |
Product_inmem | 124 | 12352 |
Jak widać, tabele są dość małe: SalesOrderHeader_inmem
wynosi około 7 MB, a SalesOrderDetail_inmem
ma około 15 MB rozmiaru.
Co uderza, to rozmiar pamięci przydzielonej do indeksów w porównaniu z rozmiarem danych tabeli. Wynika to z faktu, że indeksy skrótów w przykładzie mają wstępnie określony rozmiar dla większej ilości danych. Należy pamiętać, że indeksy skrótów mają stały rozmiar, dlatego ich rozmiar nie będzie rosnąć wraz z rozmiarem danych w tabeli.
Wykorzystanie pamięci po uruchomieniu obciążenia
Po wprowadzeniu 10 milionów zamówień sprzedaży, wykorzystanie pamięci przedstawia się następująco:
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
typ | nazwa | strony_MB |
---|---|---|
MEMORYCLERK_XTP | Domyślny | 146 |
MEMORYCLERK_XTP | DB_ID_5 | 7374 |
MEMORYCLERK_XTP | Domyślny | 0 |
MEMORYCLERK_XTP | Domyślny | 0 |
Jak widać, program SQL Server używa nieco poniżej 8 GB dla tabel i indeksów zoptymalizowanych pod kątem pamięci w przykładowej bazie danych.
Przyjrzyj się szczegółowemu użyciu pamięci na tabelę po jednym przykładowym uruchomieniu:
SELECT object_name(t.object_id) AS [Table Name]
, memory_allocated_for_table_kb
, memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t
ON dms.object_id=t.object_id
WHERE t.type='U'
Nazwa tabeli | pamięć_przydzielona_dla_tabeli_kb | pamięć_przydzielona_na_indeksy_kb |
---|---|---|
SzczegółyZamówienia_inmem | 5113761 | 663552 |
DemoSprzedażZamówienieSzczegółySeed | 64 | 10368 |
OfertaSpecjalna_inmem | 2 | 8192 |
SalesOrderHeader_inmem | 1575679 | 147456 |
Product_inmem | 111 | 12032 |
ProduktOfertySpecjalnej_wPamięci | 64 | 3712 |
DemoSalesOrderHeaderSeed | 1984 | 5504 |
Widzimy łącznie około 6,5 GB danych. Zwróć uwagę, że rozmiar indeksów w tabeli SalesOrderHeader_inmem
i SalesOrderDetail_inmem
jest taki sam jak rozmiar indeksów przed wstawieniem zamówień sprzedaży. Rozmiar indeksu nie zmienił się, ponieważ obie tabele używają indeksów skrótów, a indeksy skrótów są statyczne.
Po zresetowaniu pokazu
Procedura składowana Demo.usp_DemoReset
może służyć do resetowania demonstracji. Usuwa dane w tabelach SalesOrderHeader_inmem
i SalesOrderDetail_inmem
oraz ponownie rozsiewuje dane z oryginalnych tabel SalesOrderHeader
i SalesOrderDetail
.
Teraz, mimo że wiersze w tabelach zostały usunięte, nie oznacza to natychmiastowego odzyskania pamięci. Program SQL Server odzyskuje pamięć z usuniętych wierszy w tabelach zoptymalizowanych pod kątem pamięci w tle zgodnie z potrzebami. Zobaczysz, że natychmiast po zresetowaniu demonstracji, gdy w systemie nie ma obciążenia transakcyjnego, pamięć po usuniętych wierszach nie została jeszcze odzyskana.
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
typ | nazwa | pages_MB |
---|---|---|
MEMORYCLERK_XTP | Domyślny | 2261 |
MEMORYCLERK_XTP | DB_ID_5 | 7396 |
MEMORYCLERK_XTP | Domyślny | 0 |
MEMORYCLERK_XTP | Domyślny | 0 |
Jest to oczekiwane: pamięć zostanie odzyskana po uruchomieniu obciążenia transakcyjnego.
Jeśli uruchomisz drugi przebieg obciążenia demonstracyjnego, początkowo zobaczysz spadek wykorzystania pamięci, ponieważ wcześniej usunięte wiersze zostaną wyczyszczone. W pewnym momencie rozmiar pamięci zostanie ponownie zwiększony do momentu zakończenia obciążenia. Po wstawieniu 10 milionów wierszy, po zresetowaniu demonstracji, wykorzystanie pamięci będzie bardzo podobne do wykorzystania po pierwszym uruchomieniu. Na przykład:
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
typ | nazwa | strony_MB |
---|---|---|
MEMORYCLERK_XTP | Domyślny | 1863 |
MEMORYCLERK_XTP | DB_ID_5 | 7390 |
MEMORYCLERK_XTP | Domyślny | 0 |
MEMORYCLERK_XTP | Domyślny | 0 |
Wykorzystanie dysku dla tabel zoptymalizowanych pod kątem pamięci
Ogólny rozmiar dysku dla plików punktu kontrolnego bazy danych w danym momencie można znaleźć przy użyciu zapytania:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX';
Stan początkowy
Podczas początkowego tworzenia przykładowej grupy plików i przykładowych tabel zoptymalizowanych pod kątem pamięci wiele plików punktów kontrolnych jest wstępnie tworzonych, a system rozpoczyna wypełnianie plików — liczba wstępnie utworzonych plików punktów kontrolnych zależy od liczby procesorów logicznych w systemie. Ponieważ próbka jest początkowo bardzo mała, wstępnie utworzone pliki będą w większości puste po początkowym utworzeniu.
Poniższy kod przedstawia początkowy rozmiar dysku dla próbki na maszynie z 16 procesorami logicznymi:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX';
Rozmiar dysku w MB |
---|
2312 |
Jak widać, istnieje duża rozbieżność między rozmiarem na dysku plików punktu kontrolnego, czyli 2,3 GB, a rzeczywistym rozmiarem danych, który jest bliższy 30 MB.
Aby dokładniej przyjrzeć się, skąd pochodzi wykorzystanie miejsca na dysku, możesz użyć następującego zapytania. Rozmiar dysku zwróconego przez to zapytanie jest przybliżony dla plików ze stanem 5 (WYMAGANE DO TWORZENIA KOPII ZAPASOWEJ/HA), 6 (W PRZEJŚCIU DO TOMBSTONE) lub 7 (TOMBSTONE).
SELECT state_desc
, file_type_desc
, COUNT(*) AS [count]
, SUM(CASE
WHEN state = 5 AND file_type=0 THEN 128*1024*1024
WHEN state = 5 AND file_type=1 THEN 8*1024*1024
WHEN state IN (6,7) THEN 68*1024*1024
ELSE file_size_in_bytes
END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
W przypadku początkowego stanu próbki wynik będzie wyglądać podobnie do serwera z 16 procesorami logicznymi:
opis_stanu | opis_typu_pliku | liczba / liczyć (context-dependent) | rozmiar na dysku w MB |
---|---|---|---|
WCZEŚNIEJ UTWORZONE | DANE | 16 | 2048 |
WSTĘPNIE UTWORZONE | DELTA | 16 | 128 |
W BUDOWIE | DANE | 1 | 128 |
W BUDOWIE | DELTA | 1 | 8 |
Jak widać, większość miejsca jest używana przez wstępnie utworzone dane i pliki różnicowe. Program SQL Server wstępnie utworzył jedną parę plików (danych, różnicowych) na procesor logiczny. Ponadto pliki danych mają wstępnie ustalony rozmiar 128 MB, a pliki różnicowe 8 MB, aby zwiększyć wydajność wstawiania danych do tych plików.
Rzeczywiste dane w tabelach zoptymalizowanych pod kątem pamięci są w jednym pliku danych.
Po uruchomieniu zadania
Po uruchomieniu jednego testu, który wstawia 10 milionów zamówień sprzedaży, ogólny rozmiar dysku wygląda mniej więcej tak (dla 16-rdzeniowego serwera testowego):
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX';
Rozmiar dysku w MB |
---|
8828 |
Rozmiar na dysku wynosi około 9 GB, co jest zbliżone do rozmiaru danych w pamięci.
Dokładniej przyjrzyj się rozmiarom plików punktu kontrolnego w różnych stanach:
SELECT state_desc
, file_type_desc
, COUNT(*) AS [count]
, SUM(CASE
WHEN state = 5 AND file_type=0 THEN 128*1024*1024
WHEN state = 5 AND file_type=1 THEN 8*1024*1024
WHEN state IN (6,7) THEN 68*1024*1024
ELSE file_size_in_bytes
END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
opis_stanu | opis_typu_pliku | liczyć | rozmiar na dysku MB |
---|---|---|---|
WSTĘPNIE UTWORZONE | DANE | 16 | 2048 |
WSTĘPNIE UTWORZONE | DELTA | 16 | 128 |
W BUDOWIE | DANE | 1 | 128 |
W BUDOWIE | DELTA | 1 | 8 |
Nadal mamy 16 par wstępnie utworzonych plików, gotowych do użycia, gdy punkty kontrolne zostają zamknięte.
Istnieje jedna para w trakcie budowy, która jest używana do momentu zamknięcia bieżącego punktu kontrolnego. Wraz z aktywnymi plikami punktu kontrolnego daje to około 6,5 GB wykorzystania dysku dla 6,5 GB danych w pamięci. Pamiętaj, że indeksy nie są utrwalane na dysku, a więc ogólny rozmiar dysku jest mniejszy niż rozmiar pamięci w tym przypadku.
Po zresetowaniu pokazu
Po zresetowaniu demonstracji miejsce na dysku nie odzyskuje się natychmiast, jeśli w systemie nie ma obciążenia transakcyjnego i nie ma punktów kontrolnych bazy danych. Aby pliki punktów kontrolnych były przenoszone przez różne etapy i ostatecznie zostały odrzucone, musi się odbyć określona liczba punktów kontrolnych i zdarzeń przycinania dziennika, aby zainicjować scalanie plików punktu kontrolnego oraz proces zbierania nieużywanych danych. Będą one wykonywane automatycznie, jeśli w systemie występuje obciążenie transakcyjne (i regularnie wykonujesz kopie zapasowe dzienników, w przypadku korzystania z modelu PEŁNEGO odzyskiwania), ale nie wtedy, gdy system jest bezczynny, jak w scenariuszu demonstracyjnym.
W przykładzie po zresetowaniu pokazu może zostać wyświetlony komunikat podobny do następującego:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX';
Rozmiar dysku w MB |
---|
11839 |
Przy wielkości prawie 12 GB, jest to znacznie więcej niż 9 GB, które mieliśmy przed zresetowaniem dema. Dzieje się tak dlatego, że niektóre scalania plików punktu kontrolnego zostały uruchomione, ale niektóre obiekty docelowe scalania nie zostały jeszcze zainstalowane, a niektóre pliki źródłowe scalania nie zostały jeszcze wyczyszczone, jak widać na poniższej ilustracji:
SELECT state_desc
, file_type_desc
, COUNT(*) AS [count]
, SUM(CASE
WHEN state = 5 AND file_type=0 THEN 128*1024*1024
WHEN state = 5 AND file_type=1 THEN 8*1024*1024
WHEN state IN (6,7) THEN 68*1024*1024
ELSE file_size_in_bytes
END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
state_desc | opis_typu_pliku | liczba / liczyć | rozmiar na dysku MB |
---|---|---|---|
WCZEŚNIEJ UTWORZONE | DANE | 16 | 2048 |
PRZEDTWORZONE | DELTA | 16 | 128 |
AKTYWNY | DANE | 38 | 5152 |
AKTYWNY | DELTA | 38 | 1331 |
CEL SCALANIA | DANE | 7 | 896 |
Docelowy element scalający | DELTA | 7 | 56 |
ŹRÓDŁO SCALONE | DANE | 13 | 1772 |
ŹRÓDŁO SCALONE | DELTA | 13 | 455 |
Obiekty docelowe scalania są instalowane i scalane źródło są czyszczone, ponieważ w systemie występuje aktywność transakcyjna.
Proponowane Ulepszenia: Po drugim uruchomieniu obciążenia demonstracyjnego, po zresetowaniu demonstracji i wstawieniu 10 milionów zamówień sprzedaży, zauważysz, że pliki utworzone podczas pierwszego uruchomienia obciążenia zostały wyczyszczone. Jeśli uruchomisz powyższe zapytanie kilka razy, gdy obciążenie jest uruchomione, zobaczysz, że pliki punktu kontrolnego przechodzą przez różne etapy.
Po drugim uruchomieniu obciążenia, które wstawia 10 milionów zamówień sprzedaży, zobaczysz wykorzystanie dysku bardzo podobne do tego po pierwszym uruchomieniu, choć niekoniecznie identyczne, ponieważ system charakteryzuje się dynamicznością. Na przykład:
SELECT state_desc
, file_type_desc
, COUNT(*) AS [count]
, SUM(CASE
WHEN state = 5 AND file_type=0 THEN 128*1024*1024
WHEN state = 5 AND file_type=1 THEN 8*1024*1024
WHEN state IN (6,7) THEN 68*1024*1024
ELSE file_size_in_bytes
END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
state_desc | opis rodzaju pliku | liczyć | rozmiar na dysku MB |
---|---|---|---|
WCZEŚNIEJ UTWORZONE | DANE | 16 | 2048 |
WSTĘPNIE UTWORZONE | DELTA | 16 | 128 |
W BUDOWIE | DANE | 2 | 268 |
W BUDOWIE | DELTA | 2 | 16 |
AKTYWNY | DANE | 41 | 5608 |
AKTYWNY | DELTA | 41 | 328 |
W tym przypadku istnieją dwie pary plików punktu kontrolnego w stanie "w budowie", co oznacza, że wiele par plików zostało przeniesionych do stanu "w budowie", prawdopodobnie ze względu na wysoki poziom współbieżności w obciążeniu. Wiele współbieżnych wątków wymagało jednocześnie nowej pary plików, a tym samym przeniesiono parę z "wstępnie utworzonej" do "w budowie".
Następne kroki
- In-Memory OLTP (optymalizacjaIn-Memory) — omówienie i scenariusze użycia
- Utwórz grupę plików zoptymalizowaną pod kątem pamięci: grupa plików zoptymalizowana pod kątem pamięci
- Skrypt do włączenia In-Memory OLTP oraz ustawienia zalecanych opcji