web-dev-qa-db-fra.com

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

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.

Ma demande fait environ 500-1000 CHECKSUM TABLE instructions par seconde en période 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 TABLE 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?

22
Jacket

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. Le Documentation officielle dit ce qui suit à propos de ce champ:

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.

Donc, ce compteur encapsule 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.

Il s'agit de la fonction PHP que j'ai mise au point 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.

3
Jacket

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 .ibd fichiers, 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 le --time-style option:

$ 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 ()) .

15
RolandoMySQLDBA

Vous devez mettre à jour vers Mysql v5.6 + à cette version innodb prend également en charge la table de somme 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 regarder APE http://ape-project.org/ ou d'autres projets similaires.

1
Gamesh

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.

1
sanmai

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

0
Steve Padgett

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.

0
Romuald Brunet