MySQL - Diferença entre usar count (*) e information_schema.tables para contar linhas

16

Quero uma maneira rápida de contar o número de linhas na minha tabela que possui vários milhões de linhas. Encontrei o post " MySQL: A maneira mais rápida de contar o número de linhas " no Stack Overflow, que parecia resolver o meu problema. Bayuah forneceu esta resposta:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

O que eu gostei porque se parece com uma pesquisa em vez de uma verificação, por isso deve ser rápido, mas decidi testá-lo

SELECT COUNT(*) FROM table 

para ver quanta diferença de desempenho havia.

Infelizmente, estou recebendo respostas diferentes, como mostrado abaixo:

insira a descrição da imagem aqui

Questão

Por que as respostas são diferentes em aproximadamente 2 milhões de linhas? Suponho que a consulta que realiza uma verificação completa da tabela seja o número mais preciso, mas existe uma maneira de obter o número correto sem precisar executar essa consulta lenta?


Eu corri ANALYZE TABLE data_302, que foi concluído em 0,05 segundos. Quando executei a consulta novamente, agora obtive um resultado muito mais próximo de 34384599 linhas, mas ainda não é o mesmo número select count(*)das 34906061 linhas. A tabela de análise retorna imediatamente e processa em segundo plano? Eu sinto que vale a pena mencionar que este é um banco de dados de teste e no momento não está sendo gravado.

Ninguém vai se importar se é apenas um caso de dizer a alguém o tamanho de uma tabela, mas eu queria passar a contagem de linhas para um pouco de código que usaria essa figura para criar consultas assíncronas de "mesmo tamanho" para consultar o banco de dados em paralelo, semelhante ao método mostrado em Aumentando o desempenho lento da consulta com a execução da consulta paralela por Alexander Rubin. Sendo assim, vou obter o ID mais alto SELECT id from table_name order by id DESC limit 1e espero que minhas tabelas não fiquem muito fragmentadas.

Programador
fonte

Respostas:

23

Existem várias maneiras de "contar" linhas em uma tabela. O que é melhor depende dos requisitos (precisão da contagem, com que frequência é executada, se precisamos contar a tabela inteira ou com variáveis wheree group bycláusulas etc.)

  • a) o caminho normal. Apenas conte- os.

    select count(*) as table_rows from table_name ; 

    Precisão : contagem 100% precisa no momento da execução da consulta.
    Eficiência : Não é bom para mesas grandes. (para tabelas MyISAM é espetacularmente rápido, mas ninguém está usando o MyISAM atualmente, pois possui muitas desvantagens sobre o InnoDB. O "espetacularmente rápido" também se aplica apenas ao contar as linhas de uma tabela MyISAM inteira - se a consulta tiver uma WHEREcondição, ainda é necessário varrer a tabela ou um índice.)
    Para tabelas do InnoDB, isso depende do tamanho da tabela, pois o mecanismo precisa fazer a varredura de toda a tabela ou de um índice inteiro para obter a contagem precisa. Quanto maior a mesa, mais lento fica.

  • b) usando SQL_CALC_FOUND_ROWSe FOUND_ROWS(). Pode ser usado em vez da maneira anterior, se também queremos um pequeno número de linhas (alterando o LIMIT). Já o vi usado para paginação (para obter algumas linhas e ao mesmo tempo saber quantas são totais int e calcular o número de pgegs).

    select sql_calc_found_rows * from table_name limit 0 ; 
    select found_rows() as table_rows ;

    Precisão : o mesmo que o anterior.
    Eficiência : o mesmo que o anterior.

  • c) usando as information_schematabelas, como a questão vinculada:

    select  table_rows
    from    information_schema.tables
    where   table_schema = 'database_name'
      and   table_name = 'table_name' ;

    Precisão : apenas uma aproximação. Se a tabela for alvo de inserções e exclusões frequentes, o resultado poderá estar muito longe da contagem real. Isso pode ser aprimorado executando com ANALYZE TABLEmais frequência.
    Eficiência : Muito bom, não toca em nada na mesa.

  • d) armazenar a contagem no banco de dados (em outra tabela "contador" ) e atualizar esse valor sempre que a tabela tiver uma inserção, exclusão ou truncamento (isso pode ser alcançado com gatilhos ou com a modificação dos procedimentos de inserção e exclusão) .
    Obviamente, isso colocará uma carga adicional em cada inserção e exclusão, mas fornecerá uma contagem precisa.

    Precisão : contagem 100% precisa.
    Eficiência : Muito bom, precisa ler apenas uma linha de outra tabela.
    No entanto, coloca uma carga adicional no banco de dados.

  • e) armazenar (armazenar em cache ) a contagem na camada de aplicação - e usando o 1º método (ou uma combinação dos métodos anteriores). Exemplo: execute a consulta de contagem exata a cada 10 minutos. Nesse meio tempo entre duas contagens, use o valor em cache.

    Precisão : aproximação, mas não muito ruim em circunstâncias normais (a menos que quando milhares de linhas forem adicionadas ou excluídas).
    Eficiência : Muito bom, o valor está sempre disponível.

ypercubeᵀᴹ
fonte
1

Por INNODBque você quer information_schema.INNODB_SYS_TABLESTATS.NUM_ROWSpara dados de contagem de linha da tabela precisa, em vez de information_schema.TABLES.TABLE_ROWS.

Publiquei mais detalhes aqui: /programming/33383877/why-does-information-schema-tables-give-such-an-unstable-answer-for-number-of-ro/49184843#49184843

Rob Bradshaw
fonte
1
Informação errada ... "Para INNODB você quer information_schema.INNODB_SYS_TABLESTATS.NUM_ROWS para linha de tabela precisa:" o manual diz claramente estimado no NUM_ROWScolum
Raymond Nijland