Uma rede inteira de websites ficou lenta de uma vez, e nem um servidor estava ocupado.
Uma rede de websites de recrutamento pareceu sobrecarregada toda ao mesmo tempo. Todos os servidores estavam parados. Uma pesquisa de cidade era mais de metade de todo o tempo da base de dados. Eis como a encontrámos e como a corrigimos com um único index online.
Neste artigo 8 secções
- Todos os sites de uma rede de recrutamento abrandaram ao mesmo tempo enquanto os servidores estavam parados, e o instinto foi comprar servidores maiores.
- Todas as camadas estavam paradas exceto uma: os load balancers e os servidores web mal trabalhavam. Toda a quinta de servidores esperava pela base de dados, e a base de dados esperava por uma query.
- Essa query, uma simples pesquisa de cidade, era mais de metade de todo o tempo da base de dados, a percorrer uma tabela inteira em cada chamada para devolver uma única linha.
- Um index online, aplicado sem interromper os sites, transformou o scan num seek. Não foram precisos servidores maiores.
Uma rede de websites de recrutamento amoleceu ao mesmo tempo. Não uma página, não um site: todos, em conjunto, aquele tipo de nevoeiro em toda a quinta de servidores que leva um operador a presumir que os servidores estão a afogar-se e que chegou a hora de comprar maiores. Essa presunção é cara, é comum, e neste caso estava errada. Todas as máquinas da quinta estavam entediadas. Toda a quinta esperava pela base de dados, e a base de dados esperava por uma única query.
Todas as camadas estavam paradas exceto uma
Num abrandamento de toda a quinta, leia a utilização de cada camada antes de qualquer outra coisa, e veja qual está realmente no limite. Os load balancers estavam ao ralenti. Os servidores web estavam entediados. Nada no caminho do pedido trabalhava o suficiente para explicar um site lento.
- Load balancers
- parados
- Servidores web
- mal trabalhavam
- Base de dados
- à espera de uma query
Este é o sinal que encerra a discussão sobre capacidade antes de ela começar. Se todas as camadas estão paradas exceto a base de dados, um servidor web maior compra-lhe uma máquina parada mais rápida. O constrangimento nunca foi a computação, e nenhuma encomenda de hardware o ia corrigir.
A resposta cara era comprar mais máquinas
| O instinto | A medição |
|---|---|
| Toda a quinta está lenta, logo os servidores estão sobrecarregados. Compre maiores. | Todas as camadas exceto a base de dados estavam paradas. A capacidade nunca foi o constrangimento. |
| O QUE OS DADOS DIZIAMTem de ser um problema de hardware. | O hardware estava saudável e comprovadamente: load balancers e servidores web mal trabalhavam. |
| Distribua a carga por mais máquinas. | Uma query era mais de metade de todo o tempo da base de dados. Mais máquinas iam executá-la mais vezes, sempre a fazer scan. |
Mais hardware teria escalado o desperdício, não o teria removido. O único passo seguinte honesto era descobrir em que é que a base de dados estava realmente a gastar o seu tempo.
Agregámos o histórico de queries por impressão digital
Uma aplicação raramente escreve a mesma query da mesma forma duas vezes. A mesma pesquisa aparece parametrizada de várias formas diferentes, pelo que contar o texto bruto das queries esconde o culpado no ruído. A solução é agregar por uma impressão digital que sobreviva a essa inconsistência, e depois ordenar cada statement pela sua quota do tempo total da base de dados. Corremo-lo sobre todo o histórico de queries.
-- reconstructed, de-identified rendering of cleared facts. not a real capture.
-- full query history, grouped by query_hash
-- (a fingerprint that survives inconsistent parameterisation)
SELECT TOP 5
query_hash,
SUM(execution_count) AS calls,
SUM(total_logical_reads) AS pages_read,
SUM(total_elapsed_time) * 100.0
/ SUM(SUM(total_elapsed_time)) OVER () AS pct_of_db_time
FROM sys.dm_exec_query_stats
GROUP BY query_hash
ORDER BY pct_of_db_time DESC;Uma linha sobressaiu: mais de metade de tudo o que a base de dados fazia, gasto num só statement. O método é deliberadamente aborrecido, não adivinha, e é o mesmo todas as vezes.
Uma query era mais de metade da base de dados
O statement que saiu é do tipo que nunca olharia duas vezes. Uma pesquisa de cidade, duas colunas, um filtro.
-- reconstructed, de-identified. table/column names renamed from the real schema.
SELECT CityID, CityName
FROM Cities
WHERE RoutingPath = @p;
-- Execution plan: Clustered Index Scan (a full scan)
-- Returns: ONE row
-- Filtered column: RoutingPath --> no indexA coluna pela qual filtrava não tinha index. Por isso cada chamada percorria a tabela inteira para devolver uma única linha. Por si só isso é um custo pequeno; repetido em cada visualização de página em toda a rede, tornou-se a maior coisa que a base de dados fazia.
Uma resposta de uma linha deve ler duas ou três páginas, não a tabela inteira
“Uma resposta de uma linha deve ler duas ou três páginas, não centenas.”
Essa frase é todo o diagnóstico, comprimido em algo que se pode dizer à pessoa que é dona do código. Nada mais faz uma pesquisa de uma linha ler tanto. Nem disco lento, nem CPU ocupado, nem uma noite má. Uma resposta de uma linha que lê a tabela inteira é um full scan, e um full scan numa coluna filtrada é uma assinatura inconfundível: a coluna precisa de um index. Não precisámos de discutir hardware, limitámo-nos a mostrar as leituras.
Um index online, de um scan para um seek
- Um full scan da tabela, por uma linha
- Nenhum index na coluna filtrada
- Mais de metade de todo o tempo da base de dados
- Um index seek, duas ou três páginas para a mesma linha
- Uma pequena fração do trabalho por chamada
- O index entrou online, sem interromper os sites
-- reconstructed, de-identified. index name illustrative, on the renamed identifiers.
-- Index the column in the WHERE clause; cover the two columns it returns
CREATE NONCLUSTERED INDEX IX_Cities_RoutingPath
ON Cities (RoutingPath)
INCLUDE (CityID, CityName)
WITH (ONLINE = ON); -- applied without interrupting the site
-- New execution plan: Index SeekUm index, na única coluna pela qual a aplicação realmente filtrava. Foi aplicado online, sem interromper os sites, e a pesquisa que tinha dominado a base de dados tornou-se uma das coisas mais baratas que ela fazia.
O engenheiro do cliente acrescentou ao diagnóstico
A melhor parte deste trabalho não foi o nosso index. Foi o que aconteceu quando o próprio engenheiro de bases de dados do cliente se juntou e acrescentou ao diagnóstico: trouxe um padrão de bloqueio reader/writer do lado dele do sistema. Integrámos a correção dele com a nossa, e as duas entraram juntas.
“O engenheiro do cliente saiu da chamada como colaborador.”
Essa é a diferença entre um ticket de suporte e um diagnóstico. Quando o achado é verdadeiro e se mostram as provas, os dois lados deixam de defender posições e começam a melhorar a plataforma juntos.
Quando todas as camadas estão paradas exceto uma, pare de olhar para a capacidade
A lição é curta e é o contrário do reflexo. Lentidão em toda a quinta com máquinas paradas não é um problema de capacidade, é uma questão de software vestida de capacidade. Eis o que verificar antes de assinar uma encomenda de hardware.
Leia primeiro a utilização de cada camada.
Load balancers, web, base de dados. Se tudo está parado exceto uma camada, a capacidade não é o seu problema e um servidor maior não o vai corrigir.
Agregue as estatísticas de queries por impressão digital.
Agrupe o seu histórico por um hash que sobreviva à parametrização inconsistente, para que o mesmo statement escrito de várias formas conte como um. O pior culpado costuma sobressair por larga margem.
Compare páginas lidas com linhas devolvidas.
Uma resposta de uma linha que lê centenas de páginas está a fazer scan, não seek. Essa diferença é a assinatura de um index em falta.
Crie o index na coluna pela qual a aplicação realmente filtra.
Não em todas as colunas. Naquela do WHERE que corre milhões de vezes. Aplique-o online, sem interromper o site.
Ponha em cache as pequenas tabelas quentes.
Tabelas de consulta pequenas e que raramente mudam pertencem à cache, não ao caminho crítico da base de dados em cada visualização de página.
Durante tudo isto, a máquina manteve-se calma. Os load balancers ao ralenti, os servidores web entediados, e a correção foi um index na coluna pela qual a aplicação filtrava. O servidor de base de dados estava saudável; um statement dentro dele precisava de um index.
Respostas diretas
Como pode uma query ser mais de metade da carga de uma base de dados?+
Quando uma query corre com frequência suficiente e faz trabalho a mais de cada vez, domina tudo o resto. Esta corria em cada visualização de página e percorria uma tabela inteira em cada chamada para devolver uma única linha, porque a coluna pela qual filtrava não tinha index. Multiplique um pequeno desperdício por milhões de chamadas e torna-se a base de dados inteira.
Porque é que os servidores estavam parados se os sites estavam lentos?+
Porque o estrangulamento estava dentro da base de dados, à espera de uma query. Os load balancers e os servidores web mal trabalhavam. Toda a quinta esperava pela base de dados, e a base de dados esperava por uma pesquisa que devia ser instantânea. Acrescentar servidores só teria acrescentado mais máquinas paradas.
Como encontram a query que é realmente o problema?+
Agrega-se todo o histórico de queries por uma impressão digital que sobreviva à parametrização inconsistente, e depois ordena-se pela quota do tempo total da base de dados. O método é aborrecido e não adivinha.
Qual é a assinatura de um index em falta?+
Uma resposta de uma linha que lê centenas de páginas. Se uma query devolve uma linha e o plano mostra um scan sobre a tabela inteira, a coluna pela qual filtra não tem index.
A correção exigiu downtime?+
Não. O index entrou online, aplicado sem interromper os sites, e transformou o scan num seek.
Quem o corrigiu, a EuroVPS ou o cliente?+
Ambos, juntos. Nós encontrámos a pesquisa e acrescentámos o index; o próprio engenheiro de bases de dados do cliente juntou-se ao diagnóstico e trouxe um padrão de bloqueio reader/writer do lado dele. Integrámos a correção dele com a nossa.
Site lento, servidores parados? Vamos encontrar a verdadeira causa.
Fale com um engenheiro que lê query plans, não apenas dashboards, sobre o que está realmente a atrasar a sua plataforma.
Fale com um engenheiroO instinto numa quinta lenta é comprar a saída. A resposta honesta é quase sempre mais barata: leia cada camada, encontre a que está no limite, e vá ler as queries dela.
Chegou ao fim.
Ponha a leitura em prática
O que executa?
Fale-nos da carga de trabalho, do tráfego e dos requisitos de recuperação. Comece pelo configurador de servidores ou discuta os detalhes com um engenheiro.
Escrito pelos engenheiros que constroem e operam a infraestrutura que descreve.Fale com um engenheiro
