Entenda o ciclo de vida de uma Query no SQL Server: Parte 2

Anteriormente, no último artigo, nós havíamos percorrido “todo” caminho de uma Query, da sua execução à entrega ao solicitante. Mas, será que algo além acontece nos bastidores? A resposta é, como quase sempre será, sim!

Neste artigo, vamos nos aprofundar no Storage Engine, Buffer Pool, Access Methods e Transaction Log. Pegue o café e venha desvendar mais uma etapa sobre a execução de processos DDL e DML.

Storage Engine – Access Methods

Como falamos anteriormente, nesta etapa há diferenças entre processos DDL e DML. Vamos começar pela primeira: DDL é uma abreviação de Data Definition Language (Linguagem de Definição de Dados), ou seja, uma linguagem que cria ou modifica estrutura de objetos no banco de dados. Um exemplo rápido são comandos do tipo CREATE, ALTER E DROP.

Neste caso, quando o procedimento chega no Query Executor há uma interação com o Storage Engine através do Access Methods, que é quem fornece as estruturas de armazenamento dos dados e índices, bem como a interface pela qual os dados são recuperados e modificados. Ele é responsável por armazenar operações de linha, índice, alocação de páginas e versionamento. Apesar de conter todo o código da operação, ele não executa, pois, neste ponto ele passa a requisição ao Buffer Manager.

Storage Engine – Buffer manager

Você se lembra que, no primeiro artigo, comentamos que na etapa do Command Parser, o SQL analisa se há um plano de execução pronto para aquela requisição? Pois esta é justamente uma das funções do Buffer Manager:

Ele é responsável por gerenciar o Buffer Pool, ou seja, ele cuida de toda parte de recebimento, armazenagem e entrega de dados. Se você precisar ler algumas linhas de uma página de dados, ele verificará se esta página já está na cache de memória e, caso não esteja, ele buscará esta página no disco, armazenará na cache e só então devolve o resultado ao Access Methods. É nesta última etapa que acontece o famoso Wait_Type PAGEIOLATCH, que representa o tempo  necessário para ler uma página do disco e guardar em memória.

Ele também é responsável por gerenciar funções de I/O de disco para trazer páginas de dados e índices para a cache de memória e compartilhar com os usuários, além de assegurar que o Transaction Log seja escrito antes que cada alteração de banco seja executada, garantindo que essas informações guardadas no Transaction Log tenham uma ordem específica e deixando claro quais blocos de registro devem ser processados em caso de falha de sistema, independentemente de quando ou onde houve a falha.

Storage Engine – Data Cache

Bom, já sabemos que existe Buffer Pool e que quem “gerencia” essa casinha é o Buffer Manager. Contudo, neste Pool não existe apenas a Plan Cache (aliás, esta é a menor parte), existe também o Data Cache e nós vamos falar um pouco mais sobre ele agora.

O Data Cache é a partição do Buffer Pool que ocupa mais espaço. É nele que todas (sim, todas) as páginas de dados são lidas a partir do disco antes de serem usadas. E quando eu digo disco, são os datafiles dos bancos. O tempo que as páginas permanecem em cache é determinado por uma política LRU, para a qual precisamos abrir um parênteses: LRU-K (Last Recently Used) é um algoritmo que mantém “fresco na memória” uma página que é referenciada K vezes.

Cada página de dados possui em seu cabeçalho detalhes das duas últimas vezes em que a página foi acessada e é feita uma varredura periódica para determinar se essa página deve ou não continuar em Cache. Essa varredura também é efetuada pelo Buffer Manager. Sempre que uma varredura é feita, um contador é colocado na página e é incrementado toda vez que a página não é acessada por um determinado período de tempo e quando o SQL Server precisa liberar espaço em cache. Então, as páginas com o contador mais baixo são retiradas da Cache. Um tempo recomendado para que todo esse processo deva ocorrer é de 1000s.

Storage Engine – Transaction Manager / Transaction Log

Agora vamos falar sobre comandos do tipo DML. DML é uma abreviação de Data Modification Language (Linguagem de modificação de Dados). Esta é uma linguagem que resgata, guarda, modifica, insere, deleta e atualiza dados no banco de dados. Exemplo disso são comandos do tipo SELECT, UPDATE, INSERT, DELETE e etc.

O que difere os processos de DML dos processos de DDL, é que, no DML, em paralelo atua também o Transaction Manager. Quando o Buffer Manager recebe uma requisição de alteração, ela encaminha antes ao Transaction Manager para que este guarde um log desta transação para que só então guarde as páginas em cache.

O Transaction Manager possui dois componentes para garantir a consistência da requisição: O Lock Manager e o Log Manager. O Lock Manager gerencia a concorrência de dados através de locks e o Log Manager armazena de fato o log da transação no logfile. Esse tipo de processo é chamado de WAL (Write-ahead Logging).

Mas, neste processo, ainda falta alguém. De maneira análoga, o Buffer Manager está para o Buffer Pool assim como o Transaction Manager está para o Transaction Log! O Transaction Log, utiliza o logfile através dos VLF’s (Virtual Log Files) para armazenar e ordenar esses dados “recuperáveis”. Essa arquitetura é relativamente simples e a receita que o SQL Server usa é basicamente o seguinte:

 

  • Se o próximo crescimento for menor que 1/8 do tamanho físico do log atual, crie 1 VLF que abranja o tamanho do crescimento (começando com SQL Server 2014 (12.x))
  • Se o próximo crescimento for maior que 1/8 do tamanho atual do log, use o método pré-2014:
  • Se o crescimento for menor que 64 MB, crie 4 VLFs que abranjam o tamanho do crescimento (por exemplo, para um crescimento de 1 MB, crie quatro VLFs de 256 KB)
  • Se o crescimento for de 64 MB a 1 GB, crie 8 VLFs que abranjam o tamanho do crescimento (por exemplo, para um crescimento de 512 MB, crie oito VLFs de 64 MB)
  • Se o crescimento for maior que 1 GB, crie 16 VLFs que abranjam o tamanho do crescimento (por exemplo, para um crescimento de 8 GB, crie dezesseis VLFs de 512 MB)

E com isso, finalizamos mais um artigo. Os métodos de Recovery (recuperação) serão abordados em outra ocasião, pois é um processo um tanto quanto extenso.

Então, fique atento ao Blog da Rox, pois em breve será publicada a parte 3 deste conteúdo superinteressante.

Até a próxima e se inscreva em nossa newsletter!

Learn More

Entenda o ciclo de vida de uma Query no SQL Server: Parte 1

Tão importante quanto ter um modelo de troubleshooting desenhado, é entender o que levou a acontecer o incidente. Muito se pode entender do problema, olhando todos os pontos da execução da requisição, e esse é o intuito deste artigo: explorar todo o ciclo de vida de uma query. Entre a solicitação do usuário e a entrega do resultado, muita coisa acontece no SQL Server e é o que veremos a seguir.

Protocol Layer

Tudo começa quando um usuário envia uma solicitação via SNI( SQL Server Network Interface) ao banco de dados a partir de uma query, e aqui vamos utilizar o exemplo de uma instrução do tipo SELECT. Esta solicitação é encaminhada ao SBGD através de um protocolo denominado TDS (Tabular Data Stream) que fica responsável por “desembrulhar” este pacote no client e analisar o que está sendo solicitado. Este mesmo protocolo é o responsável por retornar o resultado ao solicitante ao fim da execução.

Relational Engine – Command Parser

O próximo componente é o Command Parser e é onde a “mágica” começa. Nesta camada, o SQL lida com eventos de T-SQL, analisando sua sintaxe e devolvendo caso haja quaisquer erros de volta à camada do solicitante. Com a sintaxe validada, o próximo passo é criar um plano de execução ou reaproveitar um plano ja existente no Plan Cache. Neste caso é criado uma hash do T-SQL para verificar nesta partição do Buffer Pool se há um plano adequado. se houver, a instrução é designada diretamente ao Query Executor, e falaremos mais sobre ele logo abaixo.

Relational Engine – Query Optimizer

“Beleza, mas e se não houver um plano adequado para a solicitação?”, Neste caso o CMD Parser gerará uma árvore de consulta baseado no T-SQL e enviará à camada do Query Optimizer. Esta camada é uma das mais complexas e “misteriosas” do SQL Server. ele avalia a árvore baseando-se em custos e o objetivo é encontrar o plano mais eficiente, ou seja, um plano que terá o menor custo de execução. Em um plano, cada operador tem um custo de linha de base, que é então multiplicado pelo tamanho da linha e pelo número estimado de linhas para obter o custo desse operador — e o custo do plano é o custo total de todos os operadores. Este “Custo” é apenas um número arbitrário utilizado para representar o custo de recurso do plano em questão.

Esta otimização ocorre em 3 fases e são elas:

  • Fase 0 — Durante esta fase, o otimizador analisa os NESTED LOOP JOINS e não considerará operadores de paralelismo. O otimizador vai parar aqui se o custo do plano que encontrou for < 0.2. Um plano gerado nesta fase é conhecido como um processo de transação, ou TRIVIAL PLAN (Plano Trivial).
  • Fase 1 — A Fase 1 usa um subconjunto das possíveis regras de otimização e procura padrões comuns para os quais já possui um plano. O otimizador vai parar aqui se o custo do plano que encontrou for < 1.0. Planos gerados nesta fase são chamados de QUICK PLAN (planos rápidos).
  • Fase 2 — Esta fase final é onde o otimizador puxa todas as paradas e é capaz de usar todas as suas regras de otimização. Ele também olha para o paralelismo e as visualizações indexadas (se você estiver executando a Enterprise Edition) – FULL PLAN (otimização TOTAL).

Relational Engine – Query Executor

E assim chegamos ao Query Executor. Este componente é o responsável por de fato executar a query, trabalhando por cada etapa do plano e devolvendo o resultado ao usuário. Nesta parte do ciclo, há uma interação com o mecanismo de armazenamento para recuperar ou modificar dados. E nesta parte há diferenças entre instruções DML e DDL. Iremos explorar esta diferença no próximo artigo com mais detalhes.

 

Com isso, chegamos ao fim deste artigo. Espero que tenham gostado e nos vemos no próximo capitulo.

Learn More