Tôi nghĩ rằng tôi đã tìm thấy giải pháp. Trong một thời gian, tôi đã tìm kiếm Percona Server để thay thế các máy chủ MySQL của mình và bây giờ tôi nghĩ có một lý do chính đáng cho việc này.
Máy chủ Percona giới thiệu nhiều bảng Information_SCHema mới như INNODB_TABLE_STATS, không có sẵn trong máy chủ MySQL tiêu chuẩn. Khi bạn làm:
SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'
Bạn nhận được số hàng thực tế và một quầy. Các tài liệu chính thức cho biết sau đây về lĩnh vực này:
Nếu giá trị của cột được sửa đổi vượt quá hàng Hàng / 16, hoặc 2000000000, việc tính toán lại số liệu thống kê được thực hiện khi innodb_stats_auto_update == 1. Chúng tôi có thể ước tính mức độ cũ của số liệu thống kê theo giá trị này.
Vì vậy, bộ đếm này thỉnh thoảng kết thúc, nhưng bạn có thể thực hiện tổng kiểm tra số lượng hàng và bộ đếm, và sau đó với mỗi lần sửa đổi bảng bạn sẽ có một tổng kiểm tra duy nhất. Ví dụ:
SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';
Tôi sẽ nâng cấp máy chủ của mình lên máy chủ Percona vì vậy giới hạn này không phải là vấn đề đối với tôi. Quản lý hàng trăm trình kích hoạt và thêm các trường vào bảng là một nỗi đau lớn cho ứng dụng này, vì nó rất muộn trong quá trình phát triển.
Đây là chức năng PHP mà tôi đã đưa ra để đảm bảo rằng các bảng có thể được kiểm tra lại bất cứ công cụ và máy chủ nào được sử dụng:
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;
}
Bạn có thể sử dụng nó như thế này:
// 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"));
Tôi hy vọng điều này sẽ cứu một số rắc rối cho những người khác có cùng vấn đề.