mysqldump流程

haoge0205發表於2016-04-19

前幾天看到群裡在討論mysqldump導致鎖表的問題,為什麼一個表已經dump完了還會被鎖住?mysqldump裡面到底是怎麼處理的,為了解答這些問題,就來看看mysqldump.cc中的實現吧。

目錄 (Contents) []

重要引數

首先我們把引數和內部變數對應起來,並且看一下它們的註釋:

–single-transaction: opt_single_transaction

Creates a consistent snapshot by dumping all tables in a single transaction. Works ONLY for tables stored in storage engines which support multiversioning (currently only InnoDB does); the dump is NOT guaranteed to be consistent for other storage engines. While a –single-transaction dump is in process, to ensure a valid dump file (correct table contents and binary log position), no other connection should use the following statements: ALTER TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE, as consistent snapshot is not isolated from them. Option automatically turns off –lock-tables.

透過將匯出操作封裝在一個事務內來使得匯出的資料是一個一致性快照。只有當表使用支援MVCC的儲存引擎(目前只有InnoDB)時才可以工作;其他引擎不能保證匯出是一致的。當匯出開啟了–single-transaction選項時,要確保匯出檔案有效(正確的表資料和二進位制日誌位置),就要保證沒有其他連線會執行如下語句:ALTER TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE,這會導致一致性快照失效。這個選項開啟後會自動關閉lock-tables。

–master-data: opt_master_data

This causes the binary log position and filename to be appended to the output. If equal to 1, will print it as a CHANGE MASTER command; if equal to 2, that command will be prefixed with a comment symbol. This option will turn –lock-all-tables on, unless –single-transaction is specified too (in which case a global read lock is only taken a short time at the beginning of the dump; don’t forget to read about –single-transaction below). In all cases, any action on logs will happen at the exact moment of the dump. Option automatically turns –lock-tables off.

這個選項可以把binlog的位置和檔名新增到輸出中,如果等於1,將會列印成一個CHANGE MASTER命令;如果等於2,會加上註釋字首。並且這個選項會自動開啟–lock-all-tables,除非同時設定了–single-transaction(這種情況下,全域性讀鎖只會在開始dump的時候加上一小段時間,不要忘了閱讀–single-transaction的部分)。在任何情況下,所有日誌中的操作都會發生在匯出的準確時刻。這個選項會自動關閉–lock-tables。

–lock-all-tables: opt_lock_all_tables

Locks all tables across all databases. This is achieved by taking a global read lock for the duration of the whole dump. Automatically turns –single-transaction and –lock-tables off.

鎖定所有庫中所有的表。這是透過在整個dump的過程中持有全域性讀鎖來實現的。會自動關閉–single-transaction 和 –lock-tables。

–lock-tables: lock_tables

Lock all tables for read. (Defaults to on; use –skip-lock-tables to disable.)

對所有表加讀鎖。(預設是開啟的;用–skip-lock-tables來關閉)

–flush-logs: flush_logs

Flush logs file in server before starting dump. Note that if you dump many databases at once (using the option –databases= or –all-databases), the logs will be flushed for each database dumped. The exception is when using –lock-all-tables or –master-data: in this case the logs will be flushed only once, corresponding to the moment all tables are locked. So if you want your dump and the log flush to happen at the same exact moment you should use –lock-all-tables or –master-data with –flush-logs。

在開始匯出前重新整理伺服器的日誌檔案。注意,如果你一次性匯出很多資料庫(使用 –databases= 或 –all-databases 選項),匯出每個庫時都會觸發日誌重新整理。例外是當使用了 –lock-all-tables 或 –master-data 時:日誌只會被重新整理一次,那個時候所有表都會被鎖住。所以如果你希望你的匯出和日誌重新整理發生在同一個確定的時刻,你需要使用–lock-all-tables,或者 –master-data 配合 –flush-logs。

–delete-master-logs: opt_delete_master_logs

Delete logs on master after backup. This automatically enables –master-data.

備份完成後刪除主庫上的日誌。這個選項會自動開啟 –master-data.

–apply-slave-statements: opt_slave_apply(5.5)

Adds ‘STOP SLAVE’ prior to ‘CHANGE MASTER’ and ‘START SLAVE’ to bottom of dump.

在’CHANGE MASTER’前加上’STOP SLAVE’,在匯出檔案的末尾加上’START SLAVE’.

主要程式碼流程

我們分別看一下5.1和5.5的程式碼,都基於最新的trunk(5.1-rev.3909; 5.5-rev.4148)。

5.1版本主要流程

我們首先看下5.1版本的。

5320 if ((opt_lock_all_tables || opt_master_data) && 5321 do_flush_tables_read_lock(mysql)) 5322 goto err;

如果設定了master-data或lock-all-tables,則做FLUSH TABLES的操作。
來看下do_flush_tables_read_lock()裡面是怎麼做的,

do_flush_tables_read_lock() 4665 return 4666 ( mysql_query_with_error_report(mysql_con, 0, 4667 ((opt_master_data != 0) ? // 如果設定了--master-data 4668 "FLUSH /*!40101 LOCAL */ TABLES" : // 那麼用FLUSH LOCAL TABLES  4669 "FLUSH TABLES")) || // 如果沒設定那麼使用FLUSH TABLE 4670 mysql_query_with_error_report(mysql_con, 0, 4671 "FLUSH TABLES WITH READ LOCK") ); // 如果上面的語句執行成功了,再執行這個

先FLUSH TABLES,成功後用FLUSH TABLES WITH READ LOCK加全域性讀鎖。
再往下會判斷single-transaction,

5323 if (opt_single_transaction && start_transaction(mysql)) 5324 goto err;

如果定義了–single-transaction則開啟一個事務來讀取資料。
我們看下start_transaction()的實現,

start_transaction() 4741 return (mysql_query_with_error_report(mysql_con, 0, 4742 "SET SESSION TRANSACTION ISOLATION " 4743 "LEVEL REPEATABLE READ") || // 先設定會話的隔離級別為RR 4744 mysql_query_with_error_report(mysql_con, 0, 4745 "START TRANSACTION " 4746 "/*!40100 WITH CONSISTENT SNAPSHOT */")); // 再用一致性快照模式(RR)啟動事務

會先設定隔離級別為RR,然後START TRANSACTION加上一致性快照的Hint。
接下來是獲取Master的狀態,

5338 if (opt_master_data && do_show_master_status(mysql)) 5339 goto err;

如果設定了–master-data 則把當前的Master status列印出來。
接下來再判斷如果啟用了–single-transaction,則可以釋放表鎖的,因為事務已經啟動了。

5340 if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ 5341 goto err;

do_unlock_tables()裡面就發一條UNLOCK TABLES語句釋放全域性表鎖。

do_unlock_tables() 4677 return mysql_query_with_error_report(mysql_con, 0, "UNLOCK TABLES");

然後開始呼叫dump_*函式根據需要匯出整個例項或者一個庫或者一個表。

dump_all_databases()->dump_all_tables_in_db() 4307 if (lock_tables) 4308 { 4309 DYNAMIC_STRING query; 4310 init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); 4311 for (numrows= 0 ; (table= getTableName(1)) ; ) 4312 { 4313 char *end= strmov(afterdot, table); 4314 if (include_table((uchar*) hash_key,end - hash_key)) 4315 { 4316 numrows++; 4317 dynstr_append_checked(&query, quote_name(table, table_buff, 1)); 4318 dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); 4319 } 4320 } 4321 if (numrows && mysql_real_query(mysql, query.str, query.length-1)) 4322 DB_error(mysql, "when using LOCK TABLES"); 4323 /* We shall continue here, if --force was given */ 4324 dynstr_free(&query); 4325 } /* 如果設定了--lock-tables(預設),則匯出之前需要LOCK TABLES tables_name READ。*/ ... 4332 while ((table= getTableName(0))) 4333 { 4334 char *end= strmov(afterdot, table); 4335 if (include_table((uchar*) hash_key, end - hash_key)) 4336 { 4337 dump_table(table,database); // 匯出一張表 4338 my_free(order_by, MYF(MY_ALLOW_ZERO_PTR)); 4339 order_by= 0; 4340 if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009) 4341 { 4342 if (dump_triggers_for_table(table, database)) // 匯出 trigger 4343 { 4344 if (path) 4345 my_fclose(md_result_file, MYF(MY_WME)); 4346 maybe_exit(EX_MYSQLERR); 4347 } 4348 } 4349 } 4350 } /* 先dump_table來匯出表,然後再看是不是配置了--triggers來決定是不是匯出Trigger,dump_triggers_for_table。*/ ... 4366 if (lock_tables) 4367 VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); /* 匯出完成之後,釋放表鎖 */

所以我們可以知道,如果用–master-data和–single-transaction來匯出資料,因為–lock-tables被自動關閉,所以匯出過程中只會對當前正在做匯出操作的表有IS鎖,已經完成或沒有開始的表,則不會加鎖。
如果用的是預設–lock-tables開啟的選項,則會先把所有庫的鎖加上,再進行匯出操作,最後一次性釋放所有鎖。

5.5版本主要流程

接下來我們再比較一下,5.5的mysqldump有哪些變化。

5464 if ((opt_lock_all_tables || opt_master_data || 5465 (opt_single_transaction && flush_logs)) && 5466 do_flush_tables_read_lock(mysql)) 5467 goto err;

這裡有所不同,增加了flush_logs的判斷,如果只是單純的–single-transaction,不會呼叫do_flush_tables_read_lock(),必須同時制定–flush-logs。

5469 /*
5470     Flush logs before starting transaction since
5471     this causes implicit commit starting mysql-5.5.
5472   */ 5473 if (opt_lock_all_tables || opt_master_data || 5474 (opt_single_transaction && flush_logs) || 5475 opt_delete_master_logs) 5476 { 5477 if (flush_logs || opt_delete_master_logs) 5478 { 5479 if (mysql_refresh(mysql, REFRESH_LOG)) 5480 goto err; 5481 verbose_msg("-- main : logs flushed successfully!\n"); 5482 } 5483 5484 /* Not anymore! That would not be sensible. */ 5485 flush_logs= 0; 5486 }

5.5裡面會嘗試FLUSH LOGS。

5488 if (opt_delete_master_logs) 5489 { 5490 if (get_bin_log_name(mysql, bin_log_name, sizeof(bin_log_name))) 5491 goto err; 5492 }

5.5新增的變數,刪除master上的log,這裡先獲取binlog的檔名。

5494 if (opt_single_transaction && start_transaction(mysql)) 5495 goto err;

這一段沒有變化

5497 /* Add 'STOP SLAVE to beginning of dump */ 5498 if (opt_slave_apply && add_stop_slave()) 5499 goto err; 5500 if (opt_master_data && do_show_master_status(mysql)) 5501 goto err; 5502 if (opt_slave_data && do_show_slave_status(mysql)) 5503 goto err; 5504 if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ 5505 goto err;

這裡有新加的opt_slave_apply和opt_slave_data部分,新增STOP SLAVE語句和顯示SHOW SALVE STATUS的結果。
之後也是呼叫dump_*來匯出資料。
但是因為5.5有了MDL(Meta data lock),所以–single-transaction時,事務內操作過的表都會持有MDL,因此不會被DDL破壞。
例如,mysqldump已經備份了a,b,c表,因為它們在事務內,事務還沒提交,它們的MDL不會釋放,因此另外的執行緒如果做a,b,c中任意一張表的DDL操作,都會出現Waiting for table metadata lock,而還沒備份到的表不會持有MDL,因此還可以做DDL。



轉自:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28939273/viewspace-2083851/,如需轉載,請註明出處,否則將追究法律責任。