Sanırım çözümü buldum. Bir süredir MySQL sunucularımı değiştirmek için Percona Sunucusuna bakıyordum ve şimdi bunun iyi bir nedeni olduğunu düşünüyorum.
Percona sunucusu, standart MySQL sunucusunda bulunmayan INNODB_TABLE_STATS gibi birçok yeni INFORMATION_SCHEMA tablosu sunar. Ne zaman yaparsın:
SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'
Gerçek satır sayısı ve bir sayaç olsun. Resmi belgeler bu alan hakkında şunları söylüyor:
Eğer değiştirilen sütunun değeri “satır / 16” veya 2000000000 değerini geçerse, istatistik yeniden hesaplama işlemi innodb_stats_auto_update == 1. olduğunda, istatistiklerin eskiliğini bu değerle tahmin edebiliriz.
Böylece bu sayaç her seferinde bir sarar, ancak satır sayısını ve sayıcıyı sağlama toplamı yapabilir ve ardından tablonun her değiştirilmesinde benzersiz bir sağlama toplamı elde edersiniz. Örneğin:
SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';
Zaten sunucularımı Percona sunucusuna yükseltecektim, bu yüzden bu sınırlama benim için bir sorun değil. Yüzlerce tetikleyiciyi yönetmek ve tablolara alan eklemek, bu uygulama için büyük bir acıdır, çünkü geliştirme aşamasında çok geç kalmıştır.
Bu, hangi motor ve sunucu kullanılırsa kullanılsın, tabloların kontrol edilememesini sağlamak için elde ettiğim PHP işlevidir:
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;
}
Bu şekilde kullanabilirsiniz:
// 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"));
Umarım bu aynı problemi yaşayan diğer insanlara biraz bela kazandırır.