Le moyen le plus rapide de vérifier si la table InnoDB a changé

22

Mon application est très gourmande en bases de données. Actuellement, j'utilise MySQL 5.5.19 et j'utilise MyISAM, mais je suis en train de migrer vers InnoDB. Le seul problème qui reste est la performance de la somme de contrôle.

Mon application fait environ 500 à 1 000 CHECKSUM TABLEinstructions par seconde aux heures de pointe, car l'interface graphique du client interroge constamment la base de données pour les changements (c'est un système de surveillance, donc doit être très réactif et rapide).

Avec MyISAM, il existe des sommes de contrôle en direct qui sont précalculées lors de la modification de la table et sont TRÈS rapides. Cependant, il n'y a rien de tel dans InnoDB. Donc, CHECKSUM TABLEc'est TRÈS lent.

J'espérais pouvoir vérifier la dernière heure de mise à jour du tableau, malheureusement, ce n'est pas disponible dans InnoDB non plus. Je suis bloqué maintenant, car les tests ont montré que les performances de l'application chutent considérablement.

Il y a tout simplement trop de lignes de code qui mettent à jour les tables, donc l'implémentation de la logique dans l'application pour enregistrer les modifications de table est hors de question.

Existe-t-il une méthode rapide pour détecter les changements dans les tables InnoDB?

Veste
la source

Réponses:

15

Pour la table mydb.mytable, exécutez cette requête:

SELECT update_time
FROM information_schema.tables
WHERE table_schema='mydb'
AND table_name='mytable';

Si vous voulez savoir quelles tables ont changé au cours des 5 dernières minutes, exécutez ceci:

SELECT table_schema,table_name,update_time
FROM information_schema.tables
WHERE update_time > (NOW() - INTERVAL 5 MINUTE);

Essaie !!!

MISE À JOUR 2011-12-21 20:04 EDT

Mon employeur (société d'hébergement DB / Wweb) a un client avec 112 000 tables InnoDB. Il est très difficile de lire INFORMATION_SCHEMA.TABLES pendant les heures de pointe. J'ai une autre suggestion:

Si innodb_file_per_table est activé et que toutes les tables InnoDB sont stockées dans des .ibdfichiers, il existe un moyen de connaître l'heure de la dernière mise à jour (jusqu'à la minute).

Pour la table mydb.mytable, procédez comme suit dans le système d'exploitation:

$ cd /var/lib/mysql/mydb
$ ls -l mytable.ibd | awk '{print $4,$5}'

Cet horodatage provient du système d'exploitation. Vous ne pouvez pas vous tromper sur celui-ci.

MISE À JOUR 2011-12-21 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Ajoutez ceci à my.cnf, redémarrez mysql et toutes les tables InnoDB subiront des vidages rapides du pool de tampons.

Pour éviter de redémarrer, lancez simplement

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

MISE À JOUR 2013-06-27 07:15 EDT

Lorsqu'il s'agit de récupérer la date et l'heure d'un fichier, ls a la --time-stylepossibilité:

$ cd /var/lib/mysql/mydb
$ ls -l --time-style="+%s" mytable.ibd | awk '{print $6}'

Vous pouvez comparer l'horodatage du fichier avec UNIX_TIMESTAMP (NOW ()) .

RolandoMySQLDBA
la source
Êtes-vous sûr de ne pas pouvoir vous tromper avec le moddate idb? Un changement pourrait être simplement en train de vivre dans le pool de tampons en mémoire et ne pas être encore vidé sur le disque.
atxdba
6
Merci pour la réponse, mais comme je l'ai dit, update_time dans information_schema.tables est NULL pour les tables InnoDB. De plus, je ne suis pas sûr que innodb_max_dirty_pages_pct = 0 soit une bonne idée, car cela sacrifiera les performances ... Je pensais à une solution avec des déclencheurs, pour insérer une valeur aléatoire dans une table de référence pour chacune des tables surveillées, mais ensuite j'aurai besoin de 3 déclencheurs par table uniquement pour cela ...
Veste
La sélection dans information_schema.tables est également un peu lente ... il me faut environ 300 ms pour vérifier une table. À titre de comparaison, faire une "CHECKSUM TABLE" sur une table MyISAM avec des millions de lignes avec Live Checksum activé prend moins d'une milliseconde.
Veste
2
+1 pour la vérification du système de fichiers, tant que le vidage du tampon est assez régulier (environ une fois par seconde est la valeur par défaut), alors cet horodatage sera assez précis, et probablement assez bon pour la plupart des cas ...
Dave Rix
1
Peut-être que c'est OK pour une base de données locale, mais j'ai plusieurs esclaves distants, donc cela ne fonctionne pas ...
Veste
3

Je pense avoir trouvé la solution. Pendant un certain temps, je cherchais à Percona Server pour remplacer mes serveurs MySQL, et maintenant je pense qu'il y a une bonne raison à cela.

Le serveur Percona introduit de nombreuses nouvelles tables INFORMATION_SCHEMA comme INNODB_TABLE_STATS, qui n'est pas disponible dans le serveur MySQL standard. Quand vous faites:

SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'

Vous obtenez le nombre de lignes réel et un compteur. La documentation officielle dit ce qui suit sur ce domaine:

Si la valeur de la colonne modifiée dépasse «lignes / 16» ou 2000000000, le recalcul des statistiques est effectué lorsque innodb_stats_auto_update == 1. Nous pouvons estimer l'ancienneté des statistiques par cette valeur.

Ce compteur encapsule donc de temps en temps, mais vous pouvez faire une somme de contrôle du nombre de lignes et du compteur, puis à chaque modification de la table, vous obtenez une somme de contrôle unique. Par exemple:

SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';

J'allais quand même mettre à niveau mes serveurs vers le serveur Percona, donc cette limite n'est pas un problème pour moi. La gestion de centaines de déclencheurs et l'ajout de champs aux tables est une difficulté majeure pour cette application, car elle est très tardive dans le développement.

Voici la fonction PHP que j'ai trouvée pour m'assurer que les tables peuvent être vérifiées quel que soit le moteur et le serveur utilisés:

function checksum_table($input_tables){
    if(!$input_tables) return false; // Sanity check
    $tables = (is_array($input_tables)) ? $input_tables : array($input_tables); // Make $tables always an array
    $where = "";
    $checksum = "";
    $found_tables = array();
    $tables_indexed = array();
    foreach($tables as $table_name){
        $tables_indexed[$table_name] = true; // Indexed array for faster searching
        if(strstr($table_name,".")){ // If we are passing db.table_name
            $table_name_split = explode(".",$table_name);
            $where .= "(table_schema='".$table_name_split[0]."' AND table_name='".$table_name_split[1]."') OR ";
        }else{
            $where .= "(table_schema=DATABASE() AND table_name='".$table_name."') OR ";
        }
    }
    if($where != ""){ // Sanity check
        $where = substr($where,0,-4); // Remove the last "OR"
        $get_chksum = mysql_query("SELECT table_schema, table_name, rows, modified FROM information_schema.innodb_table_stats WHERE ".$where);
        while($row = mysql_fetch_assoc($get_chksum)){
            if($tables_indexed[$row[table_name]]){ // Not entirely foolproof, but saves some queries like "SELECT DATABASE()" to find out the current database
                $found_tables[$row[table_name]] = true;
            }elseif($tables_indexed[$row[table_schema].".".$row[table_name]]){
                $found_tables[$row[table_schema].".".$row[table_name]] = true;
            }
            $checksum .= "_".$row[rows]."_".$row[modified]."_";
        }
    }

    foreach($tables as $table_name){
        if(!$found_tables[$table_name]){ // Table is not found in information_schema.innodb_table_stats (Probably not InnoDB table or not using Percona Server)
            $get_chksum = mysql_query("CHECKSUM TABLE ".$table_name); // Checksuming the old-fashioned way
            $chksum = mysql_fetch_assoc($get_chksum);
            $checksum .= "_".$chksum[Checksum]."_";
        }
    }

    $checksum = sprintf("%s",crc32($checksum)); // Using crc32 because it's faster than md5(). Must be returned as string to prevent PHPs signed integer problems.

    return $checksum;
}

Vous pouvez l'utiliser comme ceci:

// checksum a signle table in the current db
$checksum = checksum_table("test_table");

// checksum a signle table in db other than the current
$checksum = checksum_table("other_db.test_table");

// checksum multiple tables at once. It's faster when using Percona server, because all tables are checksummed via one select.
$checksum = checksum_table(array("test_table, "other_db.test_table")); 

J'espère que cela évite des ennuis à d'autres personnes ayant le même problème.

Veste
la source
Développement de l'histoire supplémentaire pour ceux qui sont intéressés: forum.percona.com/…
Veste
1

Vous devez mettre à jour vers Mysql v5.6 + dans cette version innodb prend également en charge la table de total de contrôle. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

à part cela, la solution idéale serait si votre client n'interrogeait pas constamment les résultats, mais plutôt que vous poussiez les données nouvelles et modifiées quand et si elles étaient disponibles. Ce serait plus rapide et moins de charge serait sur le serveur. si vous utilisez une interface graphique basée sur le Web, vous devriez consulter APE http://ape-project.org/ ou d'autres projets similaires.

Gamesh
la source
Malheureusement, c'est un tueur de performance. La somme de contrôle est constituée en hachant toutes les lignes une par une . De la documentation: "Ce calcul ligne par ligne est ce que vous obtenez avec la clause EXTENDED, avec InnoDB et tous les autres moteurs de stockage autres que MyISAM, et avec les tables MyISAM non créées avec la clause CHECKSUM = 1" :-(
LSerni
1

Si vous ajoutez principalement à une table, vous pouvez accrocher AUTO_INCREMENT comme mesure de la mise à jour.

SELECT `AUTO_INCREMENT` FROM `information_schema`.`tables` 
WHERE `table_schema` = DATABASE() AND `table_name` = 'YOUR_TABLE';

Mais je préfère faire référence à une source externe comme un compteur dans Memcached que vous incrémenterez chaque fois que vous modifiez quelque chose dans la base de données.

sanmai
la source
0

Vous pouvez essayer de faire ce qui suit:

SELECT rows_changed
FROM information_schema.table_statistics
WHERE table_schema = 'mydb' AND table_name='mytable';

Cela renvoie un nombre qui augmente avec chaque mise à jour de la table, en garder la trace permettra de détecter les changements.

Remarque importante: la valeur est modifiée immédiatement après une MISE À JOUR, pas après COMMIT. Il est donc possible que vous ne voyiez pas les modifications si les modifications ont été apportées dans une autre transaction qui n'a pas abouti.

Romuald Brunet
la source
0

Cette réponse n'a rien à voir avec les versions ou types de base de données mysql, je voulais savoir si les instructions de mise à jour apportaient des modifications ET le faire dans mon code php ..

  1. Création d'une table factice avec un enregistrement et un champ que je voudrais interroger pour obtenir la valeur de current_timestamp de mysql.

  2. À la table de données en cours de mise à jour, ajouté un champ d'horodatage et utilisé l'option mysql "ON UPDATE CURRENT_TIMESTAMP"

  3. Comparé # 1 et # 2

Cela ne fonctionnera pas à 100% du temps mais pour mon application, c'était une solution simple et excellente. J'espère que cela aide quelqu'un

Steve Padgett
la source