No post de hoje, iremos abordar de forma resumida e direta o processamento das requisições que chegam ao SQL Server, por exemplo, por meio de aplicações. Nesse sentido, se pensarmos em uma query, existe um fluxo que utiliza de vários “agentes” para atendê-la. O Processamento de Tarefas no SQL Server ocorre por meio desses agentes, que são responsáveis por garantir que cada instrução e sua etapa sejam processadas da forma mais eficiente possível.
>O processamento começa quando o SQL Server recebe a query e a converte em um plano de execução contendo várias tasks (Table Scan, Sort, Nested Loops…). O sistema então distribui essas tasks entre os workers, que executam as tasks por meio das threads (menor unidade de processamento do S.O). A execução de uma query pode usar uma ou mais threads, dependendo da ordem em que o plano de execução da query define o processamento de cada operador. Se as etapas seguem uma ordem sequencial, uma única thread executa todas elas. Caso contrário, o sistema pode distribuir o trabalho em múltiplas threads para que diferentes partes da tarefa rodem concorrentemente (paralelismo).
No processo de execução, podemos encontrar as threads em três estados principais:
- Running: quando já estão em execução na CPU;
- Suspended: quando estão aguardando algum recurso externo para poder executar (disco ou rede, por exemplo);
- Runnable: quando já possuem os recursos necessários, mas estão na fila aguardando sua vez de rodar.
Ainda no contexto dos estados das threads, cabe falarmos sobre o scheduler. Ele é o responsável por organizar a fila de execução e coordenar o uso da CPU. No SQL Server, o SQLOS (SQL Server Operating System) desempenha essa função, já que o agendador padrão do Windows, por si só, não entende as necessidades específicas de um SGBD. Cada CPU lógica (core) possui seu próprio scheduler, sendo assim, apenas uma thread por vez pode estar em execução por CPU.
A nível de curiosidade, o scheduler possui dois tipos de agendamento para o controle do tempo de execução concedido para cada thread, são eles:
- Método de “agendamento preemptivo” que reserva um tempo de CPU (quantum) para processar cada thread. Esse tempo permite que o sistema realize a alternância entre as execuções.
- Método de “agendamento cooperativo” que permite uma thread ceder seu quantum para outra thread quando chega sua vez de ser executada e ela ainda não está pronta.
Uma informação técnica importante de se observar é o tempo que uma thread passa esperando para executar. Podemos dividir esse tempo em duas partes: o tempo em que a thread espera por um recurso (wait_time) e o tempo em que ela espera para ser agendada para a CPU (signal_wait_time). Também temos o total_wait_time, que é a soma do wait_time (tempo esperando um recurso) + signal_wait_time (tempo esperando para entrar em execução na CPU). Quando o signal_wait_time representa a maior parte do total_wait_time, pode ser um indicativo de pressão de CPU. Isso ocorre pois as tasks estão demorando para entrar em execução após a disponibilização dos recursos que elas precisavam. Nesse caso, é possível que as queries executadas no ambiente estejam utilizando a CPU intensivamente. Alternativamente, o ambiente pode realmente precisar de mais recursos para atender os processos. Uma observação importante é que, nem sempre, a thread entrar em um estado de wait_time é algo negativo. Isso faz parte do fluxo de execução dos processos no SQL Server, principalmente quando vários estão sendo executados concorrentemente.
Compreender ao menos o básico dessa arquitetura auxilia no diagnóstico de problemas de performance, especialmente em ambientes altamente transacionados ou com workloads pesados. Assim, além de adquirir maior facilidade em otimizar a performance do ambiente, tem-se o conhecimento de como o SQL Server funciona nos bastidores!