Talán a legnagyobb durranással kezdem a sorozatot: a memória táblákkal.
A nagyon kemény OLTP (CUD, insert, update, delete) forgalommal terhelt táblákat meg lehet jelölni, mint memória táblákat. A memória táblák éppúgy garantálják az ACID jellemzőket, mint a normál táblák. Azaz Atomic, Consistent, Isolated, Durable. Atomic: támogatja a tranzakciókat. Consistent: ha egy tranzakció előtt konzisztensek voltak az adatok, akkor a tranzakció lezárása után is azok lesznek. Isolated: az egymással versengő tranzakciók nem hatnak egymásra (pontosabban pontosan specifikált, mennyire hatnak egymásra). Durable: ha egyszer azt mondja, elmentett egy adatot, akkor az tényleg tartósan el lesz tárolva, akkor is, ha pont elmegy az áram a commit után. Ez utóbbit gondolná, hogy nem teljesük a memória táblák esetén, de ez nem igaz, ez is teljesen Durable, mint a diszk alapú táblák.
A tranzakciók dolgozhatnak egyszerre diszk és memória táblákon is. Ráadásul a csak memória táblákat elérő tárolt eljárásokat le lehet fordítani natív kódra is, erről majd külön részben írok.
Mit várhatunk tőle? A dokumentáció szerint 5-20-szor gyorsabbak lesznek a műveletek, mint a diszk alapú tábláknál. Ezt majd ki is mérjük, hogy lássuk a saját szemünkkel.
Mire jók a memória táblák?
- Nagysebességű adatbeszúrásokhoz, pl. mérési adatok gyűjtése
- Átmeneti táblákhoz adatbetöltőkhöz illetve adat transzformációs műveletekhez
- Átmeneti adatok tárolására, ahol eddig @-os táblákat használtunk
- Tábla típusú változóként változó számú paraméter átadására data access layerből
Az In-Memory táblák TELJESEN MÁS architektúra mentén vannak felépítve, mint a diszk alapú táblák. Valójában az SQL Servertől függetlenül fejlesztették ki, kérdés volt, hogy külön termék legyen vagy integrálják bele az SQL Serverbe. Utóbbi mellett döntöttek, ami nekünk nagyon kényelmes, csak a fejlesztőknek volt iszonyatos munka az integráció. De mi jól jártuk vele, mivel kényelmes, megszokott módon használhatjuk, mint a diszk alapú táblákat, ráadásul egymás mellett él a kétféle táblatípus, így lehet őket joinolni, stb. Így a nagyon erősen terhelt táblákat át lehet tervezni memória alapúra, a többi meg maradhat diszk alapú. Ez óriási fegyvertény.
Az új engine kifejlesztésére az volt a motiváció, hogy az SQL Server architektúrája az utóbbi legalább 20 évben nem sokat változott. Az alap feltételezés az volt, hogy minden adat diszken van, és csak egy részhalmazuk van benn a memóriában, mivel drága és korlátos a RAM, illetve a tárolandó adathalmaz mérete sokkal nagyobb, mint a RAM mérete. Ez ma már sokszor nem igaz. Emiatt minden szinkronizációs és tárolási adatszerkezet ebből az előfeltételezésből lett kialakítva. Sok processzor és memóriaintenzív feldolgozás esetén azonban az eddigi adatstruktúrák elkezdenek bajba kerülni. Egyre több idő megy el várakozással, amit Latch Contentionként illetve Lock Contentionként látunk a monitoring eszközökben.
Az új engine kódneve Hekaton volt, ami görögül 100-at jelent. Azért ez a kódneve, mert az volt a cél, hogy 100-szor gyorsabb motort rakjanak össze. Nyilván ez magas léc, de mint motiváló erő jó a fejlesztőknek.
Egyetlen egy critical section van csak benne, és az is 1 utasítás hosszú, ami egy növelés művelet, a timestamp generáláshoz használják (bővebben később). A többi többszálú műveletet a processzor által támogatott, így nagyon gyors interlock compare exchange művelettel valósítják meg. (.NET-ben ez az Interlocked.CompareExchange-re van kivezetve.) Egy lock a normál adatbázisban több 10000 órajelciklust igényel, egy latch több ezret, egy compare exchange csak 10-20-at. Szép, mi?
Az új motor természetesen sok korlátot is magával hoz. Ezek egy része természetes, pl. nem támogatnak blobokat (nvarchar(max)), más részük meg lehet csak az 1.0 miatt van, pl. nincsenek check contraintek (ez csak az én véleményem). Ahogy az egyik SQL fejlesztő mondta, hiába akarunk felhőkarcolót építeni, előbb az alapokat kell stabilra építeni. Erről szól ez az iteráció, az SQL Server 2014 RTM.
A memória táblákat lehet SCHEMA_ONLY durability-vel is megjelölni, ekkor a szerver újraindulása után elveszik belőle minden adat, de cserébe tényleg nem nyúl diszkhez, mivel nem is ígér tartóssági garanciákat. De veszett gyors lesz.
Hogy lássunk valami konkrétabbat, így néz ki egy durable memória tábla, azaz, ami restart után is megtartja az adatokat:
CREATE TABLE [Person].[Person_InMem] ( [BusinessEntityID] [int] NOT NULL, [PersonType] [nchar](2) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [NameStyle] [bit] NOT NULL CONSTRAINT [DF_Person_InMem_NameStyle] DEFAULT ((0)), [Title] [nvarchar](8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [FirstName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [MiddleName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [LastName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [Suffix] [nvarchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [EmailPromotion] [int] NOT NULL CONSTRAINT [DF_Person_InMem_EmailPromotion] DEFAULT ((0)), [rowguid] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Person_InMem_rowguid] DEFAULT (newid()), [ModifiedDate] [datetime] NOT NULL CONSTRAINT [DF_Person_InMem_ModifiedDate] DEFAULT (getdate()), CONSTRAINT [PK_Person_InMem_BusinessEntityID] PRIMARY KEY NONCLUSTERED HASH ( [BusinessEntityID] )WITH ( BUCKET_COUNT = 1048576) )WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_AND_DATA )
A MEMORY_OPTIMIZED = ON miatt lesz memória és nem diszk alapú a tábla. A SCHEMA_AND_DATA miatt marad meg restart után.
Ha módosulnak az adatok, csak a tranzakció lezárásakor írja ki a módosításokat diszkre a szerver. Az indexek módosításait NEM logolja, mivel újrainduláskor újra létrehozza őket a recovery fázisban. Ezzel sokkal kevesebb és hatékonyabb IO-val oldja meg a tartós adattárolást a normál táblákhoz viszonyítva.
A következő részben a hash indexeket részletezem ki. Ha tetszett a cikk, örülök a like-nak, ösztönöz, hogy hamar megírjam a többi részt is. :)
Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.