Cara menambahkan kolom ke tabel besar di MySQL

13

Saya seorang pengembang PHP jadi jangan ketat. Saya punya meja besar ~ dump 5.5gb. PM kami memutuskan untuk membuat kolom baru di dalamnya untuk melakukan fitur baru. Tabel adalah InnoDB jadi apa yang saya coba:

  1. Ubah tabel di layar dengan kunci meja. Butuh ~ 30 jam dan tidak ada. Jadi saya menghentikannya. Pertama saya melakukan kesalahan karena saya tidak mengakhiri semua transaksi tetapi yang kedua kalinya bukan multilock. Status tadinya copy to tmp table.

  2. Karena saya juga perlu menerapkan partisi untuk tabel ini, kami memutuskan untuk membuat dump, rename, dan membuat tabel dengan nama yang sama dan struktur baru. Tapi dump membuat salinan yang ketat (setidaknya saya tidak menemukan yang lain). Jadi saya menambahkan untuk membuang kolom baru dengan seddan menanyakannya. Tetapi beberapa kesalahan aneh dimulai. Saya percaya itu disebabkan oleh charset. Tabel di utf-8 dan file menjadi as-ascii setelahnya sed. Jadi saya mendapat kesalahan (perintah tidak dikenal '\' ') pada 30% data. Jadi ini juga cara yang buruk.

Apa pilihan lain untuk mencapai ini dan mempercepat kinerja (saya bisa melakukannya dengan skrip php, tetapi akan memakan waktu lama). Apa yang akan menjadi kinerja INSERT SELECTdalam hal ini.

Terima kasih atas kemajuannya.

ineersa
sumber

Jawaban:

12

Gunakan MySQL Workbench . Anda dapat mengklik kanan tabel dan memilih "Kirim ke SQL Editor" -> "Buat Pernyataan". Dengan cara ini, tidak ada tabel "properti" yang akan dilupakan untuk ditambahkan (termasuk CHARSETatau COLLATE).
Dengan jumlah data yang sangat besar ini, saya sarankan untuk membersihkan tabel atau struktur data yang Anda gunakan (DBA yang baik sangat berguna). Jika tidak memungkinkan:

  • ganti nama tabel ( ALTER) dan buat yang baru dengan CREATEskrip yang Anda dapatkan dari Workbench. Anda juga dapat memperluas kueri itu dengan bidang baru yang Anda butuhkan
  • BULK LOAD data dari tabel lama ke yang baru:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;
    

    Dengan cara ini Anda menghindari pengindeksan / etc untuk menjalankan record by record. "Pembaruan" ke tabel masih akan lambat (karena jumlah data sangat besar) tetapi ini adalah cara tercepat yang dapat saya pikirkan.

    EDIT: Baca artikel ini untuk mendapatkan detail tentang perintah yang digunakan dalam kueri sampel di atas;)

sumber
Pilihan saya baik-baik saja. Dan saya dapat SET NAMES utf8dan COLLATION. Tapi mengapa idh 30% dari data rusak setelah sed. Saya pikir muatan curah akan menjadi yang tercepat, tetapi mungkin sesuatu yang lebih ada yang saya lewatkan. Terima kasih, Mark
ineersa,
1
@ineersa korupsi data dapat memiliki banyak alasan: misalnya Anda membuka file dengan editor yang tidak mendukung semua karakter dan menyimpannya. Atau, cara Anda mencoba mengimpor dari dump merusak data (itu buggy dan tidak dapat membaca file dengan benar). Atau, orang yang sama dapat mengidentifikasi bagian dari beberapa data sebagai ekspresi (mis. "James \ robin" == "\ r" sebagai ekspresi) atau perintah, dll. Inilah sebabnya saya tidak pernah merekomendasikan penggunaan dump, bahkan dengan alat dump data biner saja, bahkan dengan dev.mysql.com/doc/refman/5.6/en/mysqldump.html (atau BCP untuk MS SQL Server). Itu salah terlalu banyak kali ...
yeap saya coba dengan hex-blob. itu tidak membantu. Anda juga segera setelah menggunakan sed mysql mengidentifikasi \ 'sebagai perintah dalam beberapa nama (tidak semuanya). Itu aneh dan buggy. Akan mencoba pemuatan massal malam ini. Semoga itu akan dilakukan minimal dalam 10-15 jam.
ineersa
@ineersa berharap itu akan terjadi. Anda juga dapat mencoba menambahkan hanya bagian dari data, misalkan 10% dari itu untuk melihat berapa banyak waktu yang dibutuhkan - dan memiliki perkiraan untuk seluruh transaksi. Ini akan menjadi perkiraan yang sangat kasar, hal-hal bisa berjalan lambat jika cache / memori / apa pun terisi / kelebihan beban.
1
Mark terima kasih. Bekerja dengan luar biasa. Lebih cepat lagi kembalikan dari dump. Butuh waktu ~ 5 jam.
ineersa
5

Gagasan sed Anda adalah metode yang layak, tetapi tanpa kesalahan atau perintah yang Anda jalankan, kami tidak dapat membantu Anda.

Namun, metode yang terkenal untuk membuat perubahan online ke tabel besar adalah pt-online-schema-change . Pengabaian sederhana dari apa yang dilakukan alat ini disalin dari dokumentasi:

pt-online-schema-change berfungsi dengan membuat salinan tabel yang kosong untuk diubah, memodifikasinya sesuai keinginan, dan kemudian menyalin baris dari tabel asli ke tabel baru. Ketika salinan selesai, ia memindahkan tabel asli dan menggantinya dengan yang baru. Secara default, ini juga menjatuhkan tabel asli.

Metode ini mungkin juga perlu waktu untuk menyelesaikan, tetapi selama proses tabel asli akan sepenuhnya bisa digunakan.

Derek Downey
sumber
Saya akan mencoba memuat massal nanti malam. Jika itu tidak berfungsi, mungkin perlu alat ini. Kesalahan disebabkan oleh inetifieng beberapa simbol setelah menggunakan sed sebagai perintah. Misalnya 'D\'agostini'akan menyebabkan kesalahan unknown command '\''. Namun tidak selalu, seperti pada 30% kasus. Itu aneh dan buggy. Sama datang bahkan dengan kesedihan hex-blob. Derek terima kasih.
ineersa
4

alter table add column, algorithm=inplace, lock=none akan mengubah tabel MySQL 5.6 tanpa menyalin tabel dan tanpa dampak penguncian.

Baru saja diuji kemarin, massa memasukkan baris 70K ke tabel partisi 7 baris 280K, baris 10K ke setiap partisi, dengan 5 detik tidur di antaranya untuk memungkinkan throughput lainnya.

Mulai sisipan massal, lalu di sesi terpisah mulai alterpernyataan online di atas di MySQL Workbench, alterselesai sebelum sisipan, dua kolom baru ditambahkan, dan tidak ada baris yang dihasilkan dari perubahan yang berarti MySQL tidak menyalin baris apa pun.

SAK
sumber
1
Mengapa jawaban ini tidak mendapatkan lebih banyak suara, apakah itu tidak berhasil?
fguillen
1

Saat ini, opsi terbaik untuk mengubah tabel besar mungkin adalah https://github.com/github/gh-ost

gh-ost adalah solusi migrasi skema online tanpa memicu untuk MySQL. Ini dapat diuji dan memberikan jeda, kontrol / konfigurasi ulang dinamis, audit, dan banyak fasilitas operasional.

gh-ost menghasilkan beban kerja ringan pada master sepanjang migrasi, dipisahkan dari beban kerja yang ada pada tabel yang dimigrasi.

Ini telah dirancang berdasarkan pengalaman bertahun-tahun dengan solusi yang ada, dan mengubah paradigma migrasi tabel.

iJanki
sumber
1

Saya pikir Mydumper / Myloader adalah alat yang bagus untuk operasi seperti ini: Semakin baik setiap hari. Anda dapat menggunakan CPU Anda dan dapat memuat data secara paralel: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- kegunaan-fitur /

Saya telah berhasil memuat ratusan gigabytes tabel MySQL dalam hitungan jam.

Sekarang, ketika datang untuk menambahkan kolom baru, rumit karena MySQL menyalin seluruh tabel ke TMParea memori dengan ALTER TABLE...Meskipun MySQL 5.6 mengatakan itu bisa melakukan perubahan skema online, saya belum berhasil melakukannya secara online untuk tabel besar tanpa kunci pertentangan belum.

Kubilay
sumber
-2

Saya baru saja mengalami masalah yang sama. Solusi kecil:

CREATE TABLE new_table SELECT * DARI oldtable;

HAPUS DARI new_table

ALTER TABLE new_table ADD COLUMN new_column int (11);

Sisipkan ke new_table pilih *, 0 dari old_table

drop table old_table; ganti nama tabel new_table menjadi old_table;

AirCoder
sumber
Mengapa tidak hanya menambahkan klausa tempat ke pernyataan tabel buat sehingga tidak akan memilih data apa pun? Juga memotong tabel akan lebih efisien daripada menghapus data
Joe W
mengapa harus dihapus, ketika harus memasukkan nanti, lagi. Dapat mendefinisikan default = 0 pada ADD COLUMN itu sendiri.
user195280