InnoDBテーブルが変更されたかどうかを確認する最速の方法


22

私のアプリケーションは非常にデータベース集約型です。現在、MySQL 5.5.19を実行しており、MyISAMを使用していますが、InnoDBに移行中です。残っている唯一の問題は、チェックサムのパフォーマンスです。

私のアプリケーションはCHECKSUM TABLE、ピーク時に毎秒約500-1000 ステートメントを実行します。これは、クライアントGUIが変更のためにデータベースを絶えずポーリングしているためです(監視システムであるため、非常に応答性が高く、高速である必要があります)。

MyISAMには、テーブルの修正時に事前計算された非常に高速なライブチェックサムがあります。ただし、InnoDBにはそのようなものはありません。だから、非常にCHECKSUM TABLE遅いです。

テーブルの最終更新時間を確認できるようにしたいと考えています。残念ながら、これはInnoDBでも利用できません。テストでは、アプリケーションのパフォーマンスが大幅に低下することが示されているため、私は今立ち往生しています。

テーブルを更新するコードの行が多すぎるため、アプリケーションにロジックを実装してテーブルの変更を記録することは問題外です。

InnoDBテーブルの変更を検出する高速な方法はありますか?

回答:


15

テーブルmydb.mytableに対して、次のクエリを実行します。

SELECT update_time
FROM information_schema.tables
WHERE table_schema='mydb'
AND table_name='mytable';

過去5分間にどのテーブルが変更されたかを知りたい場合は、次を実行します。

SELECT table_schema,table_name,update_time
FROM information_schema.tables
WHERE update_time > (NOW() - INTERVAL 5 MINUTE);

試してみる !!!

更新2011-12-21 20:04 EDT

私の雇用主(DB / Wwebホスティング会社)には、112,000のInnoDBテーブルを持つクライアントがいます。ピーク時にINFORMATION_SCHEMA.TABLESを読み取ることは非常に困難です。私は別の提案があります:

innodb_file_per_tableを有効にしていて、すべてのInnoDBテーブルが.ibdファイルに保存されている場合、最後の更新の時刻(分まで)を確認する方法があります。

テーブルmydb.mytableについては、オペレーティングシステムで以下を実行します。

$ cd /var/lib/mysql/mydb
$ ls -l mytable.ibd | awk '{print $4,$5}'

このタイムスタンプはOSからのものです。これで間違いはありません。

更新2011-12-21 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

これをmy.cnfに追加し、mysqlを再起動すると、すべてのInnoDBテーブルでバッファープールからの高速フラッシュが発生します。

再起動を回避するには、単に実行します

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

更新2013-06-27 07:15 EDT

ファイルの日付と時刻を取得する場合、lsには次の--time-styleオプションがあります。

$ cd /var/lib/mysql/mydb
$ ls -l --time-style="+%s" mytable.ibd | awk '{print $6}'

ファイルのタイムスタンプをUNIX_TIMESTAMP(NOW())と比較できます。


idb moddateを使用すると、間違いを犯すことはできませんか?変更は、メモリ内のバッファプールにのみ存在し、まだディスクにフラッシュされていない可能性があります。
atxdba

6
答えてくれてありがとう、しかし私が言ったように、InnoDBテーブルのinformation_schema.tablesのupdate_timeはNULLです。また、パフォーマンスを犠牲にするため、innodb_max_dirty_pages_pct = 0が良いアイデアであるかどうかわかりません。この場合にのみ、テーブルごとに3つのトリガーが必要になります...
ジャケット

また、information_schema.tablesからの選択も時間がかかります。1つのテーブルをチェックするのに約300msかかります。比較のために、ライブチェックサムが有効になっている数百万行のMyISAMテーブルで「CHECKSUM TABLE」を実行すると、1ミリ秒未満かかります。
ジャケット

2
バッファフラッシュが十分に定期的である限り(ほぼ1秒間に1回がデフォルト)、ファイルシステムチェックのために+1を使用すると、このタイムスタンプはかなり正確になり、ほとんどの場合におそらく十分になります...
Dave Rix

1
多分それのローカルデータベースのOK、しかし、私は、複数のリモートスレーブを持って...動作していない。このように、
ジャケット

3

私は解決策を見つけたと思う。しばらくの間、MySQLサーバーを置き換えるためにPercona Serverを探していましたが、今ではこれには十分な理由があると思います。

Perconaサーバーは、INNODB_TABLE_STATSのような多くの新しいINFORMATION_SCHEMAテーブルを導入しますが、これは標準のMySQLサーバーでは利用できません。行うとき:

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

実際の行数とカウンターを取得します。公式ドキュメントには、このフィールドについて次のことを言います。

変更された列の値が「rows / 16」または2000000000を超える場合、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")); 

これにより、同じ問題を抱えている他の人のトラブルを軽減できると思います。


興味のある人のためのさらなるストーリー開発:forum.percona.com/…–
ジャケット

1

そのバージョンでmysql v5.6 +に更新する必要があります。innodbはチェックサムテーブルもサポートしています。 http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

それ以外の場合、理想的なソリューションは、クライアントが常に結果をポーリングしていない場合であり、代わりに、新しいデータと変更されたデータを利用可能になったときにプッシュする場合です。それはより高速で、サーバーへの負荷が少なくなります。WebベースのGUIを使用している場合は、APE http://ape-project.org/または他の同様のプロジェクトを調べる必要があります。


残念なことに、これはパフォーマンスのキラーです。チェックサムは、すべての行を1つずつハッシュすることで構成されます。ドキュメントから:「この行ごとの計算は、EXTENDED句、InnoDBおよびMyISAM以外の他のすべてのストレージエンジン、およびCHECKSUM = 1句で作成されていないMyISAMテーブルで得られます」:-(
LSerni

1

主にテーブルに追加する場合は、更新の尺度としてAUTO_INCREMENTをフックできます。

SELECT `AUTO_INCREMENT` FROM `information_schema`.`tables` 
WHERE `table_schema` = DATABASE() AND `table_name` = 'YOUR_TABLE';

しかし、データベース内の何かを変更するたびに増加するMemcachedのカウンターのようなotsideソースを参照したいと思います。


0

次のことを試みることができます。

SELECT rows_changed
FROM information_schema.table_statistics
WHERE table_schema = 'mydb' AND table_name='mytable';

これは、テーブルが更新されるたびに増加する数値を返します。これを追跡することで、変更を検出できます。

重要な注意:値は、COMMITではなく、UPDATEの直後に変更されます。そのため、変更が完了していない別のトランザクション内で行われた場合、変更が表示されない場合があります。


0

この回答は、mysqlデータベースのバージョンやタイプとは関係ありません。更新ステートメントが変更を加えているかどうか、およびPHPコードでこれを行うかどうかを知りたいと思いました。

  1. mysqlのcurrent_timestampの値を取得するためにクエリする1つのレコードと1つのフィールドを持つダミーテーブルを作成しました。

  2. 更新されるデータテーブルにタイムスタンプフィールドを追加し、mysqlオプション「ON UPDATE CURRENT_TIMESTAMP」を使用しました

  3. #1と#2の比較

これは常に機能するわけではありませんが、私のアプリケーションにとってはシンプルで素晴らしいソリューションでした。これが誰かを助けることを願っています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.