Beräkna minneskraven för Memory-Optimized tabeller
gäller för:SQL Server
Azure SQL Database
Azure SQL Managed Instance
Minnesoptimerade tabeller kräver att det finns tillräckligt med minne för att behålla alla rader och index i minnet. Eftersom minne är en begränsad resurs är det viktigt att du förstår och hanterar minnesanvändning i systemet. Avsnitten i det här avsnittet beskriver vanliga scenarier för minnesanvändning och hantering.
Oavsett om du skapar en ny minnesoptimerad tabell eller migrerar en befintlig diskbaserad tabell till en In-Memory OLTP-minnesoptimerad tabell är det viktigt att ha en rimlig uppskattning av varje tabells minnesbehov så att du kan etablera servern med tillräckligt med minne. I det här avsnittet beskrivs hur du beräknar mängden minne som du behöver för att lagra data för en minnesoptimerad tabell.
Om du överväger att migrera från diskbaserade tabeller till minnesoptimerade tabeller kan du läsa avsnittet Avgöra om en tabell eller lagrad procedur ska portas till In-Memory OLTP- för vägledning om vilka tabeller som är bäst att migrera. Alla avsnitt under Migrera till In-Memory OLTP ge vägledning om migrering från diskbaserade till minnesoptimerade tabeller.
Grundläggande vägledning för att uppskatta minneskrav
Från och med SQL Server 2016 (13.x) finns det ingen gräns för storleken på minnesoptimerade tabeller, även om tabellerna måste få plats i minnet. I SQL Server 2014 (12.x) är datastorleken som stöds 256 GB för SCHEMA_AND_DATA tabeller.
Storleken på en minnesoptimerad tabell motsvarar storleken på data plus vissa omkostnader för radrubriker. När du migrerar en diskbaserad tabell till minnesoptimerad, motsvarar storleken på den minnesoptimerade tabellen ungefär storleken på det klustrade indexet eller högen i den ursprungliga diskbaserade tabellen.
Indexar för de minnesoptimerade tabeller tenderar att vara mindre än icke-klustrade index på diskbaserade tabeller. Storleken på icke-grupperade index är i storleksordningen [primary key size] * [row count]
. Storleken på hash-index är [bucket count] * 8 bytes
.
När det finns en aktiv arbetsbelastning krävs extra minne för att ta hänsyn till radversioner och olika åtgärder. Hur mycket minne som behövs i praktiken beror på arbetsbelastningen, men för att vara säker är rekommendationen att börja med två gånger den förväntade storleken på minnesoptimerade tabeller och index och observera vad som är minneskraven i praktiken. Omkostnaderna för radversioner beror alltid på arbetsbelastningens egenskaper – särskilt tidskrävande transaktioner ökar omkostnaderna. För de flesta arbetsbelastningar som använder större databaser (t.ex. >100 GB) tenderar kostnaderna att vara begränsade (25% eller mindre).
Detaljerad beräkning av minneskrav
Exempel på minnesoptimerad tabell
Överväg följande minnesoptimerade tabellschema:
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
Med det här schemat fastställer vi det minsta minne som behövs för den här minnesoptimerade tabellen.
Minne för tabell
En minnesoptimerad tabellrad består av tre delar:
tidsmarkeringar
Radrubrik/tidsstämplar = 24 bytes.Indexpekare
För varje hashindex i tabellen har varje rad en adresspekare på 8 byte till nästa rad i indexet. Eftersom det finns fyra index allokerar varje rad 32 byte för indexpekare (en 8 byte pekare för varje index).data
Storleken på datadelen av raden bestäms genom att du summerar typstorleken för varje datakolumn. I vår tabell har vi fem heltal med 4 byte, tre kolumner med 50 byte och en kolumn på 30 byte. Därför är datadelen av varje rad 4 + 4 + 4 + 4 + 4 + 50 + 50 + 30 + 50 eller 200 byte.
Följande är en storleksberäkning för 5 000 000 (5 miljoner) rader i en minnesoptimerad tabell. Det totala minnet som används av datarader uppskattas på följande sätt:
Minne för tabellens rader
Från ovanstående beräkningar är storleken på varje rad i den minnesoptimerade tabellen 24 + 32 + 200 eller 256 byte. Eftersom vi har 5 miljoner rader förbrukar tabellen 5 000 000 * 256 byte eller 1 280 000 000 byte – cirka 1,28 GB.
Minne för index
Minne för varje hashindex
Varje hashindex är en hashmatris med 8 bytes adresspekare. Matrisens storlek bestäms bäst av antalet unika indexvärden för indexet, t.ex. antalet unika Col2-värden är en bra startpunkt för matrisstorleken för t1c2_index. En hash-matris som är för stor slösar minne. En hashtabell som är för liten saktar ner prestandan eftersom det kommer att ske för många kollisioner med indexvärden som hashats till samma index.
Hash-index uppnår mycket snabba likhetssökningar som:
SELECT * FROM t_hk
WHERE Col2 = 3;
Icke-grupperade index är snabbare för intervallsökningar, till exempel:
SELECT * FROM t_hk
WHERE Col2 >= 3;
Om du migrerar en diskbaserad tabell kan du använda följande för att fastställa antalet unika värden för indexet t1c2_index.
SELECT COUNT(DISTINCT [Col2])
FROM t_hk;
Om du skapar en ny tabell måste du beräkna matrisstorleken eller samla in data från testningen före distributionen.
Information om hur hashindex fungerar i In-Memory minnesoptimerade OLTP-tabeller finns i Hash Indexes.
Ange storleken på hash-indexmatrisen
Storleken på hash-matrisen anges av (bucket_count= value)
där value
är ett heltalsvärde som är större än noll. Om value
inte är en effekt på 2 avrundas den faktiska bucket_count upp till nästa närmaste effekt på 2. I vårt exempel på tabellen (bucket_count = 5000000), eftersom 5 000 000 inte är en potens av 2, avrundas det faktiska antalet hinkar uppåt till 8 388 608 (2^23). Du måste använda det här talet, inte 5 000 000 när du beräknar minne som behövs av hash-matrisen.
I vårt exempel är alltså det minne som behövs för varje hash-matris:
8 388 608 * 8 = 2^23 * 8 = 2^23 * 2^3 = 2^26 = 67 108 864 eller cirka 64 MB.
Eftersom vi har tre hash-index är det minne som behövs för hash-indexen 3 * 64 MB = 192 MB.
Minne för icke-grupperade index
Icke-grupperade index implementeras som Bw-trees med de inre noderna som innehåller indexvärdet och pekarna till efterföljande noder. Lövnoder innehåller indexvärdet och en pekare till tabellraden i minnet.
Till skillnad från hashindex har icke-grupperade index inte en fast bucketstorlek. Indexet växer och krymper dynamiskt med data.
Minne som behövs av icke-grupperade index kan beräknas på följande sätt:
minne som allokerats till noder som inte är lövnoder
För en typisk konfiguration är det minne som allokeras till noder som inte är lövnoder en liten procentandel av det totala minne som tas av indexet. Detta är så litet att det säkert kan ignoreras.Minne för bladnoder
Lövnoderna har en rad för varje unik nyckel i tabellen som pekar på dataraderna med den unika nyckeln. Om du har flera rader med samma nyckel (dvs. du har ett icke-unikt icke-klustrat index) finns det bara en rad i indexbladets nod som pekar på en av raderna, medan de andra raderna är länkade till varandra. Därför kan det totala minne som krävs approximeras genom att:- minneFörIckeKlustaradIndex = (pekareStorlek + summa(nyckelKolumnDatatypStorlekar)) * raderMedUnikaNycklar
Icke-grupperade index är bäst när de används för intervallsökningar, vilket exemplifieras av följande fråga:
SELECT * FROM t_hk
WHERE c2 > 5;
Minne för radversion
För att undvika lås använder In-Memory OLTP optimistisk samtidighet vid uppdatering eller borttagning av rader. Det innebär att när en rad uppdateras skapas en annan version av raden. Dessutom är borttagningar logiska – den befintliga raden markeras som borttagen, men tas inte bort omedelbart. Systemet håller gamla radversioner (inklusive borttagna rader) tillgängliga tills alla transaktioner som eventuellt kan använda versionen har slutfört körningen.
Eftersom det kan finnas många fler rader i minnet när som helst som väntar på att skräpinsamlingscykeln ska frigöra deras minne, måste du ha tillräckligt med minne för att rymma dessa andra rader.
Antalet extra rader kan beräknas genom att beräkna det högsta antalet raduppdateringar och borttagningar per sekund och sedan multiplicera det med antalet sekunder som den längsta transaktionen tar (minst 1).
Värdet multipliceras sedan med radstorleken för att få det antal byte du behöver för radversionshantering.
rowVersions = durationOfLongestTransactionInSeconds * peakNumberOfRowUpdatesOrDeletesPerSecond
Minnesbehovet för inaktuella rader uppskattas sedan genom att multiplicera antalet inaktuella rader med storleken på en minnesoptimerad tabellrad (se Minne för tabellen ovan).
memoryForRowVersions = rowVersions * rowSize
Minne för tabellvariabler
Minne som används för en tabellvariabel frigörs endast när tabellvariabeln hamnar utanför omfånget. Borttagna rader, inklusive rader som tagits bort som en del av en uppdatering, från en tabellvariabel omfattas inte av skräpinsamling. Inget minne frigörs förrän tabellvariabeln avslutar omfånget.
Tabellvariabler som definieras i en stor SQL-batch, till skillnad från ett proceduromfång som används i många transaktioner, kan förbruka mycket minne. Eftersom de inte samlas in genom sophantering kan borttagna rader i en tabellvariabel använda mycket minne och försämra prestanda, eftersom läsåtgärderna måste skanna förbi de borttagna raderna.
Minne för tillväxt
Ovanstående beräkningar beräknar dina minnesbehov för tabellen eftersom den för närvarande finns. Förutom det här minnet måste du uppskatta tabellens tillväxt och tillhandahålla tillräckligt med minne för att hantera den tillväxten. Om du till exempel förväntar dig 10% tillväxt måste du multiplicera resultaten ovan med 1,1 för att få det totala minnet som behövs för tabellen.