Todo mundo sabe que, em tabelas que usam o InnoDB como motor, consultas do tipo SELECT COUNT(*) FROM mytable
são muito inexatas e muito lentas, principalmente quando a tabela fica maior e há constantes inserções/exclusões de linhas enquanto a consulta é executada.
Pelo que entendi, o InnoDB não armazena a contagem de linhas em uma variável interna, que é o motivo desse problema.
Minha pergunta é: Por que isso acontece? Seria tão difícil armazenar tais informações? É uma informação importante saber em muitas situações. A única dificuldade que vejo se tal contagem interna seria implementada é quando as transações estão envolvidas: se a transação não for confirmada, você conta as linhas inseridas por ela ou não?
PS: Não sou especialista em DBs, sou apenas alguém que tem o MySQL como um simples hobby. Portanto, se acabei de perguntar algo estúpido, não seja excessivamente crítico: D .
Para iniciantes, não existe a 'contagem atual' para armazenar em uma variável. Uma consulta como
SELECT COUNT(*) FROM ...
está sujeita ao nível de isolamento atual e a todas as transações pendentes simultâneas. Dependendo do nível de isolamento, a consulta pode ver ou não linhas inseridas ou excluídas por transações pendentes não confirmadas. A única maneira de responder é contar as linhas visíveis para a transação atual.Observe que nem toquei no assunto ainda mais espinhoso das transações simultâneas que começam ou terminam durante a contagem. Sem falar nos retrocessos...
Eu concordo com @RemusRusanu (+1 para sua resposta)
SELECT COUNT(*) FROM mydb.mytable
no InnoDB se comporta como um mecanismo de armazenamento transacional deveria. Compare-o com o MyISAM.MyISAMGenericName
Se
mydb.mytable
for uma tabela MyISAM, iniciarSELECT COUNT(*) FROM mydb.mytable;
é como executar arquivosSELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';
. Isso aciona uma pesquisa rápida da contagem de linhas no cabeçalho da tabela MyISAM.InnoDBGenericName
Se
mydb.mytable
for uma tabela InnoDB, você obtém uma miscelânea de coisas acontecendo. Você tem MVCC acontecendo, governando o seguinte:Pedir ao InnoDB uma contagem de tabelas requer navegação por essas coisas sinistras. Na verdade, nunca se sabe se
SELECT COUNT(*) from mydb.mytable
conta apenas leituras repetíveis ou inclui leituras que foram confirmadas e aquelas que não foram confirmadas.Você pode tentar estabilizar um pouco as coisas habilitando innodb_stats_on_metadata .
De acordo com a documentação do MySQL em innodb_stats_on_meta_data
Desativá-lo pode ou não fornecer uma contagem mais estável em termos de configuração de planos EXPLAIN. Isso pode afetar o desempenho de
SELECT COUNT(*) from mydb.mytable
uma maneira boa, ruim ou não. Experimente e veja!!!Embora teoricamente seja possível manter uma contagem precisa do número de linhas para uma determinada tabela com o InnoDB, isso custaria muitos bloqueios, o que afetaria negativamente o desempenho. Também seria diferente com base no nível de isolamento.
O MyISAM já faz bloqueio de nível de tabela, então não há custo extra lá.
Eu raramente exijo uma contagem de linhas para uma tabela, embora eu use COUNT(*) bastante. Eu geralmente tenho uma cláusula WHERE anexada. Usando um índice eficiente em um pequeno conjunto de resultados, acho que eles são rápidos o suficiente.
Discordo que as contagens sejam imprecisas. As contagens representam um instantâneo dos dados e sempre as achei exatas.
Resumindo, o MySQL deixa que você implemente isso para o InnoDB. Você pode armazenar uma contagem e incrementá-la/diminuí-la após cada consulta. Porém, a solução mais fácil é provavelmente mudar para MyISAM.