Dela via


Beräkna minneskraven för Memory-Optimized tabeller

gäller för:SQL ServerAzure SQL DatabaseAzure 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.

Se även

Migrera till In-Memory OLTP-