In-Memory a.k.a OLTP “Hekaton” – Deep Dives [4]

Posted on agosto 16, 2013

2


 

Gerenciamento de Memória no “Hekaton”

O “Hekaton” possui uma forma diferente para realizar o gerenciamento de memória dentro do SQL Server. Os dados das tabelas sempre estão em memória, porém o valor máximo suportado para armazenar essas tabelas são de 512 GB.

Caso sua tabela seja maior que esse valor o SQL Server não irá alocar os dados em memória, com isso, durante uma transação dentro dessa tabela você terá falhas durante as operações.Importante saber é que, a nova parte da engine do “Hekaton” está totalmente integrada com a parte do SQL Server OS (Memory Manager) isso quer dizer que, o mesmo reage caso haja pressão de memória, com isso as DMV’s estão disponíveis sempre para verificar a quantidade de memória usada por uma ou mais tabelas. Como dito anteriormente, uma boa prática para alocar suas tabelas In-Memory é sempre utilizar o tamanho da sua tabela x 2 por causa dos versionamentos que o “Hekaton” realiza em memória além das operações internas.

Garbage Collector no “Hekaton”

O Garbage Collector (GC) http://msdn.microsoft.com/en-us/library/ee787088.aspx dentro do “Hekaton” possui o mesmo papel de sempre, quando possuímos operações de UPDATE, DELETE e INSERT’s não commitados isso faz com que o “Hekaton” crie Row Versions (Versões de registros para que não haja LOCK dentro da tabela), com isso quando um SCAN acontece dentro dessa mesma tabela faz com que essa operação fique lenta. Com isso dentro da memória é gerado registros que não são utilizandos fazendo com que o crescimento da tabela seja muito maior, sendo assim o GC atua e faz a liberação dos registros não mais utilizados para manter dentro da memória somente o registros que são necessários, ou seja o GC é totalmente integrado com o “Hekaton”.

O modo cooperativo do GC faz com que registros que não são mais utilizados dentro da tabela sejam retirados, nessa figura vemos como o GC trabalha com o “Hekaton”

hk

(Figura 1 – Modo Cooperativo do GC funcionando.)

Estamos assumindo somente para exemplo que o valor de OLDEST-TIMESTAMP do ‘Hekaton’ é de 150. Com isso uma nova transação acontece dentro da tabela e assumimos que a mesma tem como valor de BEGIN-TIMESTAMP 240. O GC irá analisar quais são os valores do campo END-TIMESTAMP e verificar se esse registro pode ser retirado da memória em toda a tabela.

Registro 1  – Vemos que o valor de END-TIMESTAMP desse registro é 300 o nosso valor de OLDEST-TIMESTAMP é 150 com isso GC não poderá retirar esse registro porque a possibilidade desse registros esta sendo usado é muito grande.

Registro 2  – Temos o símbolo de ∞, esse símbolo significa que nesse momento o registro está sendo utilizado (está nessa versão do versionamento).

Registro 3 e 4  – O Valor do END-TIMESTAMP é menor do que o valor do OLDEST-TIMESTAMP ou seja esses registros podem se coletados pelo o GC, porque o tempo dele é menor do que o o valor de OLDEST-TIMESTAMP

Registro 5 – Temos o símbolo de ∞, esse símbolo significa que nesse momento o registro está sendo utilizado (está nessa versão do versionamento).

O garbage collector (GC) realiza o SCAN da tabela procurando pelos valores que estão menores do que a HINT interna dele, assim ele sabe quais registros não são mais utilizados e com isso os remove, lembrando que o mesmo trabalho totalmente integrado com as threads do GC.

 

Para entendermos melhor iremos realizar uma DEMO de como o GC trabalha nas situações citadas acima. Iremos realizar a criação de uma tabela em memória com o BUCKET_COUNT de 1000000 com isso cada um possui 8k multiplicando temos 8.0 MB de espaço alocado em memória para o índice. Iremos realizar a inserção de 70 Mil registros em base a uma tabela que possuo em disco, que é a mesma tabela e mesma estrutura.

USE [InMemoryDB]

GO

 

CREATE TABLE [dbo].InMemoryVendasProduto

(

                [ID] [int] NOT NULL,

                [GUIDIDProduto] [uniqueidentifier] NOT NULL,

                [Nome] [varchar](50) NULL,

                [TipoProduto] [varchar](50) NOT NULL,

                [ValorProduto] [decimal](12, 2) NULL,

                [Quantidade] [int] NULL,

                [QtdEstoque] [int] NULL,

                [DataCadastro] [datetime] NOT NULL,

                [DataValidade] [datetime] NOT NULL,

                [UsrCadastro] [varchar](30) NOT NULL

 

 PRIMARY KEY NONCLUSTERED HASH

(

                [ID]

)WITH ( BUCKET_COUNT = 1000000)

)WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_AND_DATA )

 

GO

Untitled

(Figura 2 – Tamanho da tabela após criação. 8MB.)

Inserindo os 70Mil registros na tabela…

INSERT INTO InMemoryDB.dbo.InMemoryVendasProduto

SELECT TOP 70000 *

FROM InMemoryDB.dbo.VendasProduto

 

Verificando agora o tamanho da tabela In-Memory temos…

Untitled

(Figira 3 – Espaço usado em memória da tabela InMemoryVendasProduto.)

Utilizando a DMV para verificar temos…

SELECT OBJECT_NAME(object_id) AS Nome,

               *

FROM sys.dm_db_xtp_table_memory_stats

WHERE OBJECT_NAME(object_id) = ‘InMemoryVendasProduto’

Untitled

(Figura 4 – Valores de memória alocada para tabela, utilizada pela tabela, allocada e utilizada pelo índice.)

 

Agora que vimos a alocação de memória dentro do ‘Hekaton’ iremos realizar a exclusão de alguns registros para vermos o GC realizando o reciclyng da memória ou seja a memória sendo desalocada.

Deletendo 59.838 linhas da tabela…

DELETE

FROM InMemoryDB.dbo.InMemoryVendasProduto

WHERE ID > 14532

Após a deleção dos registros o GC irá atuar retirando os registros dentro da tabela fazendo assim com que os versionamentos diminuam.

 

Transactional Log no ‘Hekaton’

Como esse recurso é totalmente integrado com o SQL Server e com ACID então suas transações são logadas. Porém um fator muito importante é que essa operação e otimizada para que a escrita seja mais eficiente e assim o ganho de velocidade seja ainda mais efetivo. Iremos criar duas tabelas, uma em disco e a outra com a mesma estrutura só que em memória e comparar assim os resultados.

CREATE TABLE InMemoryTable

(

            [c1] INT NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH(BUCKET_COUNT = 1000000),

            [c2] CHAR(150) NOT NULL

) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)

GO

 

CREATE TABLE InDiskTables

(

            [c1] INT NOT NULL,

            [c2] CHAR(150) NOT NULL

)

GO

CREATE UNIQUE NONCLUSTERED INDEX idxncl_indisktables_c1

ON InDiskTables (c1)

BEGIN TRANSACTION

DECLARE @i INT = 0

WHILE (@i < 150)

            BEGIN

                        INSERT INTO InDiskTables VALUES (@i, REPLICATE(‘1’,100))

                        SET @i = @i + 1

            END

COMMIT TRANSACTION

Após popularmos a tabela em disco, iremos buscar no LOG as operações realizadas desse INSERT, porém antes disso iremos buscar em qual partição a tabela se encontra.

SELECT *

FROM sys.partitions

WHERE OBJECT_ID = OBJECT_ID(‘InDiskTables’)

Untitled

(Figura 5 – PartitionID da tabela.)

SELECT *

FROM sys.fn_dblog(NULL,NULL)

WHERE PartitionId = 72057594040549376

            OR PartitionId = 72057594040614912

Verificando o LOG……

Untitled

(Figura 6 – Verificando o LOG após a inserção dos registros.)

No final temos uma quantidade de 300 registros dentro do LOG, porém dentro da tabela possuímos somente 150 registros. Isso ocorre porque criamos um índice Não-Cluster, isso faz com que ele precise realizar o INSERT em dois locais. Agora iremos fazer o mesmo processo só que agora no ‘Hekaton’.

BEGIN TRANSACTION

DECLARE @i INT = 0

WHILE (@i < 150)

            BEGIN

                        INSERT INTO InMemoryTable VALUES (@i, REPLICATE(‘1’,100))

                        SET @i = @i + 1

            END

COMMIT TRANSACTION

 

SELECT *

FROM sys.fn_dblog(NULL,NULL)

Untitled

(Figura 7 – Gravação dos Registos no Log do ‘Hekaton’)

Vemos claramente que a arquitetura desse novo recurso se beneficia de diversos recursos, Hash Index, Data e Delta Files, Filestream, Log dentre outros. O ganho de performance como mostrado no post anterior é incrível e o que estou motrando aqui é realmente como isso funciona internamente, ou seja, a tabela está em memória é muito bom, mas estar otimizada em memória é muito melhor e é isso que o ‘Hekaton’ traz para o SQL Server 2014.