mysqldump --satu transaksi, namun permintaan pembaruan menunggu cadangan

10

Jika saya menggunakan mysqldump --single-transaction, menurut dokumen itu harus melakukan flush tables dengan read lock untuk mendapatkan status yang konsisten dan kemudian memulai transaksi dan tidak ada penulis yang harus menunggu.

Namun, saya telah menangkap situasi berikut tadi malam:

kutipan dari tampilkan daftar proses lengkap:

ratusan dari mereka ...

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

lalu ini:

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

dan sisa utasnya dalam mode Tidur

apakah ada yang tahu apa yang ditunggu-tunggu oleh sisipan ini? Saya tidak melihat tabel FLUSH atau DDL atau apa pun yang disebutkan dalam manual yang dapat menyebabkan kueri menunggu.

perintah mysqldump penuh

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

Saya kira --quick berlebihan di sini, mungkin sisa dari waktu sebelumnya, skrip ini sudah sangat tua, tetapi tidak ada salahnya

Aleksandar Ivanisevic
sumber
output penuh dari show full processlist dan show innodb status (dianonimkan) ada di sini: pastebin.com/D7WS3QAE
Aleksandar Ivanisevic
Untuk apa baris perintah penuh Anda mysqldump? Secara khusus, apakah Anda menggunakan --flush-logsatau --master-data...? Ada interaksi potensial di antara opsi.
Michael - sqlbot
menambahkan perintah mysqldump penuh, terima kasih telah melihat
Aleksandar Ivanisevic

Jawaban:

6

Opsi --single-transaction dari mysqldump tidak berfungsiFLUSH TABLES WITH READ LOCK; . Itu menyebabkan mysqldump untuk mengatur transaksi baca berulang untuk semua tabel yang dibuang.

Dari pertanyaan Anda, Anda menyatakan bahwa SELECT mysqldump untuk db_external_notificationtabel menahan ratusan perintah INSERT ke tabel yang sama. Mengapa ini terjadi?

Yang paling mungkin adalah kunci pada gen_clust_index (lebih dikenal sebagai Indeks Clustered). Paradigma ini menyebabkan data dan halaman indeks untuk tabel untuk hidup berdampingan. Halaman indeks tersebut didasarkan pada KUNCI UTAMA atau dan indeks RowID yang dibuat secara otomatis (jika tidak ada KUNCI UTAMA).

Anda harus dapat menemukannya dengan menjalankan SHOW ENGINE INNODB STATUS\Gdan mencari halaman mana pun dari gen_clust_index yang memiliki kunci eksklusif. Melakukan INSERT ke dalam tabel dengan Indeks Clustered memerlukan kunci eksklusif untuk menangani BTREE KUNCI UTAMA, serta serialisasi auto_increment.

Saya telah membahas fenomena ini sebelumnya

UPDATE 2014-07-21 15:03 EDT

Silakan lihat baris 614-617 dari PastBin Anda

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

Perhatikan bahwa baris 617 mengatakan

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

Apa artinya ini bagiku? Anda memiliki KUNCI UTAMA dengan auto_increment aktif id.

Maks Anda iduntuk tabel db_external_notificationkurang dari 1252538391saat mysqldump diluncurkan. Ketika Anda mengurangi 1252538391dari 1252538405, ini berarti bahwa 14 atau lebih perintah INSERT telah dicoba. Secara internal, ini perlu memindahkan auto_increment dari tabel ini setidaknya 14 kali. Namun, tidak ada yang dapat dilakukan atau bahkan didorong ke dalam Buffer Log karena mengelola idcelah ini .

Sekarang, lihat daftar proses dari PasteBin Anda. Kecuali saya salah hitung, saya melihat 38 Koneksi DB melakukan INSERT (19 Sebelum proses mysqldump (id proses 6155315), 19 Setelah). Saya yakin 14 atau lebih dari koneksi tersebut dibekukan karena mengelola celah auto_increment.

RolandoMySQLDBA
sumber
Saya mencari waktu yang lama dan tidak dapat menemukan kunci eksklusif. saya telah menyisipkan status pertunjukan penuh innodb di pastebin.com/D7WS3QAE , tidak ada yang tampak seperti kunci eksklusif untuk apa pun untuk saya
Aleksandar Ivanisevic
Terimakasih atas klarifikasinya. Saya bertanya-tanya mengapa mereka tidak menggunakan transaksi read-only karena jelas bahwa cadangan tidak akan pernah menulis, tetapi saya menduga mereka menyimpan fitur itu untuk cadangan perusahaan mereka.
Aleksandar Ivanisevic
10

The --single-transactionpilihan untuk mysqldump tidak melakukan FLUSH TABLES WITH READ LOCKsebelum memulai pekerjaan cadangan tetapi hanya dalam kondisi tertentu. Salah satu syarat tersebut adalah ketika Anda juga menentukan --master-dataopsi.

Dalam kode sumber, dari mysql-5.6.19/client/mysqldump.cpada baris 5797:

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

Untuk mendapatkan kunci solid pada koordinat binlog yang tepat sebelum memulai transaksi repeatable-read, --master-dataopsi memicu kunci ini untuk diperoleh dan kemudian dirilis setelah koordinat binlog telah diperoleh.

Bahkan, mysqldumptidak FLUSH TABLESdiikuti oleh FLUSH TABLES WITH READ LOCKkarena melakukan kedua hal itu memungkinkan kunci baca diperoleh lebih cepat dalam kasus di mana flush awal membutuhkan waktu.

...namun...

Segera setelah telah memperoleh koordinat binlog, mysqldumpmengeluarkan UNLOCK TABLESpernyataan, jadi seharusnya tidak ada apa pun yang memblokir sebagai akibat dari flush yang Anda mulai. Seharusnya tidak ada utas Waiting for table flushsebagai akibat dari transaksi yang mysqldumpditahan.

Ketika Anda melihat sebuah thread di Waiting for table flushnegara, yang harus berarti bahwa FLUSH TABLES [WITH READ LOCK]pernyataan itu dikeluarkan dan masih berjalan ketika query mulai - sehingga query harus menunggu flush meja, sebelum dapat mengeksekusi. Dalam kasus daftar proses yang Anda posting, mysqldumpmembaca dari tabel yang sama ini, dan kueri telah berjalan untuk sementara waktu, namun kueri pemblokiran belum memblokir selama itu.

Ini semua menunjukkan bahwa sesuatu yang lain telah terjadi.

Ada masalah lama yang dijelaskan dalam Bug # 44884 dengan cara FLUSH TABLESkerjanya, secara internal. Saya tidak akan terkejut jika masalah ini masih berlanjut, saya akan terkejut jika masalah ini pernah "diperbaiki" karena ini adalah masalah yang sangat kompleks untuk diselesaikan - hampir tidak mungkin untuk benar-benar diperbaiki dalam lingkungan konkurensi tinggi - dan segala upaya untuk memperbaikinya membawa risiko signifikan melanggar sesuatu, atau menciptakan perilaku baru, berbeda, dan masih tidak diinginkan.

Sepertinya ini akan menjadi penjelasan untuk apa yang Anda lihat.

Secara khusus:

  • jika Anda memiliki kueri yang berjalan lama berjalan melawan tabel, dan masalah FLUSH TABLES, maka FLUSH TABLESakan memblokir sampai kueri yang berjalan lama selesai.

  • Selain itu, setiap pertanyaan yang dimulai setelah FLUSH TABLESdikeluarkan akan diblokir sampai FLUSH TABLESselesai.

  • selain itu, jika Anda membunuh FLUSH TABLESkueri, kueri yang memblokir masih akan memblokir kueri yang sudah berjalan lama, kueri yang memblokir FLUSH TABLESkueri, karena meskipun FLUSH TABLESkueri yang terbunuh tidak selesai, tabel itu (satu, atau lebih lanjut, terlibat dengan permintaan yang sudah berjalan lama) masih dalam proses disiram, dan bahwa pending flush yang tertunda akan terjadi segera setelah permintaan yang sudah berjalan selesai - tetapi tidak sebelumnya.

Kesimpulan yang mungkin di sini adalah bahwa proses lain - mungkin mysqldump lain, atau permintaan yang keliru, atau proses pemantauan yang ditulis dengan buruk mencoba menyiram sebuah tabel.

Permintaan itu kemudian dibunuh atau habis waktu oleh mekanisme yang tidak diketahui, tetapi efek setelahnya bertahan sampai mysqldumpselesai membaca dari tabel yang bersangkutan.

Anda dapat meniru kondisi ini dengan mencoba FLUSH TABLESsementara permintaan yang sudah berjalan dalam proses. Kemudian mulai permintaan lain, yang akan diblokir. Kemudian bunuh FLUSH TABLESkueri, yang tidak akan membuka blokir kueri terbaru. Kemudian bunuh kueri pertama, atau biarkan selesai, dan kueri akhir akan berhasil dijalankan.


Sebagai renungan, ini tidak berhubungan:

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

Itu normal, karena mysqldump --single-transactionmasalah a START TRANSACTION WITH CONSISTENT SNAPSHOT, yang mencegahnya membuang data yang diubah saat dump sedang berlangsung. Tanpa itu, koordinat binlog yang diperoleh di awal akan menjadi tidak berarti, karena --single-transactiontidak akan seperti yang diklaimnya. Seharusnya tidak terkait dengan Waiting for table flushmasalah ini, karena transaksi ini jelas tidak memiliki kunci.

Michael - sqlbot
sumber
Jawaban ini sebenarnya benar.
Boban P.
2

Saya mengirimkan permintaan fitur: https://support.oracle.com/epmos/faces/BugDisplay?id=27103902 .

Saya juga menulis tambalan terhadap 5.6.37 yang menggunakan metode yang sama dengan --single-transaction - master-data kombinasi dengan --single-transaction --slave-data, yang disediakan apa adanya tanpa jaminan. Gunakan dengan risiko Anda sendiri.

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

Saya mengujinya dengan proses berikut dengan budak ke master yang sangat sibuk menggunakan banyak tabel InnoDB dengan hubungan FK:

  1. Hentikan budak A.
  2. Tunggu ~ 15 menit.
  3. Dump DB 1 dari slave B dengan opsi --single-transaction dan --dump-slave = 2
  4. Mulai slave A hingga koordinat dalam dump dari langkah 3.
  5. Jatuhkan DB 1 dan 2 dari slave A.
  6. Buat DB 1 dan 2 kosong pada slave A.
  7. Load dump dari langkah 3 ke slave A.
  8. Dump DB 2 dari slave B dengan opsi yang sama. DB 2 memiliki hubungan FK dengan DB 1.
  9. Tambahkan replicate_ignore_db untuk DB 2 dan skip_slave_start pada slave A.
  10. Mulai ulang budak A.
  11. Mulai budak sampai koordinat dari dump di langkah 8 pada budak A.
  12. Load dump dari langkah 8 ke slave A.
  13. Hapus opsi replicate_ignore_db dan skip_slave_start dari slave A.
  14. Mulai ulang budak A.
  15. Tunggu ~ 1 minggu.
  16. Gunakan pt-checksum untuk memverifikasi integritas data.

Proses pengiriman tambalan Oracle agak intensif sehingga saya memilih rute ini. Saya dapat mencoba dengan Percona dan / atau MariaDB untuk mengintegrasikannya.

pengguna44127
sumber