2011-12-27 131 views
5

我的應用程序非常密集。目前,我正在運行MySQL 5.5.19並使用MyISAM,但我正在遷移到InnoDB。唯一的問題是校驗和性能。MySQL - 檢查InnoDB表中的數據是否發生變化的最快方法

我的應用程序在高峯時間每秒鐘處理大約500-1000個「CHECKSUM TABLE」語句,因爲客戶端GUI不斷輪詢數據庫以進行更改(這是一個監控系統,因此必須非常快速響應)。

使用MyISAM,可以在表格修改中預先計算實時校驗和,速度非常快。但是,InnoDB中沒有這樣的東西。所以,CHECKSUM TABLE非常慢...

我希望能夠檢查表的最後更新時間,不幸的是,這在InnoDB中也是不可用的。我現在被卡住了,因爲測試顯示應用程序的性能急劇下降...

只是有太多的代碼行更新表,所以在應用程序中實現記錄表更改的邏輯的問題...

數據庫生態系統由一個主節點na 3個從節點組成,因此本地文件檢查不是一個選項。 我想到了一種模擬校驗和緩存的方法 - 一個包含兩列的查找表 - table_name,校驗和,並在發生表更改時使用觸發器更新該表,但我有大約100個表來監視,這意味着每個觸發器有3個觸發器表= 300觸發器。很難保持,我不確定這不會再次成爲表演豬。

那麼是否有任何FAST方法來檢測InnoDB表中的變化?

謝謝!

+0

重複:http://dba.stackexchange.com/questions/9569/fastest-way-to-check-if-innodb-table-has-changed – 2011-12-27 09:35:42

+0

你可能想看看[這個問題/答案]( http://stackoverflow.com/questions/2785429/how-can-i-determine-when-an-innodb-table-was-last-changed)(如果你還沒有這樣做)。 – 2011-12-27 09:36:18

+0

啊,好吧,這也是我的線索,但我不認爲這是同一個網站......對不起,我是新來的。 – Jacket 2011-12-27 09:37:56

回答

0

我想我已經找到了解決辦法。一段時間以來,我一直在尋找Percona Server來替換我的MySQL服務器,現在我認爲這是一個很好的理由。

Percona服務器引入了許多新的INFORMATION_SCHEMA表,如INNODB_TABLE_STATS,這在標準MySQL服務器中是不可用的。 當你這樣做:

SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table' 

你得到實際的行數和計數器。該Official documentation說,有關這一領域的以下內容:

如果修改列的值超過「行/ 16」或20億,當innodb_stats_auto_update == 1 我們可以估算的故鄉的的 統計重新計算完成按此值統計。

所以這個計數器每隔一段時間就會包裝一次,但是您可以對行數和計數器進行校驗和,然後在每次修改表格時都會得到一個唯一的校驗和。例如: -

SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'; 

我會盡我的服務器升級到Percona的服務器反正所以這個邊界不是我的問題。管理數百個觸發器並向表中添加字段對於此應用程序來說是一大痛苦,因爲它在開發中已經很晚了。

這是PHP函數,我拿出來確保表可以校驗和任何引擎和服務器使用:

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; 
} 

您可以使用它像這樣:

// 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")); 

我希望這可以爲遇到同樣問題的其他人節省一些麻煩。

+0

爲感興趣的人們進一步開發故事:http://forum.percona.com/index.php?t=msg&th=2060&goto=7785& – Jacket 2012-01-14 08:00:33

2

最簡單的方法是添加一個類型爲TIMESTAMP的可空列,並帶有觸發器:ON UPDATE CURRENT_TIMESTAMP

因此,刀片將不會改變,因爲列接受空值,你可以說只選擇新的變化列:

SELECT * FROM `table` WHERE `mdate` > '2011-12-21 12:31:22' 

每次更新的行此列會自動更改。

這裏有一些更多的信息:http://dev.mysql.com/doc/refman/5.0/en/timestamp.html

查看已刪除的行簡單地創建這是要刪除的每個登錄到另一個表的觸發器:

DELIMITER $$ 
CREATE TRIGGER MyTable_Trigger 
AFTER DELETE ON MyTable 
FOR EACH ROW 
BEGIN 
    INSERT INTO MyTable_Deleted VALUES(OLD.id, NOW()); 
END$$ 
+2

同意但這樣做的麻煩是我無法從表中檢測刪除(除最後一行外)。 – Jacket 2011-12-27 20:33:49

+0

看看觸發器後刪除(回答編輯上面)。 – Cleankod 2011-12-28 10:58:05

相關問題