A História do Hekaton – Parte 2

Originalmente publicado no Lab27: https://www.lab27.com.br/a-histria-do-hekaton-parte-2/

 

No post anterior, comentei sobre as motivações que levaram à criação do Hekaton.

O passo seguinte era transformar o projeto embrionário em uma implementação comercial, ou seja, incorporá-lo à família do SQL Server.

Compilação de Stored Procedures: .NET ou C/C++

Se a tradução de Stored procedure é procedimento armazenado, então porque não deixar o código de uma procedure previamente compilado em modo nativo? O primeiro passo para otimizar o desempenho transacional é eliminar a linguagem interpretada. Esse problema estava parcialmente resolvido através do uso de Stored Procedures em CLR.

A plataforma .NET oferece um recurso muito interessante chamado de “hosting”, que permite hospedar assemblies .NET dentro do processo e controlar o número de threads e consumo de memória. Você já deve ter se deparado com os latches CLR_AUTO_EVENT e CLR_MANUAL_EVENT nas DMV. Eles possuem uma série de funcionalidades e uma delas é o controle das threads de Garbage Collector e Finalizer pelo Engine do SQL Server.

A questão é que as Stored Procedures em CLR permitem substituir quase todos os comandos T-SQL (IF, WHILE, DECLARE, etc), mas não é possível compilar as queries. As queries devem ser executadas através de uma conexão de loopback chamada SqlContext. Portanto, o desafio do Hekaton era converter queries (SELECT, INSERT, UPDATE, DELETE) em código nativo.

A grande dificuldade era manter o Garbage Collector rodando em background sem afetar o desempenho do SQL Server. A equipe do Hekaton tentou a todo custo eliminar os latches associados à limpeza de memória, assim como enfrentava problemas específicos do .NET (Ex: descarregar os Assemblies dos AppDomains). Quem quiser entender mais sobre o ambiente do .NET Hosting, recomendo a leitura do livro:

Customizing the Microsoft .NET Framework Common Language Runtime
https://www.microsoft.com/learning/en-us/book.aspx?id=6895

Ao final, a decisão foi utilizar um compilador C (sim, a linguagem C e não C++). Nesse caso, o SQL Server realiza a tradução do plano de execução em um arquivo .c e depois chama o compilador CL.EXE.

image

Sabendo que a linguagem C é mais rápida que a versão em .NET, por que a equipe do Hekaton não escolheu esse mecanismo logo de início? A resposta é segurança. Enquanto uma procedure em .NET pode ser gerada por um “emitter” em memória, a versão equivalente em C requer a criação de um código-fonte no File System e a compilação em uma DLL. Do ponto de vista de segurança, foi necessário blindar todos os tipos de ataque que mudem os arquivos .c ou .dll. Vários cenários foram levados em consideração para evitar furos de segurança. Por exemplo, imagine um hacker tentando alterar os arquivos de backup e mudando os arquivos C ou DLL. No caso do SQL Server, as stored procedures compiladas são sempre recriadas.

Árvores Bw-Trees

O desenho inicial do Hekaton incluia somente índices Hash. Essas estruturas são excelentes para consultas do tipo “chave-valor”, similar aos bancos de dados NoSQL. Entretanto, frequentemente encontramos expressões de desigualdade.

Por exemplo:

data > GETDATE() .

Nesse caso, não era possível obter ganhos de desempenho apenas com os índices Hash. Por volta de 2013, uma nova estrutura de dados, chamada Bw-tree, apareceu para resolver esse tipo de problema. Existe um paper que descreve bem o mecanismo, mas é um tanto complicado de entender todo o formalismo: The Bw-Tree- A B-tree for New Hardware Platforms.

Se você quiser uma versão resumida do que é a nova Bw-Tree usada no Hekaton comparada com a tradicional B+ Tree, então diria que as modificações são feitas através de “delta updates” ao invés de “in-place updates”.

Falarei mais sobre isso e sobre o formato dos arquivos no próximo post. Aguardem!