Problemas de desempenho máximo de grupo do MySQL em uma tabela de um milhão de linhas
-
13-12-2019 - |
Pergunta
Estou tentando encontrar uma maneira direta de melhorar o desempenho de fóruns muito ativos, onde há um grande número de postagens e o MySQL não consegue mais classificar tabelas na memória e não parece aproveitar ao máximo os índices.
Esta consulta simples encontra a postagem mais recente em cada tópico para um usuário determinar se ele tem alguma resposta desde então (comparando posteriormente o topic_time)
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p
WHERE p.poster_id = '1' AND p.post_status = '0'
GROUP BY p.topic_id
ORDER BY post_time DESC
LIMIT 50
mesa simples e plana se parece com
post_id | poster_id | topic_id | post_status | post_time | post_text
No entanto, seu desempenho cai quando há um milhão de postagens e o próprio usuário tem dezenas de milhares de postagens.O MySQL não consegue mais classificar a tabela na memória ou há muitas linhas para verificar.Pode levar até 3 segundos no uso no mundo real, o que é inaceitável porque está aumentando a CPU durante esse tempo e deixando todos os outros mais lentos.
Posso fazer qualquer combinação de índice, é claro, mas o mysql parece gostar principalmente de usar uma combinação de
poster_id + post_time
Portanto, ele apenas seleciona as 50 mil postagens de um usuário entre um milhão e começa a agrupar por topic_id e classificar.Estranhamente, adicionar topic_id ao mix de índices não parece ajudar no desempenho, embora possa ser a ordem dos campos de índice.
Tentei escrever um JOIN equivalente para poder usar mais de um índice, mas tive problemas com o fato de que cada lado precisa ser filtrado por post_status e poster.
Eu estava pensando que seria mais rápido, pelo menos nas primeiras páginas, se o mysql pudesse ser feito para PRIMEIRO classificar os dados através de seu índice por post_time e então começar a escolher o topic_id distinto para o usuário em ordem decrescente.Acho que isso exigiria uma subconsulta e não tenho certeza se uma subconsulta de resultado de 50k seria melhor, ainda precisa de uma tabela temporária.
É claro que uma solução fundamental seria aumentar o design principal para que houvesse outra tabela que apenas armazenasse o post_time máximo para cada usuário em cada tópico, mas isso é uma mudança muito grande, a menos que nenhuma outra solução possa ser encontrada.
Obrigado por qualquer sugestão!
adicionando um exemplo do mundo real e EXPLAIN:
registro lento
# Query_time: 2.751334 Lock_time: 0.000056 Rows_sent: 40 Rows_examined: 48286
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p WHERE p.poster_id = '2' AND p.post_status = '0' GROUP BY p.topic_id ORDER BY post_time DESC LIMIT 7000, 40;
explicar
select_type table type possible_keys key key_len ref rows Extra
SIMPLE p ref poster_time poster_time 4 const 27072 Using where; Using temporary; Using filesort
Solução
Primeiro, corrija sua consulta para fornecer resultados determinados:
SELECT p.topic_id,
MAX(post_time) as post_time
FROM forum_posts AS p
WHERE p.poster_id = '1' AND p.post_status = '0'
GROUP BY p.topic_id
ORDER BY post_time DESC
LIMIT 50 ;
Então tente depois de adicionar um índice em (post_status, poster_id, topic_id, post_time)
.