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.