Udostępnij za pośrednictwem


Szacowanie wymagań dotyczących pamięci dla tabel Memory-Optimized

Dotyczy:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Tabele zoptymalizowane pod kątem pamięci wymagają wystarczającej ilości pamięci, aby zachować wszystkie wiersze i indeksy w pamięci. Ponieważ pamięć jest zasobem skończonym, ważne jest, aby zrozumieć użycie pamięci w systemie i zarządzać nim. Tematy w tej sekcji obejmują typowe scenariusze użycia pamięci i zarządzania.

Niezależnie od tego, czy tworzysz nową tabelę zoptymalizowaną pod kątem pamięci, czy migrujesz istniejącą tabelę opartą na dyskach do tabeli In-Memory zoptymalizowanej pod kątem pamięci OLTP, ważne jest, aby mieć uzasadnione oszacowanie potrzeb związanych z pamięcią każdej tabeli, dzięki czemu można aprowizować serwer z wystarczającą ilością pamięci. W tej sekcji opisano sposób szacowania ilości pamięci potrzebnej do przechowywania danych dla tabeli zoptymalizowanej pod kątem pamięci.

Jeśli rozważasz migrację z tabel opartych na dyskach do tabel zoptymalizowanych pod kątem pamięci, zanim przejdziesz do tego tematu, zobacz temat Określanie, czy tabela lub procedura składowana powinna zostać przeniesiona do In-Memory olTP, aby uzyskać wskazówki, które tabele najlepiej migrować. Wszystkie tematy w Migrowanie do In-Memory OLTP zawierają wskazówki dotyczące migracji z tabel opartych na dyskach do tabel zoptymalizowanych pod kątem pamięci.

Podstawowe wskazówki dotyczące szacowania wymagań dotyczących pamięci

Począwszy od programu SQL Server 2016 (13.x), nie ma limitu rozmiaru tabel zoptymalizowanych pod kątem pamięci, chociaż tabele muszą zmieścić się w pamięci. W programie SQL Server 2014 (12.x) obsługiwany rozmiar danych wynosi 256 GB dla tabel SCHEMA_AND_DATA.

Rozmiar tabeli zoptymalizowanej pod kątem pamięci odpowiada rozmiarowi danych oraz pewne obciążenie nagłówków wierszy. Podczas migracji tabeli opartej na dysku do zoptymalizowanej pod kątem pamięci rozmiar tabeli zoptymalizowanej pod kątem pamięci będzie w przybliżeniu odpowiadać rozmiarowi indeksu klastrowanego lub sterty oryginalnej tabeli opartej na dyskach.

Indeksy w tabelach zoptymalizowanych pod kątem pamięci są zwykle mniejsze niż indeksy nieklastrowane w tabelach opartych na dyskach. Rozmiar indeksów nieklastrowanych jest w przybliżeniu [primary key size] * [row count]. Rozmiar indeksów skrótu jest [bucket count] * 8 bytes.

Jeśli istnieje aktywne obciążenie, wymagana jest dodatkowa pamięć do uwzględnienia przechowywania wersji wierszy i różnych operacji. Ilość pamięci potrzebnej w praktyce zależy od obciążenia, ale aby zapewnić bezpieczeństwo, zaleca się rozpoczęcie od dwóch razy oczekiwanych rozmiarów tabel i indeksów zoptymalizowanych pod kątem pamięci oraz obserwowania wymagań dotyczących pamięci w praktyce. Obciążenie związane z wersjonowaniem wierszy zawsze zależy od charakterystyki obciążenia — szczególnie transakcje długotrwałe zwiększają to obciążenie. W przypadku większości obciążeń korzystających z większych baz danych (np. >100 GB) obciążenie zwykle jest ograniczone (25% lub mniej).

Szczegółowe obliczenia wymagań dotyczących pamięci

Przykładowa tabela zoptymalizowana pod kątem pamięci

Rozważmy następujący schemat tabeli zoptymalizowany pod kątem pamięci:

CREATE TABLE t_hk
(  
  col1 int NOT NULL  PRIMARY KEY NONCLUSTERED,  

  col2 int NOT NULL  INDEX t1c2_index   
      HASH WITH (bucket_count = 5000000),  

  col3 int NOT NULL  INDEX t1c3_index   
      HASH WITH (bucket_count = 5000000),  

  col4 int NOT NULL  INDEX t1c4_index   
      HASH WITH (bucket_count = 5000000),  

  col5 int NOT NULL  INDEX t1c5_index NONCLUSTERED,  

  col6 char (50) NOT NULL,  
  col7 char (50) NOT NULL,   
  col8 char (30) NOT NULL,   
  col9 char (50) NOT NULL  

)   WITH (memory_optimized = on)  ;
GO  

Korzystając z tego schematu, określimy minimalną ilość pamięci wymaganą dla tej tabeli zoptymalizowanej pod kątem pamięci.

Pamięć dla tabeli

Wiersz tabeli zoptymalizowany pod kątem pamięci składa się z trzech części:

  • znaczniki czasu
    Nagłówka wiersza/znaczniki czasu = 24 bajty.

  • wskaźniki indeksu
    Dla każdego indeksu skrótu w tabeli każdy wiersz ma wskaźnik adresu 8-bajtowego do następnego wiersza w indeksie. Ponieważ istnieją cztery indeksy, każdy wiersz przydziela 32 bajty dla wskaźników indeksu (wskaźnik 8 bajtów dla każdego indeksu).

  • data
    Rozmiar części danych wiersza jest określany przez sumowanie rozmiaru typu dla każdej kolumny danych. W naszej tabeli mamy pięć liczb całkowitych 4-bajtowych, trzy 50-bajtowe kolumny znaków i 30-bajtową kolumnę znaków. Dlatego część danych każdego wiersza to 4 + 4 + 4 + 4 + 4 + 4 + 50 + 50 + 30 + 50 lub 200 bajtów.

Poniżej przedstawiono obliczenia rozmiaru dla 5000 000 wierszy (5 milionów) w tabeli zoptymalizowanej pod kątem pamięci. Łączna ilość pamięci używanej przez wiersze danych jest szacowana w następujący sposób:

Pamięć dotycząca wierszy tabeli

Z powyższych obliczeń rozmiar każdego wiersza w tabeli zoptymalizowanej pod kątem pamięci wynosi 24 + 32 + 200 lub 256 bajtów. Ponieważ mamy 5 milionów wierszy, tabela będzie zużywać 5000 000 * 256 bajtów lub 1280 000 000 bajtów — około 1,28 GB.

Pamięć dla indeksów

Pamięć dla każdego indeksu skrótu

Każdy indeks skrótu jest tablicą skrótów 8-bajtowych wskaźników adresowych. Rozmiar tablicy jest najlepiej określany przez liczbę unikatowych wartości indeksu dla tego indeksu — np. liczba unikatowych wartości Col2 jest dobrym punktem wyjścia dla rozmiaru tablicy dla t1c2_index. Tablica skrótów, która jest zbyt duża, marnuje pamięć. Tablica skrótów, która jest zbyt mała, spowalnia wydajność, ponieważ istnieje zbyt wiele kolizji według wartości indeksu, które mają skrót do tego samego indeksu.

Indeksy hashowe umożliwiają bardzo szybkie zapytania o równość, takie jak:

SELECT * FROM t_hk  
   WHERE Col2 = 3;

Indeksy nieklastrowane są szybsze w przypadku wyszukiwania zakresów, takich jak:

SELECT * FROM t_hk  
   WHERE Col2 >= 3;

Jeśli migrujesz tabelę opartą na dyskach, możesz użyć poniższej metody, aby określić liczbę unikatowych wartości dla t1c2_index indeksu.

SELECT COUNT(DISTINCT [Col2])  
  FROM t_hk;

Jeśli tworzysz nową tabelę, musisz oszacować rozmiar tablicy lub zebrać dane z testów przed wdrożeniem.

Aby uzyskać informacje na temat sposobu działania indeksów skrótów w tabelach zoptymalizowanych pod kątem pamięci OLTP In-Memory, zobacz Indeksy Skrótów.

Ustawianie rozmiaru tablicy indeksów skrótów

Rozmiar tablicy skrótów jest ustawiany przez (bucket_count= value), gdzie value jest wartością całkowitą większą niż zero. Jeśli value nie jest potęgą 2, rzeczywista wartość bucket_count jest zaokrąglana do najbliższej wyższej potęgi 2. W naszej przykładowej tabeli (bucket_count = 5000000), ponieważ 5 000 000 nie jest potęgą 2, rzeczywista liczba zasobników zaokrągla się do 8 388 608 (2^23). Należy użyć tej liczby, a nie 5000 000 podczas obliczania pamięci wymaganej przez tablicę skrótów.

W związku z tym w naszym przykładzie pamięć wymagana dla każdej tablicy skrótów to:

8,388,608 * 8 = 2^23 * 8 = 2^23 * 2^3 = 2^26 = 67,108,864 lub około 64 MB.

Ponieważ mamy trzy indeksy skrótów, pamięć wymagana dla indeksów skrótu wynosi 3 * 64 MB = 192 MB.

Pamięć dla indeksów nieklastrowanych

Indeksy nieklastrowane są implementowane jako drzewa Bw z węzłami wewnętrznymi zawierającymi wartość indeksu i wskaźniki do kolejnych węzłów. Węzły liściowe zawierają wartość indeksu i wskaźnik do wiersza tabeli w pamięci.

W przeciwieństwie do indeksów skrótów indeksy nieklastrowane nie mają stałego rozmiaru zasobnika. Indeks rośnie i zmniejsza się dynamicznie wraz z danymi.

Pamięć wymagana przez indeksy nieklastrowane można obliczyć w następujący sposób:

  • pamięć przydzielona do węzłów niebędących liścieniowymi
    W przypadku typowej konfiguracji pamięć przydzielona do węzłów innych niż liścia jest niewielką częścią ogólnej pamięci pobranej przez indeks. Jest to tak małe, że można je bezpiecznie zignorować.

  • pamięć dla węzłów liści
    Węzły liścia mają jeden wiersz dla każdego unikatowego klucza w tabeli, który wskazuje wiersze danych za pomocą tego unikatowego klucza. Jeśli masz wiele wierszy z tym samym kluczem (tj. masz nieunikalny indeks nieklastrowany), w węźle liścia indeksu znajduje się tylko jeden wiersz, który wskazuje na jeden z wierszy, a pozostałe wiersze są połączone ze sobą. W związku z tym łączna ilość wymaganej pamięci może być przybliżona przez:

    • pamięćDlaIndeksuNieklastrowego = (rozmiarWskaźnika + suma(rozmiaryTypówDanychKolumnKluczowych)) * wierszeZUunikalnymiKluczami

Indeksy nieklastrowane najlepiej nadają się do wyszukiwania zakresów, co jest przykładem następującego zapytania:

SELECT * FROM t_hk  
   WHERE c2 > 5;  

Pamięć na potrzeby przechowywania wersji wierszy

Aby uniknąć blokad, In-Memory OLTP używa optymistycznej współbieżności podczas aktualizowania lub usuwania wierszy. Oznacza to, że po zaktualizowaniu wiersza zostanie utworzona inna wersja wiersza. Ponadto usunięcia są logiczne — istniejący wiersz jest oznaczony jako usunięty, ale nie jest usuwany natychmiast. System przechowuje stare wersje wierszy (w tym usunięte wiersze) do momentu zakończenia wykonywania wszystkich transakcji, które mogą korzystać z wersji.

Ponieważ w każdej chwili może istnieć wiele więcej wierszy w pamięci oczekujących na zwolnienie pamięci przez cykl odzyskiwania pamięci, musisz mieć wystarczającą ilość pamięci, aby pomieścić te inne wiersze.

Liczbę dodatkowych wierszy można oszacować, obliczając maksymalną liczbę aktualizacji wierszy i usuwania na sekundę, a następnie mnożąc je przez liczbę sekund najdłuższej transakcji (co najmniej 1).

Ta wartość jest następnie mnożona przez rozmiar wiersza, aby uzyskać liczbę bajtów potrzebnych do przechowywania wersji wierszy.

rowVersions = durationOfLongestTransactionInSeconds * peakNumberOfRowUpdatesOrDeletesPerSecond

Zapotrzebowanie na nieaktualne wiersze jest następnie szacowane przez pomnożenie liczby nieaktualnych wierszy według rozmiaru wiersza tabeli zoptymalizowanego pod kątem pamięci (zobacz Pamięć dla tabeli powyżej).

memoryForRowVersions = rowVersions * rowSize

Pamięć dla zmiennych tabeli

Pamięć używana dla zmiennej tabeli jest zwalniana tylko wtedy, gdy zmienna tabeli wykracza poza zakres. Usunięte wiersze, w tym wiersze usunięte w ramach aktualizacji, ze zmiennej tabeli nie podlegają wyrzucaniu elementów bezużytecznych. Żadna pamięć nie jest zwalniana, dopóki zmienna tabeli nie opuści zakresu.

Zmienne tabeli zdefiniowane w dużej partii SQL, w przeciwieństwie do zakresu procedury, które są używane w wielu transakcjach, mogą zużywać dużo pamięci. Ponieważ nie są one zbierane, usunięte wiersze w zmiennej tabeli mogą zużywać dużo pamięci i obniżyć wydajność, ponieważ operacje odczytu muszą skanować obok usuniętych wierszy.

Pamięć na potrzeby wzrostu

Powyższe obliczenia szacują zapotrzebowanie na pamięć dla tabeli w jej obecnym stanie. Oprócz tej pamięci należy oszacować wzrost tabeli i zapewnić wystarczającą ilość pamięci, aby uwzględnić ten wzrost. Na przykład, jeśli przewidujesz wzrost o 10%, musisz pomnożyć wyniki z powyższych przez 1,1, aby uzyskać łączną ilość pamięci potrzebną do tabeli.

Zobacz też

Migrowanie do In-Memory OLTP