mysqldump - giao dịch đơn lẻ, nhưng các truy vấn cập nhật đang chờ sao lưu


10

Nếu tôi sử dụng mysqldump --single-giao dịch, theo các tài liệu, nó nên thực hiện các bảng có khóa đọc để có trạng thái nhất quán và sau đó bắt đầu giao dịch và không có nhà văn nào phải chờ đợi.

Tuy nhiên, tôi đã bắt gặp tình huống sau đây đêm qua:

trích đoạn từ danh sách quy trình đầy đủ:

hàng trăm ...

   Command: Query
   Time: 291
   State: Waiting for table flush
   Info: insert into db_external_notification.....

thì đây:

Command: Query
Time: 1204
State: Sending data
Info: SELECT /*!40001 SQL_NO_CACHE */ * FROM `db_external_notification`

và phần còn lại của các chủ đề đang trong giấc ngủ

Có ai có ý tưởng gì về những chèn này đang chờ không? Tôi không thấy bất kỳ bảng FLUSH hoặc DDL hoặc bất cứ điều gì được đề cập trong hướng dẫn có thể khiến các truy vấn phải chờ.

lệnh mysqldump đầy đủ

mysqldump --quick --add-drop-table --single-transaction --master-data=2 -uxx -pxx dbname

Tôi đoán --quick là dư thừa ở đây, có lẽ là một phần còn sót lại từ thời trước, kịch bản này rất cũ, nhưng không nên làm tổn thương


đầu ra đầy đủ của danh sách quy trình đầy đủ và hiển thị trạng thái innodb (ẩn danh) ở đây: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic

Dòng lệnh đầy đủ của bạn là mysqldumpgì? Cụ thể, bạn đang sử dụng --flush-logshay --master-data...? Có các tương tác tiềm năng giữa các tùy chọn.
Michael - sqlbot

đã thêm lệnh mysqldump đầy đủ, cảm ơn vì đã tìm kiếm
Aleksandar Ivanisevic

Câu trả lời:


6

Các --single giao dịch tùy chọn mysqldump không làm FLUSH TABLES WITH READ LOCK;. Nó khiến mysqldump thiết lập một giao dịch đọc lặp lại cho tất cả các bảng được kết xuất.

Từ câu hỏi của bạn, bạn đã nói rằng CHỌN của mysqldump cho db_external_notificationbảng đang giữ hàng trăm lệnh INSERT cho cùng bảng đó. Tại sao chuyện này đang xảy ra ?

Điều có khả năng nhất là khóa trên gen_clust_index (được gọi là Chỉ số cụm). Mô hình này làm cho các trang dữ liệu và chỉ mục cho một bảng cùng tồn tại. Các trang chỉ mục đó dựa trên chỉ số PRIMARY KEY hoặc chỉ mục RowID được tạo tự động (trong trường hợp không có KEY PRIMARY).

Bạn sẽ có thể phát hiện ra điều này bằng cách chạy SHOW ENGINE INNODB STATUS\Gvà tìm bất kỳ trang nào từ gen_clust_index có khóa độc quyền. Việc thực hiện INSERT vào một bảng có Indexed Index yêu cầu một khóa độc quyền để xử lý BTREE của PRIMARY KEY, cũng như việc tuần tự hóa auto_increment.

Tôi đã thảo luận về hiện tượng này trước đây

CẬP NHẬT 2014-07-21 15:03 EDT

Vui lòng xem các dòng 614-617 của PastBin của bạn

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

Lưu ý rằng dòng 617 nói

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

Điều này nói gì với tôi? Bạn có một số KHÓA CHÍNH với chức năng auto_increment id.

Tối đa của bạn idcho bảng db_external_notificationít hơn so với 1252538391khi mysqldump được khởi chạy. Khi bạn trừ đi 1252538391từ 1252538405, phương tiện này mà 14 hoặc nhiều lệnh INSERT đã cố gắng. Trong nội bộ, điều này sẽ cần phải di chuyển auto_increment của bảng này ít nhất 14 lần. Tuy nhiên, không có gì có thể được cam kết hoặc thậm chí được đẩy vào Bộ đệm đăng nhập vì quản lý idkhoảng cách này .

Bây giờ, hãy xem danh sách quy trình từ PasteBin của bạn. Trừ khi tôi bị nhầm lẫn, tôi đã thấy 38 Kết nối DB đang thực hiện một INSERT (19 Trước quá trình mysqldump (id quá trình 6155315), 19 Sau). Tôi chắc chắn 14 hoặc nhiều hơn các kết nối đó bị đóng băng vì quản lý khoảng cách auto_increment.


Tôi đã tìm kiếm trong một thời gian dài và không thể tìm thấy bất kỳ ổ khóa độc quyền nào. tôi đã dán toàn bộ trạng thái innodb hiển thị tại pastebin.com/D7WS3QAE , không có gì giống như khóa độc quyền đối với tôi
Aleksandar Ivanisevic

Cảm ơn bạn đã làm rõ. Tôi tự hỏi tại sao họ không sử dụng giao dịch chỉ đọc vì rõ ràng sao lưu sẽ không bao giờ viết, nhưng tôi đoán rằng họ đang giữ tính năng đó để sao lưu doanh nghiệp của họ.
Alexanderar Ivanisevic

10

Các --single-transactiontùy chọn mysqldump không làm một FLUSH TABLES WITH READ LOCKtrước khi bắt đầu công việc sao lưu nhưng chỉ trong điều kiện nhất định. Một trong những điều kiện đó là khi bạn cũng chỉ định --master-datatùy chọn.

Trong mã nguồn, từ mysql-5.6.19/client/mysqldump.cdòng 5797:

if ((opt_lock_all_tables || opt_master_data ||
     (opt_single_transaction && flush_logs)) &&
    do_flush_tables_read_lock(mysql))
  goto err;

Để có được khóa chắc chắn trên tọa độ binlog chính xác trước khi bắt đầu giao dịch đọc lặp lại, --master-datatùy chọn kích hoạt khóa này để có được và sau đó được giải phóng sau khi đã đạt được tọa độ binlog.

Trong thực tế, mysqldumpviệc FLUSH TABLEStiếp theo là bởi FLUSH TABLES WITH READ LOCKvì thực hiện cả hai điều này cho phép khóa đọc được nhanh hơn trong trường hợp việc xả ban đầu mất một thời gian.

...Tuy nhiên...

Ngay sau khi nó có được tọa độ binlog, sẽ mysqldumpđưa ra một UNLOCK TABLEScâu lệnh, do đó, không nên có bất cứ điều gì chặn do kết quả của việc bạn bắt đầu. Không có bất kỳ chủ đề nào Waiting for table flushlà kết quả của giao dịch mysqldumpđang giữ.

Khi bạn nhìn thấy một sợi trong Waiting for table flushnhà nước, mà nên có nghĩa là FLUSH TABLES [WITH READ LOCK]tuyên bố được đưa ra và vẫn chạy khi truy vấn bắt đầu - vì vậy truy vấn phải đợi cho tuôn ra bảng, trước khi nó có thể thực thi. Trong trường hợp danh sách quy trình bạn đã đăng, mysqldumpđang đọc từ cùng bảng này và truy vấn đã chạy được một lúc, nhưng các truy vấn chặn đã không bị chặn trong suốt thời gian dài.

Tất cả điều này cho thấy rằng một cái gì đó khác đã xảy ra.

Có một vấn đề tồn tại lâu dài được giải thích trong Bug # 44884 với cách thức FLUSH TABLEShoạt động trong nội bộ. Tôi sẽ không ngạc nhiên nếu vấn đề vẫn còn, tôi sẽ ngạc nhiên nếu vấn đề này đã được "khắc phục" bởi vì đây là một vấn đề rất phức tạp để giải quyết - hầu như không thể thực sự khắc phục trong môi trường đồng thời cao - và mọi nỗ lực tại sửa chữa nó mang một rủi ro đáng kể trong việc phá vỡ một cái gì đó khác, hoặc tạo ra hành vi mới, khác biệt và vẫn không mong muốn.

Có vẻ như đây sẽ là lời giải thích cho những gì bạn đang thấy.

Đặc biệt:

  • nếu bạn có một truy vấn chạy dài chạy trên một bảng và phát hành FLUSH TABLES, thì FLUSH TABLESsẽ chặn cho đến khi truy vấn chạy dài hoàn thành.

  • ngoài ra, bất kỳ truy vấn nào bắt đầu sau khi FLUSH TABLESđược đưa ra sẽ chặn cho đến khi FLUSH TABLEShoàn thành.

  • ngoài ra, nếu bạn giết FLUSH TABLEStruy vấn, các truy vấn đang chặn sẽ vẫn chặn truy vấn chạy dài ban đầu, truy vấn đang chặn FLUSH TABLEStruy vấn, bởi vì mặc dù FLUSH TABLEStruy vấn bị giết không kết thúc, bảng đó (hoặc hơn nữa, liên quan đến truy vấn chạy dài) vẫn đang trong quá trình xóa và việc xử lý đang chờ xử lý sẽ xảy ra ngay khi truy vấn chạy dài kết thúc - nhưng không phải trước đó.

Kết luận có khả năng ở đây là một quy trình khác - có thể là một mysqldump khác, hoặc một truy vấn không chính xác, hoặc một quy trình giám sát kém bằng văn bản đã cố gắng xóa một bảng.

Truy vấn đó sau đó đã bị giết hoặc hết thời gian bởi một cơ chế không xác định, nhưng hậu quả của nó đã kéo dài cho đến khi mysqldumpđọc xong từ bảng trong câu hỏi.

Bạn có thể sao chép điều kiện này bằng cách thử FLUSH TABLEStrong khi một truy vấn chạy dài đang được xử lý. Sau đó bắt đầu một truy vấn khác, sẽ chặn. Sau đó, giết FLUSH TABLEStruy vấn, sẽ không bỏ chặn truy vấn mới nhất. Sau đó, giết truy vấn đầu tiên hoặc để nó kết thúc và truy vấn cuối cùng sẽ chạy thành công.


Như một suy nghĩ lại, điều này không liên quan:

Trx read view will not see trx with id >= 1252538405, sees < 1252538391

Điều đó là bình thường, bởi vì mysqldump --single-transactioncác vấn đề a START TRANSACTION WITH CONSISTENT SNAPSHOT, ngăn không cho dữ liệu bị hủy dữ liệu đã bị thay đổi trong khi bãi chứa đang diễn ra. Nếu không có điều đó, tọa độ binlog thu được khi bắt đầu sẽ là vô nghĩa, vì --single-transactionnó sẽ không phải là những gì nó tuyên bố là. Điều này không nên liên quan đến Waiting for table flushvấn đề này, vì giao dịch này rõ ràng không có khóa.


Câu trả lời này thực sự chính xác.
Boban P.

2

Tôi đã gửi một yêu cầu tính năng: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Tôi cũng đã viết một bản vá với 5.6.37 sử dụng cùng một phương pháp như --single-giao dịch - kết hợp dữ liệu -master với dữ liệu --single-giao dịch --slave-data, được cung cấp như là không có bảo hành. Sử dụng có nguy cơ của riêng bạn.

--- 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)
   {

Tôi đã thử nghiệm nó với quy trình sau với nô lệ cho một bậc thầy rất bận rộn bằng cách sử dụng nhiều bảng InnoDB với các mối quan hệ FK:

  1. Ngăn chặn nô lệ A.
  2. Đợi ~ 15 phút.
  3. Kết xuất DB 1 từ nô lệ B với tùy chọn - giao dịch đơn lẻ và --dump-Slave = 2
  4. Bắt đầu nô lệ A cho đến khi tọa độ trong bãi chứa từ bước 3.
  5. Thả DB 1 và 2 từ nô lệ A.
  6. Tạo DB 1 và 2 trống trên nô lệ A.
  7. Tải bãi chứa từ bước 3 vào nô lệ A.
  8. Kết xuất DB 2 từ nô lệ B với các tùy chọn tương tự. DB 2 có mối quan hệ FK với DB 1.
  9. Thêm bản sao_ignore_db cho DB 2 và Skip_slave_start trên Slave A.
  10. Khởi động lại nô lệ A.
  11. Bắt đầu nô lệ cho đến khi tọa độ từ bãi rác ở bước 8 trên nô lệ A.
  12. Tải bãi chứa từ bước 8 vào nô lệ A.
  13. Xóa các tùy chọn sao chép_ignore_db và Skip_slave_start khỏi Slave A.
  14. Khởi động lại nô lệ A.
  15. Đợi ~ 1 tuần.
  16. Sử dụng pt-checksum để xác minh tính toàn vẹn dữ liệu.

Quá trình đệ trình bản vá của Oracle khá chuyên sâu do đó tôi đã đi theo con đường này. Tôi có thể thử với Percona và / hoặc MariaDB để tích hợp nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.