Saya pikir saya sudah menemukan solusinya. Untuk beberapa waktu saya melihat Percona Server untuk mengganti server MySQL saya, dan sekarang saya pikir ada alasan bagus untuk ini.
Server Percona memperkenalkan banyak tabel INFORMATION_SCHEMA baru seperti INNODB_TABLE_STATS, yang tidak tersedia di server MySQL standar. Saat kamu melakukan:
SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'
Anda mendapatkan jumlah baris aktual dan penghitung. The dokumentasi resmi mengatakan berikut tentang bidang ini:
Jika nilai kolom yang dimodifikasi melebihi "rows / 16" atau 2000000000, perhitungan ulang statistik dilakukan ketika innodb_stats_auto_update == 1. Kita dapat memperkirakan usia statistik dengan nilai ini.
Jadi penghitung ini membungkus sesekali, tetapi Anda dapat membuat checksum dari jumlah baris dan penghitung, dan kemudian dengan setiap modifikasi tabel Anda mendapatkan checksum yang unik. Misalnya:
SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';
Saya akan melakukan upgrade server saya ke server Percona jadi ini tidak masalah bagi saya. Mengelola ratusan pemicu dan menambahkan bidang ke tabel adalah masalah besar untuk aplikasi ini, karena sangat terlambat dalam pengembangan.
Ini adalah fungsi PHP yang saya buat untuk memastikan bahwa tabel dapat dicek dengan mesin apa pun dan server yang digunakan:
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;
}
Anda bisa menggunakannya seperti ini:
// 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"));
Saya harap ini menghemat masalah bagi orang lain yang memiliki masalah yang sama.