MySQL - Différence entre l'utilisation de count (*) et information_schema.tables pour compter les lignes


16

Je veux un moyen rapide de compter le nombre de lignes de ma table qui compte plusieurs millions de lignes. J'ai trouvé le post " MySQL: le moyen le plus rapide de compter le nombre de lignes " sur Stack Overflow, qui semblait résoudre mon problème. Bayuah a fourni cette réponse:

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

Ce que j'ai aimé car cela ressemble à une recherche au lieu d'une analyse, donc ça devrait être rapide, mais j'ai décidé de le tester

SELECT COUNT(*) FROM table 

pour voir combien il y avait une différence de performance.

Malheureusement, je reçois des réponses différentes comme indiqué ci-dessous:

entrez la description de l'image ici

Question

Pourquoi les réponses diffèrent-elles d'environ 2 millions de lignes? Je suppose que la requête qui effectue une analyse complète de la table est le numéro le plus précis, mais existe-t-il un moyen d'obtenir le numéro correct sans avoir à exécuter cette requête lente?


J'ai couru ANALYZE TABLE data_302, ce qui s'est terminé en 0,05 seconde. Lorsque j'ai exécuté à nouveau la requête, j'obtiens maintenant un résultat beaucoup plus proche de 34384599 lignes, mais ce n'est toujours pas le même nombre select count(*)qu'avec 34906061 lignes. Est-ce que l'analyse de la table revient immédiatement et se déroule en arrière-plan? Je pense qu'il vaut la peine de mentionner qu'il s'agit d'une base de données de test et n'est pas en cours d'écriture.

Personne ne s'en souciera s'il s'agit simplement de dire à quelqu'un la taille d'une table, mais je voulais passer le nombre de lignes à un peu de code qui utiliserait ce chiffre pour créer des requêtes asynchrones de «taille égale» pour interroger la base de données en parallèle, similaire à la méthode indiquée dans Augmentation des performances de requête lente avec l'exécution de requête parallèle par Alexander Rubin. En l'état, j'obtiendrai simplement l'identifiant le plus élevé avec SELECT id from table_name order by id DESC limit 1et j'espère que mes tables ne seront pas trop fragmentées.

Réponses:


23

Il existe différentes façons de «compter» les lignes d'un tableau. Ce qui est le mieux dépend des exigences (précision du comptage, à quelle fréquence est effectuée, si nous avons besoin du comptage de la table entière ou avec une variable whereet des group byclauses, etc.)

  • a) la voie normale. Il suffit de les compter .

    select count(*) as table_rows from table_name ; 

    Précision : compte précis à 100% au moment de l'exécution de la requête.
    Efficacité : Pas bon pour les grandes tables. (pour les tables MyISAM est spectaculairement rapide mais personne n'utilise MyISAM de nos jours car il présente de nombreux inconvénients par rapport à InnoDB. Le "spectaculairement rapide" ne s'applique également qu'au comptage des lignes d'une table MyISAM entière - si la requête a une WHEREcondition, elle doit encore analyser la table ou un index.)
    Pour les tables InnoDB, cela dépend de la taille de la table, car le moteur doit analyser la table entière ou un index entier pour obtenir le nombre exact. Plus la table est grande, plus elle devient lente.

  • b) en utilisant SQL_CALC_FOUND_ROWSet FOUND_ROWS(). Peut être utilisé à la place de la manière précédente, si nous voulons également un petit nombre de lignes (en changeant le LIMIT). Je l'ai vu utilisé pour la pagination (pour obtenir des lignes et en même temps savoir combien sont au total et calculer le nombre de pgegs).

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

    Précision : la même que la précédente.
    Efficacité : identique à la précédente.

  • c) en utilisant les information_schematableaux, comme question liée:

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

    Précision : seulement une approximation. Si la table est la cible d'insertions et de suppressions fréquentes, le résultat peut être loin du compte réel. Cela peut être amélioré en exécutant ANALYZE TABLEplus souvent.
    Efficacité : très bonne, elle ne touche pas du tout à la table.

  • d) stocker le comptage dans la base de données (dans une autre table "compteur" ) et mettre à jour cette valeur chaque fois que la table a une insertion, une suppression ou une troncature (ceci peut être réalisé avec des déclencheurs ou en modifiant les procédures d'insertion et de suppression) .
    Bien entendu, cela ajoutera une charge supplémentaire à chaque insert et supprimera, mais fournira un décompte précis.

    Précision : compte précis à 100%.
    Efficacité : très bonne, ne doit lire qu'une seule ligne d'une autre table.
    Il met cependant une charge supplémentaire sur la base de données.

  • e) stocker (mettre en cache ) le nombre dans la couche application - et utiliser la 1ère méthode (ou une combinaison des méthodes précédentes). Exemple: exécutez la requête de comptage exact toutes les 10 minutes. Dans l'intervalle moyen entre deux comptes, utilisez la valeur mise en cache.

    Précision : approximation mais pas trop mauvaise dans des circonstances normales (sauf lorsque des milliers de lignes sont ajoutées ou supprimées).
    Efficacité : très bonne, la valeur est toujours disponible.


1

Car INNODBvous voulez information_schema.INNODB_SYS_TABLESTATS.NUM_ROWSdes données précises sur le nombre de lignes du tableau, au lieu de information_schema.TABLES.TABLE_ROWS.

J'ai posté plus de détails ici: /programming/33383877/why-does-information-schema-tables-give-such-an-unstable-answer-for-number-of-ro/49184843#49184843


1
Informations erronées ... "Pour INNODB, vous voulez information_schema.INNODB_SYS_TABLESTATS.NUM_ROWS pour une ligne de tableau précise:" le manuel indique clairement estimé sur la NUM_ROWScolonne
Raymond Nijland
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.