Bagaimana cara mengubah tabel baris 66.862.521 dari MyISAM ke InnoDB tanpa offline selama beberapa jam?

18

apakah mungkin (dan bagaimana) untuk mengubah tabel MyISAM yang besar menjadi InnoDB tanpa menjadikan aplikasi offline. Perlu memasukkan beberapa baris ke dalam tabel itu setiap detik tetapi dimungkinkan untuk menangguhkannya selama sekitar 2 menit.

Jelas ALTER TABLE ... engine = innodb tidak akan berfungsi. Karenanya saya punya rencana untuk membuat tabel baru dengan mesin innodb dan menyalin konten ke dalamnya. Dan pada akhirnya, tunda thread log aplikasi dan RENAME TABLE.

Sayangnya bahkan melakukan penyalinan dalam batch kecil 100 baris menghasilkan kelambatan yang signifikan setelah beberapa waktu.

Sunting : Baris yang ada tidak pernah diubah, tabel ini digunakan untuk pencatatan.

Hendrik Brummermann
sumber
3
Nah, pertanyaan itu adalah tentang meminimalkan waktu percakapan. Saya tidak peduli jika percakapannya berlangsung beberapa hari atau minggu. Tetapi itu harus bekerja di latar belakang tanpa memerlukan waktu aplikasi dan tanpa membuat jeda terlihat.
Hendrik Brummermann

Jawaban:

15

Buat pengaturan Master-Master sebagai berikut:

  • Buat master kedua, MasterB
  • MasterB bertindak sebagai budak logTable
  • Buat logTable_newsebagai innodb
  • Jalankan INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) pada MasterB, yang mengirimkan replikasi ke MasterA
  • Ketika logTable_newpada MasterA selesai menyinkronkan, tukar tabel tersebut
Derek Downey
sumber
10

Mengingat kendala:

Saya tidak peduli jika percakapannya memakan waktu beberapa hari atau minggu. Tetapi itu harus bekerja di latar belakang tanpa memerlukan waktu aplikasi dan tanpa membuat jeda terlihat

Saat Anda melakukan pencatatan, jika Anda memiliki beberapa cara yang baik untuk mengatur penanda sehingga Anda dapat mengetahui apa yang Anda mulai prosesnya, sehingga Anda kemudian dapat menerapkan kembali log apa pun, atau meminta log dituliskan ke file teks sehingga nanti Anda bisa menelannya LOAD DATA INFILE

Sebagian dari masalahnya adalah bahwa menulis dalam batch yang lebih kecil berarti bahwa indeks harus dihitung ulang berulang kali; Anda lebih baik menjalankannya sekaligus, tetapi ini mungkin menyebabkan kelambatan 'yang terlihat' pada sistem .. tetapi Anda tidak harus melakukannya di server produksi Anda.

  1. Jeda pencatatan atau atur beberapa penanda sehingga Anda dapat menerapkan kembali log dari titik ini nanti.
  2. Salin tabel MyISM Anda ke sistem lain
  3. Di sistem lain, buat tabel InnoDB dengan nama yang berbeda dan migrasikan datanya (bahkan mungkin lebih cepat untuk membuangnya dan menggunakannya LOAD DATA INFILE)
  4. Salin tabel InnoDB kembali ke sistem asli
  5. Tetapkan penanda lain untuk pencatatan.
  6. Terapkan kembali semua log ke tabel baru dari antara dua penanda terakhir.
  7. (ulangi langkah 5 & 6 jika langkah # 6 membutuhkan waktu lebih dari satu menit, sampai hanya beberapa detik)
  8. Tukar tabel (ganti nama yang lama ke table_BACKUP, yang baru dengan nama yang lama)
  9. Telusuri log sejak penanda terakhir.
Joe
sumber
9

Sayangnya bahkan melakukan penyalinan dalam batch kecil 100 baris menghasilkan kelambatan yang signifikan setelah beberapa waktu.

Apakah Anda menambahkan penundaan antara setiap batch, atau hanya menumpuk pembaruan dan menjalankan setiap batch langsung setelah yang sebelumnya?

Jika demikian maka cobalah skrip konversi dalam bahasa favorit Anda dengan sesuatu seperti:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

Ini harus memastikan bahwa konversi tidak memakan lebih dari atau kurang dari setengah kapasitas server Anda, bahkan memungkinkan perbedaan beban yang dikenakan karena penggunaan sistem bervariasi sesuai waktu.

Atau jika Anda ingin menggunakan waktu sebanyak mungkin ketika layanan ini relatif tidak digunakan tetapi mundur (berpotensi berhenti untuk waktu yang cukup lama) ketika database perlu melakukan beberapa pekerjaan untuk para penggunanya, ganti sleep for as long as the update tookdengan if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Ini berarti ia dapat bergerak maju dalam waktu tenang tetapi akan berhenti sepenuhnya ketika server sedang sibuk melakukan itu beban kerja normal. Menentukan beban akan bergantung pada OS Anda - di Linux dan sejenisnya, nilai rata-rata beban 1 menit dari /proc/loadavgatau keluaran yang uptimeseharusnya dilakukan. <lower measure>dan <upper measure>mungkin nilainya sama, meskipun biasanya pada kontrol seperti ini memiliki perbedaan sehingga proses Anda tidak terus berjalan maka segera berhenti karena memulai kembali sendiri memiliki pengaruh pada ukuran beban.

Tentu saja ini tidak akan berfungsi untuk tabel di mana baris lama dapat dimodifikasi, tetapi akan berfungsi dengan baik untuk tabel log seperti yang Anda gambarkan.

Anda akan ingin mengabaikan kebijaksanaan membuat indeks setelah mengisi tabel baru dalam kasus ini. Walaupun hal itu memang lebih efisien ketika Anda ingin segala sesuatunya menjadi secepat mungkin (efeknya pada sistem yang lain terkutuk), dalam hal ini Anda tidak ingin beban yang besar pada akhir proses sebagai indeks dibuat sepenuhnya dalam sekali jalan karena ini adalah proses yang tidak dapat Anda hentikan saat segala sesuatunya menjadi sibuk.

David Spillett
sumber
4

Apakah sesuatu seperti ini berfungsi?

  1. Jeda pencatatan (agar $auto_incrementtabel pencatatan mytable Anda tidak berubah).
  2. Perhatikan $auto_incrementnilainya menggunakan SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Aktifkan logging lagi. Tabel Innodb sekarang akan mulai mengisi.
  7. INSERT INTO mytable SELECT * FROM mytable_old.

Anda dapat melakukan langkah 7 dalam batch atau dalam satu pernyataan karena seharusnya tidak memblokir ke pencatatan normal.

Riedsio
sumber
masih akan memblokir, karena cara innodb menangani auto_increment,. secara default innodb mengambil kunci level tabel ketika memasukkan ke dalam kolom auto_increment, dan melepaskan kunci segera setelah insert selesai.
ovais.tariq