2011-12-27 26 views
5

Ứng dụng của tôi có rất nhiều cơ sở dữ liệu không. Hiện tại, tôi đang chạy MySQL 5.5.19 và sử dụng MyISAM, nhưng tôi đang trong quá trình chuyển sang InnoDB. Vấn đề duy nhất còn lại là hiệu suất kiểm tra.MySQL - Cách nhanh nhất để kiểm tra xem dữ liệu trong bảng InnoDB đã thay đổi

Ứng dụng của tôi có khoảng 500-1000 câu lệnh "CHECKSUM TABLE" mỗi giây trong thời gian cao điểm, vì GUI khách hàng đang bỏ phiếu cho cơ sở dữ liệu liên tục thay đổi (nó là hệ thống giám sát, vì vậy phải rất nhạy và nhanh).

Với MyISAM, có tổng kiểm tra trực tiếp được tính toán trước khi sửa đổi bảng và rất nhanh. Tuy nhiên, không có điều như vậy trong InnoDB. Vì vậy, CHECKSUM TABLE rất chậm ...

Tôi hy vọng có thể kiểm tra thời gian cập nhật cuối cùng của bảng, Thật không may, điều này cũng không có trong InnoDB. Tôi bị kẹt ngay bây giờ, bởi vì các thử nghiệm đã cho thấy hiệu suất của ứng dụng giảm mạnh ...

Chỉ đơn giản là có quá nhiều dòng mã cập nhật các bảng, vì vậy việc thực hiện logic trong ứng dụng để ghi lại các thay đổi bảng của câu hỏi ...

Hệ sinh thái cơ sở dữ liệu bao gồm một tổng thể 3 nô lệ chính, do đó việc kiểm tra tệp cục bộ không phải là một tùy chọn. Tôi đã nghĩ về một phương pháp bắt chước bộ nhớ cache kiểm tra - bảng tra cứu có hai cột - table_name, checksum và cập nhật bảng đó khi các thay đổi trong bảng xảy ra, nhưng tôi có khoảng 100 bảng để theo dõi và điều này có nghĩa là 3 trình kích hoạt mỗi table = 300 trigger. Khó duy trì, và tôi không chắc chắn rằng điều này sẽ không phải là một hog hiệu suất một lần nữa.

Vì vậy, có phương pháp FAST nào để phát hiện các thay đổi trong bảng InnoDB không?

Cảm ơn!

+0

trùng lặp: http://dba.stackexchange.com/questions/9569/fastest-way-to-check-if-innodb-table-has-changed –

+0

Bạn có thể muốn xem [câu hỏi/câu trả lời này] (http://stackoverflow.com/questions/2785429/how-can-i-determine-when-an-innodb-table-was-last-changed) (nếu bạn chưa làm điều đó). –

+0

Ah, OK, đây là chủ đề của tôi, nhưng tôi không nghĩ rằng đây là trang web tương tự ... xin lỗi, tôi mới ở đây. – Jacket

Trả lời

0

Tôi nghĩ rằng tôi đã tìm thấy giải pháp. Đối với một số thời gian tôi đã nhìn vào máy chủ Percona để thay thế máy chủ MySQL của tôi, và bây giờ tôi nghĩ rằng 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 chuẩn. Khi bạn thực hiện:

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

Bạn nhận số đếm thực tế và bộ đếm.Các Official documentation nói như sau về lĩnh vực này:

Nếu giá trị của cột được sửa đổi vượt “hàng/16” hoặc 2000000000, các thống kê tính toán lại được thực hiện khi innodb_stats_auto_update == 1. Chúng tôi có thể ước tính oldness của thống kê theo giá trị này.

Vì vậy, bộ đếm này sẽ kết thúc mọi lúc, nhưng bạn có thể kiểm tra số lượng hàng và bộ đếm, và sau đó với mọi sửa đổi của bảng, bạn sẽ có một lần 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 chậm phát triển.

Đây là chức năng PHP tôi đã đi lên với để đảm bảo rằng bảng có thể được bất cứ điều gì checksummed động cơ và máy chủ đượ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ẽ tiết kiệm một số rắc rối cho những người khác có cùng một vấn đề.

+0

Phát triển thêm câu chuyện cho những người quan tâm: http://forum.percona.com/index.php?t=msg&th=2060&goto=7785& – Jacket

2

Cách đơn giản nhất là thêm cột có thể có giá trị bằng loại TIMESTAMP, với trình kích hoạt: TRÊN CẬP NHẬT CURRENT_TIMESTAMP.

Do đó, chèn sẽ không thay đổi vì cột chấp nhận null, và bạn có thể chọn chỉ cột mới và thay đổi bằng cách nói:

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

Mỗi khi bạn cập nhật một hàng cột này sẽ thay đổi tự động.

Dưới đây là một số thông tin hơn: http://dev.mysql.com/doc/refman/5.0/en/timestamp.html

Để xem hàng bị xóa chỉ đơn giản là tạo ra một kích hoạt mà sẽ đăng nhập mỗi xóa để bàn khác:

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

Đồng ý nhưng vấn đề với cách này là tôi không thể phát hiện xóa từ bảng (ngoại trừ nếu nó là hàng cuối cùng). – Jacket

+0

Hãy xem bộ kích hoạt SAU KHI XÓA (trả lời đã chỉnh sửa ở trên). – Cleankod

Các vấn đề liên quan