Na segunda parte, vimos pontos como resultados, consulta à memória em execução, organização de dados e acesso aos dados. Nesta última parte, dando continuidade, serão abordados leitura de dados, travas, bloqueios, entre outros tópicos.
***
Leitura de dados
Os operadores de acesso de dados sempre leem dados de um cache, nunca a partir do disco. Esse cache é chamado de área de buffer (Buffer Pool). Se os dados não estão presentes no cache, o operador de acesso de dados deve solicitá-los a partir do disco (leitura de IO) e aguardar até que a informação seja localizada no cache. Os dados do cache (na área de buffer) são compartilhados entre todas as consultas para que os operadores de acesso das leituras subsequentes e que tiverem de os mesmos dados possam se beneficiar encontrando os dados no cache. O SQL Server irá ler o máximo de dados em cache, aumentando o processo de memória alocada privada até que toda a memória do sistema seja alocada para o SQL Server (sujeita a um limite máximo configurado, alterando a memória máxima do servidor). O cache de buffer (pool) e a leitura e gravação de IO não lidam com linhas individuais, mas sempre operam em uma página de 8Kb de buffer.
Vamos considerar como um operador de acesso a dados, como uma varredura, iria ler dados a partir de uma pilha desordenada:
- Na primeira chamada next(), o operador tem que encontrar a primeira linha e devolvê-la. O SQL Server armazena metadados sobre tabelas que descrevem exatamente quais páginas pertencem a uma tabela. Para obter mais detalhes, recomendo a leitura dos artigos Gerenciando o espaço usado por objetos e Por dentro do motor de armazenamento: páginas IAM, cadeias de IAM e unidades de alocação. O operador de acesso a dados irá solicitar uma referência na área de buffer para essa página, que irá retornar um ponteiro para a cópia na memória da página solicitada. Se a página não estiver na memória, os blocos de pedido até a página serão lidos a partir do disco. A página contém uma série de registros de dados individuais. Um registro não é necessariamente uma linha inteira, porque os grandes valores e os valores de comprimento variável podem ser armazenados em outra página. Recomendo a leitura do artigo Por dentro do motor de armazenamento: Anatomia de um registro para mais detalhes sobre como as linhas são dispostas na página. O operador de acesso de dados irá localizar a primeira linha na página, copiar os valores de campos solicitados e seus retornos. O operador de acesso a dados mantém o estado interno que permite que ele retorne de forma eficiente para essa posição (a mesma página e fila).
- Operadores principais consomem a primeira linha retornada pelo operador de acesso de dados.
- Quando o método next() é chamado novamente pelo operador de acesso a dados, este irá utilizar o contexto de status previamente estabelecido para se posicionar rapidamente na página e na linha atual, de forma que possa avançar da posição em que está para a próxima linha, copiar os valores de campos solicitados e retornar os dados.
- Operadores principais consomem a próxima linha retornada pelo operador de acesso a dados.
- Quando o método next() é chamado novamente sobre o operador de acesso a dados e todas as linhas da página tiverem sido consumidas, o operador irá solicitar ao pool de buffer que faça referência a próxima página. A “próxima” página é determinada a partir dos metadados da tabela. Recomendo novamente a leitura do artigo de Paul Randal para mais detalhes. Uma vez obtida a próxima página, o operador retorna a primeira linha sobre essa nova página (ele copia os campos de solicitação e retorna o resultado).
- Operadores principais consomem a linha retornada pelo operador de acesso a dados.
- Isso continua até que a última linha da última página pertencente à tabela seja retornada. Depois disso, o operador atinge o fim da tabela, e o status interno permanece posicionado no final de tabela e não pode mais retornar quaisquer linhas.
- Quando o operador de acesso a dados não pode mais retornar quaisquer linhas, o operador pai (ou principal) executa qualquer ação que tiver que fazer quando os operadores filhos atingirem o status de ‘feito’. Por exemplo, um operador SORT agora pode começar a retornar as linhas. Uma junção de hash pode começar a iteração e retornar as linhas. O operador SELECT pode retornar falso, causando novos operadores até que a árvore termine, em última análise, fazendo com que a consulta seja executada.
- Operadores de acesso a dados podem retroceder. Por exemplo, se o nosso operador for filho de um operador pai em um loop aninhado, quando esse loop estiver completo o operador pai teria pedido a próxima linha a partir do operador filho e então retornaria ao operador de acesso a dados e o incrementaria novamente. Reformulando: um operador de acesso a dados faz com que ele redefina seu status de posição interna e faz com que ele comece novamente a partir da primeira linha na primeira página.
Para efeitos de comparação, aqui está como um operador de acesso a dados que opera em uma árvore ordenada B-Tree se comporta:
- Na primeira chamada ao método next(), o operador tem de procurar (encontrar) a primeira linha que tem a chave solicitada e devolvê-la. O SQL Server armazena metadados sobre os B-Trees que descrevem exatamente quais páginas pertencem a um índice, mas, ao contrário do Heap, quando é necessário ir para a primeira página, o operador tem de navegar para uma chave específica. A partir dos metadados, ele recupera o id da página raiz e, em seguida, solicita uma referência na área de buffer para essa página. Usando o valor da chave pesquisada, o operador de acesso de dados navega até a B-Tree e também acessa a página a partir do local que contém a primeira linha igual (ou logo depois dela) da chave pesquisada. A cada acesso ao B-Tree, o operador de acesso de dados deve solicitar a página relevante do pool de buffer e, possivelmente, esperar por ele para ser lido a partir do disco. Na página, o operador de acesso a dados terá que procurar as linhas para localizar o registro do local desejado, copiar os valores da coluna desejada e depois voltar. É possível que o operador de acesso a dados não encontre uma linha com o valor da chave procurada. O acesso B-Tree pode solicitar um valor de chave exata para a linha, e pode solicitar a primeira linha após o valor da chave ou pode solicitar a primeira linha igual ou após o valor da chave. O B-Tree pode ser pesquisado em ambos os sentidos (ascendente ou descendente), o que depende da direção da pesquisa. Não que isso não seja a mesma coisa que ter a ordenação por ascendente ou descendente definida no índice, o que significa alterar a ordem real de linhas encontradas no B-Tree.
- Operadores principais consomem a primeira linha retornada pelo operador de acesso a dados.
- Se o operador é usado para uma varredura, então o método next() será chamado novamente pedindo para recuperar a linha depois que algum valor for retornado. O operador de acesso B-Tree irá armazenar o valor da chave retornada anteriormente e irá se posicionar sobre esse valor, usando o mesmo procedimento para navegar no B-Tree, como descrito acima, e depois vai passar para a próxima linha. Se não houver mais linhas na página, então o operador irá retornar a primeira linha da página seguinte (novamente, pedindo para a próxima página do pool de buffer e, possivelmente, ter que esperar para que ela seja lida no disco). No B-Tree, índices organizam as páginas que estão ligadas, onde cada página tem sobre ela um ponteiro (e o ID de página) da página seguinte.
- Operadores principais consomem a próxima linha retornada pelo operador de acesso a dados.
- Varreduras podem conter valor de chave final. Nesse caso, a chamada do método next() pode retornar falso (não retornar uma linha) se a linha localizada se mover para a próxima linha a partir da posição atual e for maior do que o valor final da linha. O termo “maior” é relativo, pois a varredura pode atravessar o B-Tree, tanto em ordem crescente ou decrescente, e o próprio B-Tree pode ser organizado de forma ascendente ou descendente. O operador pode terminar a varredura quando atinge uma chave posicionada após o fim do valor de intervalo, ou quando atinge a última linha da última página do B-Tree.
- Além de retroceder, os operadores B-Tree também podem se reconectar. Uma reconexão redefine o estado do operador para iniciar novamente a procura/varredura com os mesmos parâmetros de chave. A reconexão irá alterar os valores-chave reais. Veja o artigo Planos de execução de referência lógica e operadores físicos para obter mais detalhes.
Leitura de dados antecipada
A partir da descrição sobre como os operadores de digitalização operam, você poderá ver quecada vez que terminar de ler todas as linhas em uma página terá que “consertar” a página no buffer pool que, potencialmente, pode parar o operador para esperar a página a ser obtida a partir do disco para a memória. Se o operador tem de parar em cada página, o desempenho cai. A solução é ler antecipadamente as páginas que não são referenciadas pelo operador agora, mas que serão referenciadas em breve. O SQL Server faz isso e corrige a leitura assíncrona antes da requisição para páginas que serão exigidas pelo operador de digitalização antes que este realmente atinja essa página para ler as linhas. Por sorte, no momento em que o operador atinge essa página, ela já estará no pool de buffer e será fixada quase instantaneamente. Para mais detalhes, veja os artigos Leitura de páginas e Leitura antecipada sequencial. Existe também um tipo especial de leitura antecipada para ler páginas aleatórias de um loop aninhado – consulte o artigo Carregamento antecipado.
Travas: recurso de proteção contra acesso simultâneo
Antes de partir para a forma como as gravações de dados são executadas, é necessário conhecer os mecanismos que existem para garantir que os dados sempre serão os corretos durante a leitura de uma página. Por conta do recurso de multithreading, é sempre necessário efetuar verificações para garantir que os processos não leem uma gravação incompleta. Os desenvolvedores estão familiarizados com as estruturas primitivas utilizadas para impor tal proteção: mutexes, semáforos, seções críticas. Na nomenclatura de banco de dados, esse recurso é conhecido como trava. Travas são estruturas de dados que protegem recursos para acesso simultâneo (por exemplo: cada página na área de buffer tem uma trava para protegê-la contra acesso simultâneo). Travas suportam vários modos: compartilhado, exclusivo, atualização, manter como está, destruir. A tabela a seguir mostra a compatibilidade modo trava:
KP | SH | UP | EX | DT | |
---|---|---|---|---|---|
KP | Y | Y | Y | Y | N |
SH | Y | Y | Y | N | N |
UP | Y | Y | N | N | N |
EX | Y | N | N | N | N |
DT | N | N | N | N | N |
Sempre que um operador de consulta precisa acessar a página (por exemplo, para comparar uma chave ou para ler uma linha), deve adquirir a trava de página no modo SH. Vários operadores podem ler a mesma página ao mesmo tempo em múltiplas threads e também podem adquirir a trava de página no modo SH.
Qualquer operador que precise modificar a página deve adquirir a trava de página no modo EX. Apenas um operador pode modificar a página de cada vez, e não há outros operadores que podem ler a mesma página até que a modificação esteja completa. Esses dois tipos de solicitações são exibidos nas estatísticas como tipos de espera PAGELATCH_SH e PAGELATCH_EX. O whitepaper diagnóstico e resolução de problemas com as travas do SQL Server fornece mais detalhes, inclusive sobre como a trava também protege os pedidos de IO (para que a página só seja lida uma vez a partir do disco, mesmo em múltiplas threads simultâneas sem que seja preciso buscá-la a partir do disco a todo momento) e também como sistemas multi-CPU de ponta usam superlatches para evitar invalidação do cache do processador.
É importante não confundir a proteção de página física oferecido pelas travas com a proteção lógica de bloqueio. O bloqueio pode ser controlado pelo cliente (por exemplo, selecionando um nível de isolamento), mas travas são sempre necessárias.
Bloqueios
Enquanto travas oferecem proteção física para o acesso simultâneo entre threads, bloqueios oferecem proteção lógica para acesso simultâneo entre as transações:
Lógico versus Físico
Os bloqueios descrevem a entidade que está sendo bloqueada, eles não são a entidade real. Os bloqueios possuem seis cadeias de bytes que descrevem o que está sendo bloqueado. Travas são o objeto real na memória a ser bloqueado. Para criar uma trava, o código deve de alguma forma obter uma referência (o ponteiro, o endereço etc.) para o objeto da trava. Para obter um bloqueio do código, é necessário apenas descrever o objeto a ser bloqueado e, em seguida, solicitar o bloqueio.
Threads versus Transações
Travas são criadas e liberadas pelas threads em execução dentro do processo. Os bloqueios são solicitados e liberados pelas transações. Essa é uma diferença muito importante, já que as transações podem se estender por vários pedidos e executar múltiplas threads (simultaneamente, em caso de execução paralela, ou sequencialmente, em caso de pedidos subsequentes realizados por diferentes workers).
Os bloqueios são mais complexos do que as travas, mas não menos importantes, já que existem 22 modos de bloqueio documentados no material Compatibilidade de Bloqueio do SQL Server 2008 R2. Adicione nomes enigmáticos, tais como SCH-S, SIX ou RX-U e não é à toa que os tipos de bloqueio parecerão um pouco arcaicos. Vamos tentar esclarecer um pouco o que esses vários modos de bloqueio significam.
Esquema de estabilidade
Enquanto um plano de consulta está sendo executado, é desejável que os objetos referenciados por esse plano (tabelas, índices) não alterem a estrutura (por exemplo, alterar uma coluna em uma tabela), nem desaparecçam completamente. Para evitar essas alterações, todas as consultas obtém, no início da execução, um bloqueio chamado “esquema de bloqueio de estabilidade” em todos os objetos referenciados pelo plano. Esse é o modo de bloqueio SCH-S. A DDL obterá o modo de bloqueio SCH-M (a modificação do esquema), que é o mais poderoso modo de bloqueio (mais restritivo, menos compatível), garantindo que nenhuma outra declaração poderá até mesmo fazer referência a esse objeto. Na prática, a maioria das consultas não adquirem o modo de bloqueio SCH-S; em vez disso, obtêm os dados equivalentes à intenção de bloqueio, como o modo de bloqueio IS .
Shared, Update, Exclusive
Os modos de bloqueio básicos são S (shared), U (update) e X (exclusive). S e X são autoexplicativos. O modo U é adquirido ao inspecionar uma linha que pode ser posteriormente atualizada ou excluída (daí o bloqueio pode ser atualizado para X). Bloqueios U não bloqueiam a leitura dos dados, mas bloqueiam outras consultas contra uma possível atualização/exclusão em potencial. Sem um modo de bloqueio U, duas consultas tentando atualizar a mesma linha chegariam num impasse, assim como aconteceria na tentativa de escalonar o modo S para o modo X. Consultas em atualização (update) adquirem diretamente o modo de bloqueio X em todas as linhas, mesmo aquelas linhas que não se qualificam para atualização, o que resultaria em bloqueio desnecessário de leitura.
Hierarquia de bloqueio e Bloqueio intencional
Todos os modos de bloqueio que têm um I em nome são bloqueios intencionais. Para entender o que são bloqueios intencionais, você terá que pensar no problema da hierarquia de bloqueio.Considere uma consulta que vai digitalizar uma tabela inteira, que contém alguns milhões de linhas. Criar bloqueios individuais sobre essas linhas irá resultar em alguns milhões de solicitações de bloqueio de linha, uma proposta que sairá cara para o servidor. Sim, a criação de um bloqueio é rápida, mas até a rapidez pode gerar problemas quando bloqueios são repetidos à exaustão. Então, a consulta decide, em vez de bloquear toda a tabela, manter um bloqueio do tipo S sobre o objeto da tabela, em vez de bloquear cada linha individualmente. Outra outra consulta simultânea quer apagar uma linha nessa tabela e solicita um bloqueio X, e aqui está o dilema: como o Gerenciador de Bloqueio deveria saber que não pode conceder um bloqueio X para essa tabela porque a tabela inteira está bloqueada em modo S por outra transação?
Lembre-se de que os bloqueios são apenas processos que descrevem o que está sendo bloqueado, e não há como o Gerenciador de Bloqueio saber que a cadeia descrita na linha solicitada em modo X passou a ser uma linha na tabela em que o modo de bloqueio S foi concedido. A solução são os bloqueios intencionais. Os operadores que solicitam bloqueios conhecem a hierarquia real dos objetos envolvidos: o operador se atualiza para saber qual linha deve atualizar e que pertence a essa tabela. Então, é da responsabilidade do operador solicitar um bloqueio intencional do objeto pai dessa hierarquia. O operador irá solicitar primeiro um bloqueio IX da tabela (“exclusivo e intencional”), que pode ser lido como uma declaração: “Eu não sou o bloqueio de tabela, mas eu posso bloquear uma linha nessa tabela em modo X”.
Esse modo IX não será compatível com o modo S (shared ou compartilhado) solicitado pela digitalização de dados que quer bloquear toda a tabela e agora o problema está resolvido, tornando o conflito oculto explícito, e portanto, bloqueando uma das transações até que as outras sejam concluídas. Então, quando você vê modos de bloqueio como SIX, entenda seu significado: “Eu sou o bloqueio de tabela em modo S e eu posso bloquear uma linha nessa tabela em modo X”.
Bloqueio de intervalo de chaves
Modos de bloqueio que contêm um R no nome são bloqueios de intervalo de chaves. Eles são utilizados para proteger as linhas que não existem. Bloqueios de intervalo de chave são o mecanismo usado pelo SQL Server para obter maior nível de isolamento da serialização entre transações (por exemplo, não é uma leitura fantasma). Como o isolamento da serialização requer que não existam novas linhas sendo exibidas em um conjunto de resultados quando uma consulta é realizada repetidamente sob a mesma transação, o caminho para garantir que essa exigência seja cumprida é bloquear não só as linhas reais retornadas por uma consulta, mas também toda a gama que abrange essas linhas para que uma inclusão concorrente de dados não possa danificar as linhas já devolvidas pela primeira execução da consulta. Veja o documento Intervalo de bloqueio de chave para obter mais detalhes.
Gravação de dados
Operadores que modificam dados (escrita) são muito semelhantes aos operadores de leitura apresentados acima. Quando o método next() é chamado em um operador de gravação, é necessário localizar a linha (ou, para uma inclusão, localizar a posição para inserir uma nova linha) e, em seguida, fazer a modificação real dos dados. Então, quando o método next() for chamado novamente, irá fazer a modificação na próxima linha. Excluir operadores de atualização é uma tarefa geralmente conduzida por outros operadores de leitura que localizam a linha a ser excluída ou atualizada, e esses operadores de leitura passam para os operadores de gravação de um marcador que localiza exatamente a linha a ser modificada. A inserção real, a exclusão e modificação de dados, porém, é algo um pouco mais complicado do que a leitura. O SQL Server registra cada modificação feita aos dados pela primeira vez no log. A grosso modo, todas as gravações ocorrem na seguinte sequência:
- O operador de gravação é “posicionado” na página que será modificada (a linha que deve ser inserida, eliminada ou modificada na página). Isso significa que a página deve ser “fixada” no cache do SQL Server.
- O operador deve obter uma trava exclusiva para a página. Isso garante que nenhum outro operador poderá ler essa página.
- O operador deve gerar um registro de log descrevendo exatamente a operação que está prestes a realizar na página e acrescentar esse registro ao log.
- O operador pode agora modificar a imagem na memória cache.
- A LSN gerada para o registro de log criado antes da modificação da página deve ser escrita no cabeçalho da página como “última modificação LSN”.
- A trava de página exclusiva é liberada. A página agora pode ser lida por qualquer outro operador. É importante compreender que, até agora, nenhuma gravação em disco ocorreu, pois todas essas modificações foram feitas apenas na memória.
- Antes da operação que fez a modificação, o commit deve gerar um novo registro de log descrevendo o fato para que essa transação seja confirmada e, assim, esse registro de log é anexado ao registro na memória, antes de ser escrito no disco. Por definição, isso incluirá registro de log que descreve as modificações feitas na página. Note que a página foi modificada apenas na memória e não gravada no disco. Neste momento, a transação trava a modificação feita nessa página e garante que será descrita no registro, salva em disco, e o processo de recuperação vai refazer essa operação se necessário.
- Periodicamente, as páginas modificadas no cache do SQL Server são gravadas no disco. Isso é chamado de ‘checkpoint‘.
Há um tipo especial de gravação que ocorre utilizando uma sequência diferente: a gravação minimamente registrada (log mínimo da transação). Apenas as operações que inserem novos dados podem ser minimamente registradas com operações como INSERT e acrescentam um campo blob usando a sintaxe WRITE@value, NULL, …) de UPDATE. Certas condições devem ser cumpridas antes que uma operação minimamente conectada possa ocorrer. Veja Operações que podem ser minimamente registradas e também leia O guia de carregamento de dados de desempenho. A sequência de operações realizadas numa operação minimamente registrada é, aproximadamente, o seguinte:
- O operador que faz uma operação minimamente registrada (o operador de inserção em massa) aloca uma nova página. Veja Gerenciando alocações de extensão e espaço livre para entender como as páginas estão sendo alocadas.
- O operador ‘corrige’ a página em cache e obtém uma trava exclusiva sobre ela.
- Um registro de log é gerado descrevendo o fato de que a página está sendo usada para inserção em massa minimamente registrada. Esse registro de log é anexado ao log da memória e o registro LSN está escrito na página como o último LSN modificado.
- A página é adicionada a uma lista de páginas minimamente registradas associadas com a transação.
- O operador pode agora adicionar quantas linhas quiser na página. Não é preciso gerar qualquer log para descrever cada linha. Só a imagem na memória da página é modificada e não ocorre nenhuma gravação em disco.
- Quando a página enche, uma nova página é atribuída e o processo descrito acima se repete.
- Antes da operação que comprometeu a operação minimamente registrada, todas as páginas modificadas no modo minimamente registrado por essa transação devem ser escritas para o disco. Depois que todas as páginas foram gravadas em disco, um novo registro de log descrevendo o fato de que a transação foi confirmada é gerado e anexado ao log de memória. Todo o log incluindo esse último registro deve ser gravado em disco agora.
- Evite um fenômeno chamado ‘thundering herd’, no qual todas as páginas modificadas pela operação minimamente registrada tentam gravar em disco ao mesmo tempo no final da operação de um processo de gravação, escrevendo o log no disco antes do término da operação em execução.
Operações minimamente registradas ainda são totalmente transacionais, consistentes e duráveis. Até o último momento, a operação pode ser abortada (revertida) ou o servidor pode falhar e o banco de dados é deixado em um estado consistente (após recuperação interna). Tal como com as operações totalmente registradas, uma descrição detalhada desse processo pode ser encontrada em O básico de sobre IO do SQL Server 2000.
DDL
Nem todas as declarações T-SQL são executadas como uma iteração de operadores em uma árvore no plano de execução. Um exemplo típico são as instruções DDL, como CREATE TABLE. Para entender como as declarações DDL trabalham, é importante compreender que o SQL Server armazena todos os metadados sobre qualquer objeto existente nas tabelas internas do banco de dados. Um INSERT nas tabelas do sistema que descrevem as tabelas existentes irá resultar em uma nova tabela que será reconhecida pelo SQL Server. Uma inserção na tabela de sistema que descreve essas colunas irá adicionar uma coluna a uma tabela. Excluir uma linha nessas tabelas de sistema significa que o objeto de tabela ou uma coluna de uma tabela será descartada.
Tudo dentro do banco de dados é descrito por essas tabelas de sistema: há cerca de 80 tabelas de sistema que cobrem objetos, procedimentos, funções, esquemas, usuários, logins, certificados, views, permissões, bancos de dados, arquivos, ou seja, literalmente cada recurso do SQL Server. Portanto, o que a DDL faz é apenas manter essas tabelas do sistema. Uma declaração como CREATE TABLE é inserida em uma linha na tabela de sistema que descreve objetos (tabelas são apenas um dos possíveis tipos de objetos) e algumas linhas nas tabelas do sistema descrevem as colunas e, pronto, sua nova tabela foi “criada” (tenha em mente que estou simplificando a explicação aqui). Enquanto instruções DDL não usam diretamente os operadores que mencionei, elas usam o mesmo código para acessar efetivamente as tabelas do sistema (ler e escrever linhas). Uma instrução DDL não executa o método next(), ou um operador de busca, mas, em vez disso, usa diretamente o código que o operador de busca tinha usado para localizar a linha que deseja na tabela de sistema.
A declaração DDL realiza seu trabalho através da inserção, exclusão ou atualização de linhas nessas tabelas do sistema. Algumas instruções DDL adicionalmente fazem algumas operações fora das tabelas do sistema, por exemplo, quando necessitam criar ou apagar arquivos no disco para o banco de dados, ou quando têm que se conectar com a Windows Clustering API para configurar grupos de disponibilidade. E algumas instruções DDL têm de manipular as tabelas de dados internamente, por exemplo. para preencher os valores padrão de uma nova coluna ou validar se os dados existentes estão em conformidade com a restrição de verificação recém-adicionada.
Backup, restauração e DBCC
Depois de peneirar as instruções de consulta (incluindo DML) e DDL, ficamos com algumas declarações especiais. Operações de backup e restauração ignoram quase tudo o que discutimos até agora. O que o backup e a restauração fazem é apenas uma cópia de um arquivo para outro. O BACKUP lê a partir dos dados e/ou arquivos de log e escreve para o arquivo de backup. A RESTAURAÇÃO lê a partir do arquivo de backup e grava os dados e/ou arquivos de log. Eles precisam fazer algumas tarefas domésticas relacionadas a tabelas de sistema, mas a maior parte do trabalho é simplesmente ler um arquivo e gravar em outro. Ao fazê-lo, ignoramos o cache de dados (o buffer). Quanto às declarações prospectivas DBCC, praticamente cada uma faz algo diferente. Para um exemplo de como DBCC CHECKDB funciona, recomendo a leitura da série de artigos CHECKDB de todos os ângulos.
Como posso usar toda essa informação?
Desenvolvedores que interagem com bancos de dados têm duas principais preocupações: solucionar problemas de desempenho e problemas de perda de dados. Este artigo não vai ajudar muito com a perda de dados, pois se você não tiver um backup, nada vai te salvar, desculpe. Mas espero que a compreensão de como as coisas funcionam irá lançar alguma luz sobre a solução de problemas de desempenho. Depois de entender que o seu cliente está enviando solicitações para o servidor e que do servidor partem as tarefas para cada solicitação, o enigma do desempenho pode ser simplificado: a qualquer momento sua tarefa será executada (consumindo ciclos de CPU) ou estará esperando. E cada vez que ela espera, as informações de espera (o que está esperando e por quanto tempo) serão recolhidas pelas estatísticas internas do SQL Server. Há uma excelente metodologia sobre como aproveitar estas informações de espera coletadas para solucionar os gargalos de desempenho: o whitepaper sobre filas e espera é uma leitura valiosa. Se você seguir um link a partir do muitos aos quais me refiro neste artigo, é melhor que seja este último.
***
Artigo traduzido pela Redação iMasters com autorização do autor. Publicado originalmente em http://rusanu.com/2013/08/01/understanding-how-sql-server-executes-a-query/