Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
gäller för:SQL Server
Azure SQL Database
Azure SQL Managed Instance
Om du använder temporära tabeller, tabellvariabler eller tabellvärdesparametrar bör du överväga konverteringar av dem för att använda minnesoptimerade tabeller och tabellvariabler för att förbättra prestandan. Kodändringarna är vanligtvis minimala.
I den här artikeln beskrivs:
- Scenarier som argumenterar för konvertering till In-Memory.
- Tekniska steg för att implementera konverteringarna till minnebaserad.
- Förutsättningar före konvertering till Minnesbaserad.
- Ett kodexempel som visar prestandafördelarna med minnesoptimering
A. Grunderna i minnesoptimerade tabellvariabler
En minnesoptimerad tabellvariabel ger stor effektivitet genom att använda samma minnesoptimerade algoritm och datastrukturer som används av minnesoptimerade tabeller. Effektiviteten maximeras när tabellvariabeln nås inifrån en internt kompilerad modul.
En minnesoptimerad tabellvariabel:
- Lagras endast i minnet och har ingen komponent på disken.
- Innebär ingen I/O-aktivitet.
- Innebär ingen
tempdbanvändning eller konflikt. - Kan skickas till en lagrad procedur som en tabellvärdesparameter (TVP).
- Måste ha minst ett index, antingen hash eller icke-grupperat.
- För ett hash-index bör antalet bucketar helst vara 1–2 gånger antalet förväntade unika indexnycklar, men att överskatta antalet bucketar är vanligtvis bra (upp till 10X). För mer information, se Index över Memory-Optimized-tabeller.
Objekttyper
In-Memory OLTP innehåller följande objekt som kan användas för minnesoptimering av temporära tabeller och tabellvariabler:
- Minnesoptimerade tabeller
- Hållbarhet = SCHEMA_ONLY
- Minnesoptimerade tabellvariabler
- Måste anges i två steg (snarare än inline):
-
CREATE TYPE my_type AS TABLE ...;, då -
DECLARE @mytablevariable my_type;.
-
- Måste anges i två steg (snarare än inline):
B. Scenario: Ersätt global tillfällig tabell
Det är ganska enkelt att ersätta en global tillfällig tabell med en minnesoptimerad SCHEMA_ONLY tabell. Den största ändringen är att skapa tabellen vid distributionen, inte vid körtid. Det tar längre tid att skapa minnesoptimerade tabeller än att skapa traditionella tabeller på grund av kompileringstidsoptimeringarna. Att skapa och släppa minnesoptimerade tabeller som en del av onlinearbetsbelastningen skulle påverka arbetsbelastningens prestanda, samt prestandan för att göra om på AlwaysOn-tillgänglighetsgruppens sekundärfiler och databasåterställning.
Anta att du har följande globala temporära tabell.
CREATE TABLE ##tempGlobalB
(
Column1 INT NOT NULL,
Column2 NVARCHAR (4000)
);
Överväg att ersätta den globala temporära tabellen med följande minnesoptimerade tabell som har DURABILITY = SCHEMA_ONLY.
CREATE TABLE dbo.soGlobalB
(
Column1 INT NOT NULL INDEX ix1 NONCLUSTERED,
Column2 NVARCHAR (4000)
)
WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY);
Steg
Konverteringen från global tillfällig till SCHEMA_ONLY är följande steg:
-
dbo.soGlobalBSkapa tabellen en gång, precis som med en traditionell disktabell. - Ta bort tabellskapandet
##tempGlobalBfrån din Transact-SQL (T-SQL). Det är viktigt att skapa den minnesoptimerade tabellen vid distributionstillfället, inte vid körning, för att undvika kompileringskostnaderna som följer med att skapa tabeller. - I din T-SQL ersätter du alla omnämnanden av
##tempGlobalBmeddbo.soGlobalB.
C. Scenario: Ersätt temporär tabell för session
Förberedelserna för att ersätta en tillfällig sessionstabell omfattar mer T-SQL än för det tidigare globala temporära tabellscenariot. Lyckligtvis innebär inte den extra T-SQL att det behövs mer arbete för att utföra konverteringen.
Precis som med scenariot med den globala temporära tabellen är den största ändringen att skapa tabellen vid distributionstid, inte körning, för att undvika kompileringskostnaderna.
Anta att du har följande temporära sessionstabell.
CREATE TABLE #tempSessionC
(
Column1 INT NOT NULL,
Column2 NVARCHAR (4000)
);
Skapa först följande tabellvärdefunktion för att filtrera på @@spid. Funktionen kan användas av alla SCHEMA_ONLY tabeller som du konverterar från temporära sessionstabeller.
CREATE FUNCTION dbo.fn_SpidFilter
(@SpidFilter SMALLINT)
RETURNS TABLE
WITH SCHEMABINDING, NATIVE_COMPILATION
AS
RETURN
SELECT 1 AS fn_SpidFilter
WHERE @SpidFilter = @@spid
För det andra skapar du tabellen SCHEMA_ONLY plus en säkerhetsprincip i tabellen.
Varje minnesoptimerad tabell måste ha minst ett index.
- För table dbo.soSessionC kan ett HASH-index vara bättre om vi beräknar lämpliga BUCKET_COUNT. Men för det här exemplet förenklar vi till ett NONCLUSTERED-index.
CREATE TABLE dbo.soSessionC
(
Column1 INT NOT NULL,
Column2 NVARCHAR (4000) NULL,
SpidFilter SMALLINT DEFAULT (@@spid) NOT NULL,
CONSTRAINT CHK_soSessionC_SpidFilter CHECK (SpidFilter = @@spid),
INDEX ix_SpidFiler NONCLUSTERED (SpidFilter)
-- INDEX ix_SpidFilter HASH
-- (SpidFilter) WITH (BUCKET_COUNT = 64),
)
WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY);
GO
CREATE SECURITY POLICY dbo.soSessionC_SpidFilter_Policy
ADD FILTER PREDICATE dbo.fn_SpidFilter(SpidFilter) ON dbo.soSessionC
WITH (STATE = ON);
GO
För det tredje, i din allmänna T-SQL-kod:
- Ändra alla referenser till temp-tabellen i dina Transact-SQL-instruktioner till den nya minnesoptimerade tabellen:
-
gammal:
#tempSessionC -
Ny:
dbo.soSessionC
-
gammal:
- Ersätt uttrycken
CREATE TABLE #tempSessionCi koden medDELETE FROM dbo.soSessionCför att säkerställa att en session inte exponeras för tabellinnehåll som infogats av en tidigare session med samma session_id. Det är viktigt att skapa den minnesoptimerade tabellen vid distributionstillfället, inte vid körning, för att undvika kompileringskostnaderna som följer med att skapa tabeller. - Ta bort
DROP TABLE #tempSessionC-satserna från koden. Valfritt kan du också infoga enDELETE FROM dbo.soSessionCinstruktion om minnesstorlek är ett potentiellt problem.
D. Scenario: Tabellvariabeln kan vara MEMORY_OPTIMIZED=ON
En traditionell tabellvariabel representerar en tabell i tempdb databasen. För mycket snabbare prestanda kan du minnesoptimera tabellvariabeln.
Här är T-SQL för en traditionell tabellvariabel. Dess omfång upphör när antingen batchen eller sessionen slutar.
DECLARE @tvTableD TABLE (
Column1 INT NOT NULL,
Column2 CHAR (10));
Konvertera i linje till explicit
Den föregående syntaxen sägs skapa tabellvariabeln inline. Den infogade syntaxen stöder inte minnesoptimering. Så låt oss konvertera den inbyggda syntaxen till den explicita syntaxen för TYP.
Omfattning: Typdefinitionen som skapades av den första goavgränsade batchen kvarstår även efter att servern har stängts av och startats om. Men efter den första go-avgränsare behålls den deklarerade tabellen @tvTableC endast tills nästa steg har nåtts och batchen slutar.
CREATE TYPE dbo.typeTableD AS TABLE (
Column1 INT NOT NULL,
Column2 CHAR (10));
GO
SET NOCOUNT ON;
DECLARE @tvTableD AS dbo.typeTableD;
INSERT INTO @tvTableD (Column1) VALUES (1), (2);
SELECT * FROM @tvTableD;
GO
D.2 Konvertera uttryckligen lagrade data till minnesoptimerad format.
En minnesoptimerad tabellvariabel finns inte i tempdb. Minnesoptimering resulterar i hastighetsökningar som ofta är 10 gånger snabbare eller mer.
Konverteringen till minnesoptimerad sker bara i ett steg. Förbättra skapandet av den explicita TYPE enligt följande, vilket tillsätter:
- Ett index. Återigen måste varje minnesoptimerad tabell ha minst ett index.
- MEMORY_OPTIMIZED = PÅ.
CREATE TYPE dbo.typeTableD AS TABLE (
Column1 INT NOT NULL INDEX ix1,
Column2 CHAR (10))
WITH (MEMORY_OPTIMIZED = ON);
Färdig.
E. Nödvändig FILGRUPP för SQL Server
Om du vill använda minnesoptimerade funktioner på Microsoft SQL Server måste databasen ha en FILEGROUP som deklareras med MEMORY_OPTIMIZED_DATA.
- Azure SQL Database kräver inte att du skapar den här FILGRUPPEN.
Förutsättning: Följande Transact-SQL kod för en FILEGROUP är en förutsättning för de långa T-SQL-kodexemplen i senare avsnitt i den här artikeln.
- Du måste använda SSMS.exe eller ett annat verktyg som kan skicka T-SQL.
- Klistra in exempelkoden FILEGROUP T-SQL i SSMS.
- Redigera T-SQL för att ändra dess specifika namn och katalogsökvägar efter dina önskemål.
- Alla kataloger i FILENAME-värdet måste finnas i förväg, förutom att den slutliga katalogen inte får finnas i förväg.
- Kör din redigerade T-SQL.
- Du behöver inte köra FILEGROUP T-SQL mer än en gång, även om du upprepade gånger justerar och kör hastighetsjämförelse-T-SQL i nästa underavsnitt.
ALTER DATABASE InMemTest2
ADD FILEGROUP FgMemOptim3 CONTAINS MEMORY_OPTIMIZED_DATA;
GO
ALTER DATABASE InMemTest2
ADD FILE (NAME = N'FileMemOptim3a', FILENAME = N'C:\DATA\FileMemOptim3a'
-- C:\DATA\ preexisted.
) TO FILEGROUP FgMemOptim3;
GO
Följande skript skapar filgruppen åt dig och konfigurerar rekommenderade databasinställningar: enable-in-memory-oltp.sql
Mer information om ALTER DATABASE ... ADD för FILE och FILEGROUP finns i:
F. Snabbtest för att bevisa hastighetsförbättring
Det här avsnittet innehåller Transact-SQL kod som du kan köra för att testa och jämföra hastighetsvinsten för INSERT-DELETE från att använda en minnesoptimerad tabellvariabel. Koden består av två halvor som är nästan desamma, förutom att tabelltypen är minnesoptimerad under den första halvan.
Jämförelsetestet varar i cirka 7 sekunder. Så här kör du exemplet:
- Förutsättning: Du måste redan ha kört FILEGROUP T-SQL från föregående avsnitt.
- Kör följande T-SQL-INSERT-DELETE-skript.
- Lägg märke till instruktionen
GO 5001som skickas in igen 5 001 gånger för T-SQL. Du kan justera talet och köra igen.
När du kör skriptet i en Azure SQL Database ska du köra från en virtuell dator i samma region.
PRINT ' ';
PRINT '---- Next, memory-optimized, faster. ----';
DROP TYPE IF EXISTS dbo.typeTableC_mem;
GO
CREATE TYPE dbo.typeTableC_mem -- !! Memory-optimized.
AS TABLE (
Column1 INT NOT NULL INDEX ix1,
Column2 CHAR(10)
)
WITH (MEMORY_OPTIMIZED = ON);
GO
DECLARE @dateString_Begin NVARCHAR(64) =
CONVERT(NVARCHAR(64), GETUTCDATE(), 121);
PRINT CONCAT (
@dateString_Begin,
' = Begin time, _mem.'
);
GO
SET NOCOUNT ON;
DECLARE @tvTableC dbo.typeTableC_mem;-- !!
INSERT INTO @tvTableC (Column1) VALUES (1), (2);
INSERT INTO @tvTableC (Column1) VALUES (3), (4);
DELETE @tvTableC;GO 5001
DECLARE @dateString_End NVARCHAR(64) =
CONVERT(NVARCHAR(64), GETUTCDATE(), 121);
PRINT CONCAT (
@dateString_End,
' = End time, _mem.'
);
GO
DROP TYPE IF EXISTS dbo.typeTableC_mem;
GO
---- End memory-optimized.
-------------------------------------------------
---- Start traditional on-disk.
PRINT ' ';
PRINT '---- Next, tempdb based, slower. ----';
DROP TYPE IF EXISTS dbo.typeTableC_tempdb;
GO
CREATE TYPE dbo.typeTableC_tempdb -- !! Traditional tempdb.
AS TABLE (
Column1 INT NOT NULL,
Column2 CHAR(10)
);
GO
DECLARE @dateString_Begin NVARCHAR(64) =
CONVERT(NVARCHAR(64), GETUTCDATE(), 121);
PRINT CONCAT (
@dateString_Begin,
' = Begin time, _tempdb.'
);
GO
SET NOCOUNT ON;
DECLARE @tvTableC dbo.typeTableC_tempdb;-- !!
INSERT INTO @tvTableC (Column1) VALUES (1), (2);
INSERT INTO @tvTableC (Column1) VALUES (3), (4);
DELETE @tvTableC;
GO 5001
DECLARE @dateString_End NVARCHAR(64) =
CONVERT(NVARCHAR(64), GETUTCDATE(), 121);
PRINT CONCAT (
@dateString_End,
' = End time, _tempdb.'
);
GO
DROP TYPE IF EXISTS dbo.typeTableC_tempdb;
GO
PRINT '---- Tests done. ----';
GO
Här är resultatet.
---- Next, memory-optimized, faster. ----
2016-04-20 00:26:58.033 = Begin time, _mem.
Beginning execution loop
Batch execution completed 5001 times.
2016-04-20 00:26:58.733 = End time, _mem.
---- Next, tempdb based, slower. ----
2016-04-20 00:26:58.750 = Begin time, _tempdb.
Beginning execution loop
Batch execution completed 5001 times.
2016-04-20 00:27:05.440 = End time, _tempdb.
---- Tests done. ----
G. Förutsäga aktiv minnesförbrukning
Du kan lära dig att förutsäga de aktiva minnesbehoven för dina minnesoptimerade tabeller med följande resurser:
För större tabellvariabler använder icke-grupperade index mer minne än för minnesoptimerade tabeller. Ju större radantal och indexnyckel, desto mer ökar skillnaden.
Om den minnesoptimerade tabellvariabeln endast nås med ett exakt nyckelvärde per åtkomst kan ett hash-index vara bättre än ett icke-klustrat index. Men om du inte kan uppskatta lämpliga BUCKET_COUNT är ett NONCLUSTERED-index ett bra andra val.