Udostępnij za pośrednictwem


Rozmiar tabeli i wiersza w tabelach zoptymalizowanych pod kątem pamięci

Dotyczy:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Przed programem SQL Server 2016 (13.x) rozmiar danych w wierszu tabeli zoptymalizowanej pod kątem pamięci nie może być dłuższy niż 8060 bajtów. Jednak począwszy od programu SQL Server 2016 (13.x) i usługi Azure SQL Database, można utworzyć tabelę zoptymalizowaną pod kątem pamięci z wieloma dużymi kolumnami (na przykład wiele kolumn varbinary(8000)) i loB (czyli varbinary(max), varchar(max)i nvarchar(max)) i wykonywać operacje na nich przy użyciu natywnie skompilowanych modułów Transact-SQL (T-SQL) i typów tabel.

Kolumny, które nie mieszczą się w limicie rozmiaru wiersza 8060 bajtów, są umieszczane poza wierszem w oddzielnej tabeli wewnętrznej. Każda kolumna poza wierszem ma odpowiadającą tabelę wewnętrzną, która z kolei ma pojedynczy indeks nieklastrowany. Aby uzyskać szczegółowe informacje o tych tabelach wewnętrznych używanych do przechowywania kolumn poza wierszem, zobacz sys.memory_optimized_tables_internal_attributes.

Istnieją pewne scenariusze, w których warto obliczyć rozmiar wiersza i tabeli:

  • Ile pamięci używa tabela.

    • Nie można dokładnie obliczyć ilości pamięci używanej przez tabelę. Wiele czynników wpływa na ilość używanej pamięci. Czynniki takie jak alokacja pamięci na podstawie strony, lokalność, buforowanie i dopełnienie. Ponadto wiele wersji wierszy, które są związane z aktywnymi transakcjami lub czekają na zbieranie nieużywanej pamięci.

    • Minimalny rozmiar wymagany dla danych i indeksów w tabeli jest podawany przez obliczenie dla <table size>, omówionego w dalszej części tego artykułu.

    • Obliczanie użycia pamięci jest najlepszym przybliżeniem i zaleca się uwzględnienie planowania pojemności w planach wdrożenia.

  • Rozmiar danych wiersza i czy mieści się w ograniczeniu rozmiaru wiersza 8060 bajtów? Aby odpowiedzieć na te pytania, użyj obliczeń dla <row body size>, omówionej w dalszej części tego artykułu.

Tabela zoptymalizowana pod kątem pamięci składa się z kolekcji wierszy i indeksów zawierających wskaźniki do wierszy. Na poniższej ilustracji przedstawiono tabelę z indeksami i wierszami, które z kolei mają nagłówki wierszy i treści:

Diagram tabeli zoptymalizowanej pod kątem pamięci.

Oblicz rozmiar tabeli

Rozmiar w pamięci tabeli w bajtach jest obliczany w następujący sposób:

<table size> = <size of index 1> + ... + <size of index n> + (<row size> * <row count>)

Rozmiar indeksu skrótu jest stały w czasie tworzenia tabeli i zależy od rzeczywistej liczby wiader. bucket_count określony definicją indeksu jest zaokrąglany do najbliższej potęgi liczby 2, aby uzyskać rzeczywistą liczbę zasobników. Na przykład, jeśli określona bucket_count wynosi 100000, to rzeczywista liczba zasobników dla indeksu wynosi 131072.

<hash index size> = 8 * <actual bucket count>

Rozmiar indeksu nieklastrowanego jest w przybliżeniu rzędu wielkości <row count> * <index key size>.

Rozmiar wiersza jest obliczany przez dodanie nagłówka i treści:

<row size> = <row header size> + <actual row body size>
<row header size> = 24 + 8 * <number of indexes>

Rozmiar treści wiersza obliczeniowego

Wiersze w tabeli zoptymalizowanej pod kątem pamięci mają następujące składniki:

  • Nagłówek wiersza zawiera znacznik czasu niezbędny do zaimplementowania wersjonowania wierszy. Nagłówek wiersza zawiera również wskaźnik indeksu w celu zaimplementowania łańcucha wierszy w zasobnikach skrótów (opisanych wcześniej).

  • Treść wiersza zawiera rzeczywiste dane kolumn, w tym niektóre informacje pomocnicze, takie jak tablica null dla kolumn dopuszczających wartość null i tablica przesunięcia dla typów danych o zmiennej długości.

Na poniższej ilustracji przedstawiono strukturę wierszy dla tabeli zawierającej dwa indeksy:

Diagram struktury wierszy dla tabeli zawierającej dwa indeksy.

Znaczniki czasu rozpoczęcia i zakończenia wskazują okres, w którym określona wersja wiersza jest prawidłowa. Transakcje rozpoczynające się w tym interwale mogą zobaczyć tę wersję wiersza. Aby uzyskać więcej informacji, zobacz Transakcje z tabelami Memory-Optimized.

Wskaźniki indeksu wskazują następny wiersz w łańcuchu należącym do zasobnika skrótu. Na poniższej ilustracji przedstawiono strukturę tabeli z dwiema kolumnami (nazwa, miasto) i dwoma indeksami, jedną w nazwie kolumny i jedną w mieście kolumny.

Diagram struktury tabeli z dwiema kolumnami i indeksami.

Na tym rysunku nazwy John i Jane są zhashowane do pierwszego wiadra. Susan jest haszowany do drugiego zasobnika. Miasta Beijing i Bogota zostały zhaszowane do pierwszego segmentu. Paris i Prague zostały zahaszowane do drugiego wiadra.

W związku z tym łańcuchy indeksu skrótu dla nazwy są następujące:

  • Pierwszy zasobnik: (John, Beijing); (John, Paris); (Jane, Prague)
  • Drugi zasobnik: (Susan, Bogota)

Łańcuchy indeksu dotyczącego miasta są następujące:

  • Pierwszy zasobnik: (John, Beijing), (Susan, Bogota)
  • Drugi zasobnik: (John, Paris), (Jane, Prague)

Znacznik czasu zakończenia ∞ (nieskończoność) wskazuje, że jest to aktualnie prawidłowa wersja wiersza. Wiersz nie został zaktualizowany ani usunięty, ponieważ ta wersja wiersza została zapisana.

W przypadku czasu większego niż 200tabela zawiera następujące wiersze:

Nazwa Miasto
John Pekin
Jane Praga

Jednak każda aktywna transakcja z czasem rozpoczęcia 100, zobacz następującą wersję tabeli:

Nazwa Miasto
John Paryż
Jane Praga
Susan Bogota

Obliczenie <row body size> omówiono w poniższej tabeli.

Istnieją dwa różne obliczenia dotyczące rozmiaru treści wiersza: obliczony rozmiar i rzeczywisty rozmiar:

  • Obliczony rozmiar, oznaczony obliczonym rozmiarem treści wiersza, służy do określenia, czy przekroczono ograniczenie rozmiaru wiersza o rozmiarze 8060 bajtów.

  • Rzeczywisty rozmiar, oznaczony jako rzeczywisty rozmiar treści wiersza, to rzeczywisty rozmiar przechowywania treści wiersza w pamięci i w plikach punktów kontrolnych.

Zarówno obliczony rozmiar treści wiersza, jak i rzeczywisty rozmiar treści wiersza są obliczane w podobny sposób. Jedyną różnicą jest obliczanie rozmiaru kolumn (n)varchar(i) oraz varbinary(i), co znajduje odzwierciedlenie w dolnej części poniższej tabeli. Obliczony rozmiar treści wiersza używa zadeklarowanego rozmiaru i jako rozmiar kolumny, podczas gdy rzeczywisty rozmiar treści wiersza używa rzeczywistego rozmiaru danych.

W poniższej tabeli opisano obliczenie rozmiaru treści wiersza podanego jako <actual row body size> = SUM(<size of shallow types>) + 2 + 2 * <number of deep type columns>.

Sekcja Rozmiar Komentarze
kolumny płytkiego typu SUM(<size of shallow types>). Rozmiar w bajtach poszczególnych typów jest następujący:

bit: 1
tinyint: 1
smallint: 2
: 4
rzeczywiste: 4
smalldatetime: 4
małepieniądze: 4
bigint: 8
data i czas: 8
datetime2: 8
zmiennoprzecinkowe: 8
pieniądze: 8
liczbowy (precyzja <= 18): 8
czas: 8
liczbowych (> precyzji 18): 16
uniqueidentifier: 16
Płytkie wypełnienie kolumny Możliwe wartości to:

1, jeśli istnieją kolumny typu głębokiego, a łączny rozmiar danych płytkich kolumn jest liczbą nieparzystą.

0 w przeciwnym razie
Typy głębokie to typy (var)binary i (n)(var)char.
Tablica przesunięcia dla kolumn typu głębokiego Możliwe wartości to:

0, jeśli nie ma kolumn typu głębokiego

2 + 2 * <number of deep type columns> w przeciwnym razie
Typy głębokie to typy (var)binary i (n)(var)char.
tablica null <number of nullable columns> / 8 zaokrąglone do pełnych bajtów. Tablica ma 1 bit na kolumnę, która może przyjąć wartość null. Jest to zaokrąglane do pełnych bajtów.
wypełnienie tablicy null Możliwe wartości to:

1, jeśli istnieją kolumny typu głębokiego, a rozmiar tablicy NULL to nieparzysta liczba bajtów.
0 w przeciwnym razie
Typy głębokie to typy (var)binary i (n)(var)char.
Wypełnienie Jeśli nie ma kolumn typu głębokiego: 0

W przypadku kolumn typu głębokiego dodawane jest 0–7 bajtów wypełnienia, w zależności od największego wyrównania wymaganego przez kolumnę typu płytkiego. Każda płytka kolumna wymaga wyrównania odpowiadającego jej rozmiarowi, jak opisano wcześniej, z tą różnicą, że kolumny GUID wymagają wyrównania 1 bajtu (a nie 16), a kolumny liczbowe zawsze wymagają wyrównania 8 bajtów (nigdy 16). Największe wymaganie dotyczące wyrównania spośród wszystkich płytkich kolumn jest stosowane. 0 - 7 bajtów wypełnienia jest dodawanych w taki sposób, że całkowity rozmiar do tej pory (bez kolumn typu głębokiego) jest wielokrotnością wymaganego wyrównania.
Typy głębokie to typy (var)binary i (n)(var)char.
kolumny głębokiego typu o stałej długości SUM(<size of fixed length deep type columns>)

Rozmiar każdej kolumny jest następujący:

i dla char(i) i binary(i).
2 * i dla nchar(i)
Kolumny typu głębokiego o stałej długości to kolumny typu char(i), nchar(i)lub binary(i).
Kolumny typu głębokiego o zmiennej długości rozmiar obliczony SUM(<computed size of variable length deep type columns>)

Obliczony rozmiar każdej kolumny jest następujący:

i dla varchar(i) i varbinary(i)

2 * i dla nvarchar(i)
Ten wiersz został zastosowany tylko do obliczonego rozmiaru treści wiersza.

Kolumny typu głębokiego o zmiennej długości to kolumny typu varchar(i), nvarchar(i)lub varbinary(i). Obliczony rozmiar jest określany przez maksymalną długość (i) kolumny.
kolumny typu głębokiego o zmiennej długości rzeczywisty rozmiar SUM(<actual size of variable length deep type columns>)

Rzeczywisty rozmiar każdej kolumny jest następujący:

n, gdzie n oznacza liczbę znaków przechowywanych w kolumnie dla varchar(i).

2 * n, gdzie n jest liczbą znaków przechowywanych w kolumnie, dla nvarchar(i).

n, gdzie n jest liczbą bajtów przechowywanych w kolumnie, dla varbinary(i).
Ten wiersz dotyczy tylko rzeczywistego rozmiaru ciała wiersza .

Rzeczywisty rozmiar jest określany przez dane przechowywane w kolumnach w wierszu.

Przykład: obliczanie rozmiaru tabeli i wiersza

W przypadku indeksów skrótów rzeczywista liczba kubełków jest zaokrąglona do najbliższej potęgi 2. Jeśli na przykład określona bucket_count wynosi 100000, rzeczywista liczba zasobników dla indeksu jest 131072.

Rozważ tabelę Orders (Zamówienia) z następującą definicją:

CREATE TABLE dbo.Orders (
    OrderID INT NOT NULL PRIMARY KEY NONCLUSTERED,
    CustomerID INT NOT NULL INDEX IX_CustomerID HASH WITH (BUCKET_COUNT = 10000),
    OrderDate DATETIME NOT NULL,
    OrderDescription NVARCHAR(1000)
)
WITH (MEMORY_OPTIMIZED = ON);
GO

Ta tabela zawiera jeden indeks skrótu i indeks nieklastrowany (klucz podstawowy). Ma również trzy kolumny o stałej długości i jedną kolumnę o zmiennej długości, przy czym jedna z kolumn ma atrybut NULLable (OrderDescription). Załóżmy, że tabela Orders ma 8379 wierszy, a średnia długość wartości w kolumnie OrderDescription wynosi 78 znaków.

Aby określić rozmiar tabeli, najpierw określ rozmiar indeksów. bucket_count dla obu indeksów jest określony jako 10000. Jest to zaokrąglane do najbliższej mocy 2: 16384. W związku z tym całkowity rozmiar indeksów dla tabeli Orders wynosi:

8 * 16384 = 131072 bytes

Pozostaje rozmiar danych tabeli, czyli:

<row size> * <row count> = <row size> * 8379

(Przykładowa tabela zawiera 8379 wierszy). Teraz mamy:

<row size> = <row header size> + <actual row body size>
<row header size> = 24 + 8 * <number of indices> = 24 + 8 * 1 = 32 bytes

Następnie obliczmy <actual row body size>:

  • Kolumny płytkiego typu

    SUM(<size of shallow types>) = 4 <int> + 4 <int> + 8 <datetime> = 16
    
  • Płytkie wypełnienie kolumny wynosi 0, ponieważ łączny płytki rozmiar kolumny jest równy.

  • Tablica przesunięć dla głębokiego typu kolumn

    2 + 2 * <number of deep type columns> = 2 + 2 * 1 = 4
    
  • tablica NULL = 1

  • NULL wypełnienie tablicy = 1, ponieważ rozmiar tablicy NULL jest dziwny i istnieje kolumna typu głębokiego.

  • Dopełnienie

    • 8 to największe wymaganie wyrównania
    • Rozmiar do tej pory wynosi 16 + 0 + 4 + 1 + 1 = 22
    • Najbliższa wielokrotność 8 to 24
    • Całkowite wypełnienie wynosi 24–22 = 2 bajty
  • Nie ma kolumn typu głębokiego o stałej długości (kolumny typu głębokiego o stałej długości: 0).

  • Rzeczywisty rozmiar kolumny typu głębokiego wynosi 2 * 78 = 156. Kolumna pojedynczego typu głębokiego OrderDescription ma typ nvarchar.

<actual row body size> = 24 + 156 = 180 bytes

Aby ukończyć obliczenie:

<row size> = 32 + 180 = 212 bytes
<table size> = 8 * 16384 + 212 * 8379 = 131072 + 1776348 = 1907420

Łączny rozmiar tabeli w pamięci wynosi zatem około 2 megabajty. Nie uwzględnia to potencjalnego obciążenia związanego z alokacją pamięci ani żadnych wersji wierszy wymaganych dla transakcji, które uzyskują dostęp do tej tabeli.

Rzeczywista pamięć przydzielona do tej tabeli i używana przez nią oraz jej indeksy można uzyskać za pomocą następującego zapytania:

SELECT * FROM sys.dm_db_xtp_table_memory_stats
WHERE object_id = object_id('dbo.Orders');

Ograniczenia kolumn poza rzędem

Niektóre ograniczenia i zastrzeżenia dotyczące używania kolumn poza wierszami w tabeli zoptymalizowanej pod kątem pamięci są wymienione w następujący sposób:

  • Jeśli istnieje indeks magazynowania kolumn w tabeli zoptymalizowanej pod kątem pamięci, wszystkie kolumny muszą znajdować się w wierszu.
  • Wszystkie kolumny klucza indeksu muszą być przechowywane w wierszu. Jeśli kolumna klucza indeksu nie mieści się w wierszu, dodanie indeksu zakończy się niepowodzeniem.
  • Zastrzeżenia dotyczące zmiany tabeli zoptymalizowanej pod kątem pamięci przy użyciu kolumn poza wierszem.
  • W przypadku obiektów LOB ograniczenie rozmiaru odzwierciedla tabele oparte na dyskach (limit 2 GB dla wartości LOB).
  • Aby uzyskać optymalną wydajność, zalecamy, aby większość kolumn mieściła się w ciągu 8060 bajtów.
  • Dane poza wierszem mogą powodować nadmierne użycie pamięci i/lub dysku.