mysqldump - ธุรกรรมเดี่ยว แต่อัพเดตคิวรี่กำลังรอการสำรองข้อมูล


10

ถ้าฉันใช้ mysqldump - ธุรกรรมเดี่ยวตามเอกสารที่ควรทำตารางล้างด้วยล็อคการอ่านเพื่อให้ได้สถานะที่สอดคล้องกันจากนั้นเริ่มต้นการทำธุรกรรมและไม่มีนักเขียนที่ควรจะรอ

อย่างไรก็ตามเมื่อคืนนี้ฉันได้พบกับสถานการณ์ต่อไปนี้:

ส่วนที่ตัดตอนมาจากรายการกระบวนการเต็มรูปแบบแสดง:

หลายร้อยคน ...

   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

ฉันเดา - ด่วนคือการซ้ำซ้อนที่นี่อาจเป็นของเหลือจากครั้งก่อนหน้าสคริปต์นี้เก่ามาก แต่ไม่ควรทำร้ายใด ๆ


ผลผลิตเต็มรูปแบบของการแสดงรายการกระบวนการเต็มรูปแบบและแสดงสถานะ Innodb (ไม่เปิดเผยชื่อ) อยู่ที่นี่: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic

บรรทัดคำสั่งแบบเต็มของคุณมีไว้เพื่อmysqldumpอะไร? โดยเฉพาะคุณใช้--flush-logsหรือ--master-data... มีปฏิสัมพันธ์ระหว่างตัวเลือกที่อาจเกิดขึ้น
Michael - sqlbot

เพิ่มคำสั่ง mysqldump แบบเต็มขอบคุณที่มอง
Aleksandar Ivanisevic

คำตอบ:


6

--single ธุรกรรมทางเลือกในการmysqldumpFLUSH TABLES WITH READ LOCK;ไม่ได้ทำ ทำให้ mysqldump ตั้งค่าธุรกรรมอ่านซ้ำสำหรับตารางทั้งหมดที่ถูกทิ้ง

จากคำถามของคุณคุณระบุว่า SELECT ของ mysqldump สำหรับdb_external_notificationตารางถือคำสั่ง INSERT หลายร้อยรายการไว้ในตารางเดียวกัน ทำไมสิ่งนี้จึงเกิดขึ้น

สิ่งที่เป็นไปได้มากที่สุดคือการล็อคบนgen_clust_index (รู้จักกันดีในชื่อ Clustered Index) กระบวนทัศน์นี้ทำให้ข้อมูลและหน้าดัชนีสำหรับตารางอยู่ร่วมกัน หน้าดัชนีเหล่านั้นจะขึ้นอยู่กับคีย์หลักหรือดัชนี RowID ที่สร้างขึ้นอัตโนมัติ (ในกรณีที่ไม่มีคีย์หลัก)

คุณควรจะเห็นสิ่งนี้ด้วยการเรียกใช้SHOW ENGINE INNODB STATUS\Gและค้นหาหน้าใด ๆ จากgen_clust_indexที่มีการล็อคแบบเอกสิทธิ์ การแทรกลงในตารางด้วยดัชนีแบบกลุ่มจะต้องมีการล็อคแบบเอกสิทธิ์เฉพาะบุคคลสำหรับการจัดการ BTREE ของคีย์หลักรวมถึงการจัดลำดับของ auto_increment

ฉันได้กล่าวถึงปรากฏการณ์นี้มาก่อน

อัพเดท 2014-07-21 15:03 EDT

โปรดดูบรรทัด 614-617 ของ PastBin ของคุณ

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

สิ่งนี้บอกอะไรฉัน คุณมีคีย์หลักที่มี auto_increment idบน

ค่าสูงสุดidสำหรับตารางของคุณdb_external_notificationน้อยกว่า1252538391เมื่อเปิด mysqldump เมื่อคุณลบ1252538391จาก1252538405วิธีนี้ที่ 14 หรือมากกว่าคำสั่ง INSERT ได้รับการพยายาม ภายในนี้จะต้องย้าย auto_increment ของตารางนี้อย่างน้อย 14 ครั้ง กระนั้นก็ไม่มีสิ่งใดที่จะก่อให้เกิดหรือแม้แต่ผลักเข้าไปใน Log Buffer เนื่องจากการจัดการidช่องว่างนี้

ตอนนี้ดูรายการกระบวนการจาก PasteBin ของคุณ ฉันเห็นการเชื่อมต่อฐานข้อมูล 38 รายการทำ INSERT (19 ก่อนหน้ากระบวนการ mysqldump (รหัสกระบวนการ6155315), 19 หลัง) ฉันมั่นใจว่าการเชื่อมต่อเหล่านั้น 14 หรือมากกว่านั้นหยุดนิ่งเนื่องจากการจัดการช่องว่าง auto_increment


ฉันกำลังมองหาเป็นเวลานานและไม่สามารถหาล็อคพิเศษใด ๆ ฉันได้วางสถานะ Innodb การแสดงแบบเต็มที่pastebin.com/D7WS3QAEแล้วไม่มีอะไรที่ดูเหมือนล็อคแบบเอกสิทธิ์เฉพาะบุคคลสำหรับฉัน
Aleksandar Ivanisevic

ขอขอบคุณสำหรับการชี้แจง. ฉันสงสัยว่าทำไมพวกเขาไม่ใช้ธุรกรรมแบบอ่านอย่างเดียวเนื่องจากมันชัดเจนว่าการสำรองข้อมูลจะไม่เขียน แต่ฉันเดาว่าพวกเขาจะเก็บคุณสมบัตินั้นไว้สำหรับการสำรองข้อมูลระดับองค์กร
Aleksandar Ivanisevic

10

--single-transactionตัวเลือกของการmysqldump ไม่ทำFLUSH TABLES WITH READ LOCKก่อนที่จะเริ่มงานสำรองแต่ภายใต้เงื่อนไขบาง หนึ่งในเงื่อนไขเหล่านั้นคือเมื่อคุณระบุ--master-dataตัวเลือกด้วย

ในซอร์สโค้ดจากmysql-5.6.19/client/mysqldump.cที่บรรทัด 5797:

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คำสั่งดังนั้นจึงไม่ควรมีสิ่งใดที่ปิดกั้นเนื่องจากการฟลัชที่คุณเริ่มต้น ไม่ควรมีเธรดใด ๆWaiting for table flushเป็นผลมาจากธุรกรรมที่mysqldumpกำลังถือครองอยู่

เมื่อคุณเห็นเธรดอยู่ในWaiting for table flushสถานะนั่นควรหมายความว่าFLUSH TABLES [WITH READ LOCK]คำสั่งนั้นถูกใช้และยังคงทำงานอยู่เมื่อคิวรีเริ่มต้น - ดังนั้นคิวรีจึงต้องรอให้ฟลัชตารางหยุดทำงานก่อนที่จะสามารถดำเนินการได้ ในกรณีของรายการกระบวนการที่คุณโพสต์mysqldumpกำลังอ่านจากตารางเดียวกันนี้และมีการเรียกใช้คิวรีมาระยะหนึ่งแล้ว แต่คิวรีการบล็อกไม่ได้ถูกบล็อกตลอดระยะเวลาที่ผ่านมา

ทั้งหมดนี้แสดงให้เห็นว่ามีสิ่งอื่นเกิดขึ้น

มีปัญหาอันยาวนานอธิบายไว้ใน Bug # 44884ด้วยวิธีการFLUSH TABLESทำงานภายใน ฉันจะไม่แปลกใจถ้าปัญหายังคงมีอยู่ฉันจะประหลาดใจถ้าปัญหานี้ "คงที่" เพราะเป็นปัญหาที่ซับซ้อนมากในการแก้ไข - แทบเป็นไปไม่ได้ที่จะแก้ไขอย่างแท้จริงในสภาพแวดล้อมที่เกิดขึ้นพร้อมกันสูง - และความพยายามใด ๆ การแก้ไขมันมีความเสี่ยงที่สำคัญในการทำลายสิ่งอื่นหรือสร้างพฤติกรรมที่แตกต่างและไม่พึงประสงค์

ดูเหมือนว่านี่จะเป็นคำอธิบายสำหรับสิ่งที่คุณเห็น

โดยเฉพาะ:

  • หากคุณมีคิวรีที่ใช้เวลานานรันกับตารางและปัญหาFLUSH TABLESดังนั้นFLUSH TABLESจะปิดกั้นจนกว่าคิวรีที่รันเป็นเวลานานจะเสร็จสมบูรณ์

  • นอกจากนี้แบบสอบถามใด ๆ ที่เริ่มต้นหลังจากที่FLUSH TABLESมีการออกจะปิดกั้นจนกว่าFLUSH TABLESจะเสร็จสมบูรณ์

  • นอกจากนี้หากคุณฆ่าFLUSH TABLESคิวรีแบบสอบถามที่กำลังบล็อกจะยังคงบล็อกในคิวรีเดิมที่รันมานานคิวรีที่กำลังบล็อกFLUSH TABLESคิวรีนั้นแม้ว่าคิวรีที่ถูกฆ่าFLUSH TABLESจะไม่เสร็จสิ้นตารางนั้น (อันนั้นหรือ ยิ่งเกี่ยวข้องกับคิวรีที่ใช้เวลานาน) ยังอยู่ในขั้นตอนของการฟลัชและฟลัชที่ค้างอยู่จะเกิดขึ้นทันทีที่เคียวรีที่รันนานจะเสร็จสิ้น แต่ไม่ใช่ก่อนหน้านี้

ข้อสรุปที่น่าจะเป็นที่นี่คือกระบวนการอื่น - อาจเป็นอีก mysqldump หรือแบบสอบถามที่ไม่เหมาะสมหรือกระบวนการตรวจสอบที่เขียนไม่ดีพยายามล้างตาราง

แบบสอบถามนั้นถูกฆ่าหรือหมดเวลาโดยกลไกที่ไม่รู้จัก แต่ภายหลังผลกระทบของมันยังคงอยู่จนกระทั่งmysqldumpอ่านจากตารางที่มีปัญหา

คุณสามารถทำซ้ำเงื่อนไขนี้ได้โดยพยายามFLUSH TABLESในขณะที่แบบสอบถามที่ใช้เวลานานกำลังดำเนินการอยู่ จากนั้นเริ่มแบบสอบถามอื่นซึ่งจะปิดกั้น จากนั้นจึงฆ่าFLUSH TABLESแบบสอบถามซึ่งจะไม่เลิกบล็อกแบบสอบถามล่าสุด จากนั้นฆ่าแบบสอบถามแรกหรือปล่อยให้เสร็จและแบบสอบถามสุดท้ายจะทำงานได้สำเร็จ


ตามมาภายหลังสิ่งนี้ไม่เกี่ยวข้อง:

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

นั่นเป็นเรื่องปกติเนื่องจากmysqldump --single-transactionปัญหา a START TRANSACTION WITH CONSISTENT SNAPSHOTซึ่งป้องกันไม่ให้ข้อมูลดัมพ์ที่ถูกเปลี่ยนแปลงในขณะที่ดัมพ์กำลังดำเนินการอยู่ หากไม่มีสิ่งนั้นพิกัด binlog ที่ได้รับเมื่อเริ่มต้นจะไม่มีความหมายเนื่องจาก--single-transactionจะไม่ใช่สิ่งที่มันอ้างว่าเป็น สิ่งนี้ไม่ควรเกี่ยวข้องกับWaiting for table flushปัญหาเนื่องจากธุรกรรมนี้ไม่มีการล็อค


คำตอบนี้ถูกต้องจริง
Boban P.

2

ฉันส่งคำขอคุณสมบัติ: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902

ฉันยังเขียนโปรแกรมปะแก้กับ 5.6.37 ที่ใช้วิธีการเดียวกันกับ --single-transaction - master-data ชุดข้อมูลที่มี --single-transaction --slave-data ซึ่งให้ตามที่เป็นอยู่โดยไม่มีการรับประกัน ใช้ความเสี่ยงของคุณเอง

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

ฉันทดสอบด้วยกระบวนการต่อไปนี้กับทาสให้กับเจ้านายที่ยุ่งมากโดยใช้ตาราง InnoDB จำนวนมากที่มีความสัมพันธ์ FK:

  1. หยุดทาส A
  2. รอ ~ 15 นาที
  3. ดัมพ์ DB 1 จาก slave B พร้อมตัวเลือก --single-transaction และ --dump-slave = 2
  4. เริ่ม Slave A จนกระทั่งพิกัดในการถ่ายโอนข้อมูลจากขั้นตอนที่ 3
  5. ปล่อย DB 1 และ 2 จาก Slave A.
  6. สร้าง DB 1 เปล่าและ 2 บนทาส A
  7. โหลดการถ่ายโอนข้อมูลจากขั้นตอนที่ 3 ไปเป็นทาส A
  8. ดัมพ์ DB 2 จาก slave B พร้อมตัวเลือกเดียวกัน DB 2 มีความสัมพันธ์ FK กับ DB 1
  9. เพิ่ม replicate_ignore_db สำหรับ DB 2 และ skip_slave_start บน slave A
  10. รีสตาร์ A
  11. เริ่ม Slave จนกระทั่งพิกัดจากการถ่ายโอนข้อมูลในขั้นตอนที่ 8 บน slave A
  12. โหลดการถ่ายโอนข้อมูลจากขั้นตอนที่ 8 ไปเป็นทาส A
  13. ลบ replicate_ignore_db และตัวเลือก skip_slave_start จาก slave A
  14. รีสตาร์ A
  15. รอ ~ 1 สัปดาห์
  16. ใช้ pt-checksum เพื่อตรวจสอบความถูกต้องของข้อมูล

กระบวนการส่งแพทช์ของ Oracle นั้นค่อนข้างเข้มข้นดังนั้นทำไมฉันถึงใช้เส้นทางนี้ ฉันอาจลองกับ Percona และ / หรือ MariaDB เพื่อให้มันรวมเข้าด้วยกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.