web-dev-qa-db-fra.com

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

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:

enter image description here

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 exécuté ANALYZE TABLE data_302, 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 que select count(*) avec 34906061 lignes. Est-ce que l'analyse de la table revient immédiatement et est traitée 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 juste l'id le plus élevé avec SELECT id from table_name order by id DESC limit 1 Et j'espère que mes tables ne seront pas trop fragmentées.

16
Programster

Il existe différentes façons de "compter" les lignes d'une table. 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 des clauses where et group by Variables, etc.)

  • a) la manière normale. Il suffit de compter eux.

    select count(*) as table_rows from table_name ; 
    

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

  • b) en utilisant SQL_CALC_FOUND_ROWS et 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 quelques 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: le même que le précédent.
    Efficacité: identique à la précédente.

  • c) en utilisant les tables information_schema, 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 TABLE Plus souvent.
    Efficacité: Très bien, il ne touche pas du tout à la table.

  • d) stocker le nombre dans la base de données (dans une autre, table "compteur") et mettre à jour cette valeur à chaque fois que la table a un insert, supprimer ou tronquer (cela peut soit avec des déclencheurs, soit en modifiant les procédures d'insertion et de suppression).
    Cela ajoutera bien sûr une charge supplémentaire dans chaque insert et supprimera mais fournira un décompte précis.

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

  • e) stocker (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 décomptes, utilisez la valeur mise en cache.

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

24
ypercubeᵀᴹ

Pour INNODB vous voulez information_schema.INNODB_SYS_TABLESTATS.NUM_ROWS pour des 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: https://stackoverflow.com/questions/33383877/why-does-information-schema-tables-give-such-an-unstable-answer-for-number-of-ro/ 49184843 # 4918484

1
Rob Bradshaw