Mysqldump --single-transactionを使用する場合、ドキュメントによれば、読み取りロックでテーブルをフラッシュして一貫した状態を取得し、トランザクションを開始する必要があります。ライターは待機していません。
しかし、私は昨夜、次の状況に遭遇しました。
show fullプロセスリストからの抜粋:
何百もの...
Command: Query
Time: 291
State: Waiting for table flush
Info: insert into db_external_notification.....
次にこれ:
Command: Query
Time: 1204
State: Sending data
Info: SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`
残りのスレッドはスリープ状態です
これらの挿入が何を待っているのか誰かが何か知っていますか?クエリを待機させるFLUSHテーブル、DDL、またはマニュアルに記載されているものはありません。
mysqldumpコマンド全体
mysqldump --quick --add-drop-table --single-transaction --master-data=2 -uxx -pxx dbname
私は--quickはここでは冗長だと思います、おそらく以前の残り物です、このスクリプトは非常に古いですが、何も傷つけるべきではありません
-single-transaction の mysqldump のオプションは_FLUSH TABLES WITH READ LOCK;
_。これにより、mysqldumpは、ダンプされるすべてのテーブルに対して繰り返し可能な読み取りトランザクションをセットアップします。
あなたの質問から、_db_external_notification
_テーブルに対するmysqldumpのSELECTが何百ものINSERTコマンドを同じテーブルに保持していると述べました。なんでこんなことが起こっているの ?
最も可能性が高いのは、 gen_clust_index のロックです(クラスター化インデックスとして知られています)。このパラダイムにより、テーブルのデータページとインデックスページが共存します。これらのインデックスページは、PRIMARY KEYまたは自動生成されたRowIDインデックスに基づいています(PRIMARY KEYがない場合)。
_SHOW ENGINE INNODB STATUS\G
_を実行してこれを見つけ、 gen_clust_index から排他ロックのあるページを探すことができます。クラスタ化インデックスのあるテーブルにINSERTを実行するには、PRIMARY KEYのBTREEを処理するための排他ロックと、auto_incrementのシリアル化が必要です。
以前にこの現象について話しました
Aug 08, 2011
_: InnoDBデッドロックはINSERT/UPDATE/DELETE専用ですか?Dec 22, 2011
_: MySQLデッドロック-正常に再起動できませんか?Dec 13, 2012
_: MySQL InnoDBは、READ COMMITTEDパストビンの614行目から617行目をご覧ください
_mysql tables in use 1, locked 0
MySQL thread id 6155315, OS thread handle 0x85f11b70, query id 367774810 localhost root Sending data
SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`
Trx read view will not see trx with id >= 1252538405, sees < 1252538391
_
617行目は言うことに注意してください
_Trx read view will not see trx with id >= 1252538405, sees < 1252538391
_
これは何を教えてくれますか? id
にauto_incrementを持ついくつかの主キーがあります。
Mysqldumpの起動時に、テーブル_db_external_notification
_の最大id
が_1252538391
_未満でした。 _1252538391
_から_1252538405
_を減算すると、14以上のINSERTコマンドが試行されたことを意味します。内部的には、このテーブルのauto_incrementを少なくとも14回移動する必要があります。ただし、このid
ギャップを管理しているため、ログバッファーにコミットしたりプッシュしたりすることはできません。
次に、ペーストビンのプロセスリストを確認します。誤解しない限り、38のDB接続がINSERTを実行しているのを見ました(19はmysqldumpプロセスの前(プロセスID _6155315
_)、19は後)。 auto_incrementギャップを管理しているため、これらの接続の14以上がフリーズしていると思います。
mysqldump
doesの--single-transaction
オプションは、バックアップジョブを開始する前にFLUSH TABLES WITH READ LOCK
を実行しますbut only特定の条件下で。これらの条件の1つは、--master-data
オプションも指定した場合です。
ソースコードの5797行目のmysql-5.6.19/client/mysqldump.c
から:
if ((opt_lock_all_tables || opt_master_data ||
(opt_single_transaction && flush_logs)) &&
do_flush_tables_read_lock(mysql))
goto err;
反復可能読み取りトランザクションを開始する前に正確なbinlog座標を確実にロックするには、--master-data
オプションがこのロックをトリガーし、binlog座標が取得されると解放されます。
実際、mysqldump
はFLUSH TABLES
を実行し、その後にFLUSH TABLES WITH READ LOCK
を実行します。これは、両方の処理を行うと、初期フラッシュに時間がかかる場合に、読み取りロックをより速く取得できるためです。
...しかしながら...
Binlog座標を取得するとすぐに、mysqldump
がUNLOCK TABLES
ステートメントを発行するため、開始したフラッシュの結果として何もブロックされないはずです。また、mysqldump
が保持しているトランザクションの結果として、スレッドがWaiting for table flush
になることはありません。
Waiting for table flush
状態のスレッドが表示された場合、そのshouldは、FLUSH TABLES [WITH READ LOCK]
ステートメントが発行されたことを意味しますそしてまだ実行されていましたクエリの開始時に-クエリは待機する必要があります実行する前にテーブルをフラッシュします。あなたが投稿したプロセスリストの場合、mysqldump
はこの同じテーブルから読み取りを行っており、クエリはしばらく実行されていますが、ブロッキングクエリはその間ずっとブロックされていません。
これはすべて、何か他のことが起こったことを示唆しています。
内部でFLUSH TABLES
が機能する方法には、長期にわたる問題があります Bug#44884で説明 。 問題が解決しない場合でも、私は驚かないでしょう、 この問題が解決されると非常に複雑な問題であるため、この問題が「修正」されると驚きます。高並行性環境では本当に修正することは事実上不可能です。修正しようとすると、他の何かを壊す大きなリスクが伴います。または、新しい、異なる、それでも望ましくない動作を作成します。
これはあなたが見ているものの説明になると思われます。
具体的には:
テーブルに対して実行されている長時間のクエリがあり、FLUSH TABLES
を発行した場合、FLUSH TABLES
は長時間のクエリが完了するまでブロックされます。
さらに、FLUSH TABLES
が発行された後に始まるクエリは、FLUSH TABLES
が完了するまでブロックされます。
さらに、FLUSH TABLES
クエリを強制終了すると、ブロックしているクエリは、元の長時間実行クエリでstill blockになります。これは、FLUSH TABLES
クエリをブロックしていたクエリでも、強制終了されたFLUSH TABLES
クエリは終了しませんでしたが、そのテーブル(長時間実行クエリに関連する1つ以上)はまだフラッシュされており、保留中のフラッシュは、実行中のクエリは終了しますが、前ではありません。
ここで考えられる結論は、別のプロセス-おそらく別のmysqldump、不適切なクエリ、または不適切に記述された監視プロセスがテーブルをフラッシュしようとしたことです。
その後、そのクエリは不明なメカニズムによって強制終了またはタイムアウトしましたが、その影響は、mysqldump
が問題のテーブルからの読み取りを終了するまで続きました。
長時間実行クエリの処理中にFLUSH TABLES
を試行することにより、この状態を再現できます。次に、ブロックする別のクエリを開始します。次に、FLUSH TABLES
クエリを強制終了します。これにより、最新のクエリのブロックが解除されません。次に、最初のクエリを強制終了するか、終了させます。最後のクエリが正常に実行されます。
後から考えると、これは無関係です。
Trx read view will not see trx with id >= 1252538405, sees < 1252538391
mysqldump --single-transaction
はSTART TRANSACTION WITH CONSISTENT SNAPSHOT
を発行し、ダンプの進行中に変更されたデータをダンプできないため、これは正常です。それがなければ、--single-transaction
は主張されているものとは異なるため、最初に取得したbinlog座標は意味がありません。このトランザクションは明らかにロックを保持していないため、これはWaiting for table flush
の問題とは関係ありません。
機能リクエストを送信しました: https://support.Oracle.com/epmos/faces/BugDisplay?id=27103902 。
--single-transaction --master-dataと--single-transaction --slave-dataの組み合わせと同じ方法を使用する5.6.37に対するパッチも作成しました。これは、保証なしで現状のまま提供されます。自己責任。
--- mysql-5.6.37/client/mysqldump.c.bak 2017-11-14 12:24:41.846647514 -0600
+++ mysql-5.6.37/client/mysqldump.c 2017-11-14 14:17:51.187050091 -0600
@@ -4900,10 +4900,10 @@
return 0;
}
+/*
static int do_stop_slave_sql(MYSQL *mysql_con)
{
MYSQL_RES *slave;
- /* We need to check if the slave sql is running in the first place */
if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
return(1);
else
@@ -4911,23 +4911,21 @@
MYSQL_ROW row= mysql_fetch_row(slave);
if (row && row[11])
{
- /* if SLAVE SQL is not running, we don't stop it */
if (!strcmp(row[11],"No"))
{
mysql_free_result(slave);
- /* Silently assume that they don't have the slave running */
return(0);
}
}
}
mysql_free_result(slave);
- /* now, stop slave if running */
if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD"))
return(1);
return(0);
}
+*/
static int add_stop_slave(void)
{
@@ -5841,10 +5839,12 @@
if (!path)
write_header(md_result_file, *argv);
+ /*
if (opt_slave_data && do_stop_slave_sql(mysql))
goto err;
+ */
- if ((opt_lock_all_tables || opt_master_data ||
+ if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
(opt_single_transaction && flush_logs)) &&
do_flush_tables_read_lock(mysql))
goto err;
@@ -5853,7 +5853,7 @@
Flush logs before starting transaction since
this causes implicit commit starting mysql-5.5.
*/
- if (opt_lock_all_tables || opt_master_data ||
+ if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
(opt_single_transaction && flush_logs) ||
opt_delete_master_logs)
{
static int add_stop_slave(void)
{
@@ -5841,10 +5839,12 @@
if (!path)
write_header(md_result_file, *argv);
+ /*
if (opt_slave_data && do_stop_slave_sql(mysql))
goto err;
+ */
- if ((opt_lock_all_tables || opt_master_data ||
+ if ((opt_lock_all_tables || opt_master_data || opt_slave_data ||
(opt_single_transaction && flush_logs)) &&
do_flush_tables_read_lock(mysql))
goto err;
@@ -5853,7 +5853,7 @@
Flush logs before starting transaction since
this causes implicit commit starting mysql-5.5.
*/
- if (opt_lock_all_tables || opt_master_data ||
+ if (opt_lock_all_tables || opt_master_data || opt_slave_data ||
(opt_single_transaction && flush_logs) ||
opt_delete_master_logs)
{
私は、FK関係を持つ多くのInnoDBテーブルを使用して、非常にビジーなマスターへのスレーブを使用して、次のプロセスでテストしました。
Oracleのパッチ提出プロセスはかなり集中的であるため、なぜ私はこの方法を採用しましたか。 PerconaやMariaDBを使って統合することもできます。