Udostępnij za pośrednictwem


Przykładowa baza danych dla In-Memory OLTP

Dotyczy:programu SQL ServerAzure 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:

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ę:

  1. Pobierz AdventureWorks2016_EXT.bak i SQLServer2016Samples.zip z: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks do folderu lokalnego, na przykład C:\Temp.

  2. Przywróć kopię zapasową bazy danych przy użyciu programu Transact-SQL lub PROGRAMU SQL Server Management Studio:

    1. Zidentyfikuj folder docelowy i nazwę pliku danych, na przykład

      "h:\DATA\AdventureWorks2022_Data.mdf"

    2. Zidentyfikuj folder docelowy i nazwę pliku dziennika, na przykład

      "i:\DATA\AdventureWorks2022_log.ldf"

      1. 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  
    
  3. 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, SpecialOfferi 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 kolumn OrderDate i ModifiedDate. 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 w SalesOrderHeader_inmem i SalesOrderDetail_inmemw procedurze składowanej Sales.usp_InsertSalesOrder_inmem.

  • Aliasowe zdefiniowane przez użytkownika typy danych (UTD) — oryginalna tabela używa dwóch aliasów UTD, odpowiednio dbo.OrderNumber i dbo.AccountNumber, dla kolumn PurchaseOrderNumber i AccountNumber. 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 i TotalDue zostaną pominięte, ponieważ program SQL Server 2016 (13.x) nie obsługuje kolumn obliczanych w tabelach zoptymalizowanych pod kątem pamięci. Nowy widok Sales.vSalesOrderHeader_extended_inmem odzwierciedla kolumny SalesOrderNumber i TotalDue. 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.
  • 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 tabeli Sales.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 widoku Sales.vSalesOrderDetail_extended_inmem.

  • Rowguid — pominięto kolumnę rowguid. Aby uzyskać szczegółowe informacje, zobacz opis tabeli SalesOrderHeader.

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 tabeli SalesOrderHeader.

Sprzedaż.SpecjalnaOferta

  • Rowguid - pominięto kolumnę rowguid. Aby uzyskać szczegółowe informacje, zobacz opis tabeli SalesOrderHeader.

Sprzedaż.OfertaSpecjalnaProdukt

  • rowguid - kolumna rowguid jest pominięta. Aby uzyskać szczegółowe informacje, zobacz opis tabeli SalesOrderHeader.

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:

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ów

  • Indeks 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ówienie

  • Indeks 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ów

  • Indeks 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 w ProductID 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ów

  • Indeks 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
    • 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.

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 i SalesOrderDetail.

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.

  1. dbo.usp_ValidateIntegrity

    • Opcjonalny parametr: @object_id — identyfikator obiektu w celu zweryfikowania integralności dla

    • Ta procedura opiera się na tabelach dbo.DomainIntegrity, dbo.ReferentialIntegrityi dbo.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 danych AdventureWorks2022.

    • Opiera się on na procedurach pomocnika dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKChecki dbo.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:

  1. Pobierz i uruchom pakiet instalacyjny x64 dla narzędzi RML z następującej strony: Pobierz narzędzia RML dla programu SQL Server

  2. 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_inmemoraz 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