ALTER TABLE tanpa mengunci meja?

107

Saat melakukan pernyataan ALTER TABLE di MySQL, seluruh tabel dikunci-baca (memungkinkan pembacaan bersamaan, tetapi melarang penulisan serentak) selama pernyataan. Jika tabelnya besar, pernyataan INSERT atau UPDATE dapat diblokir untuk waktu yang sangat lama. Apakah ada cara untuk melakukan "perubahan panas", seperti menambahkan kolom sedemikian rupa sehingga tabel masih dapat diperbarui selama proses berlangsung?

Sebagian besar saya tertarik dengan solusi untuk MySQL tetapi saya akan tertarik dengan RDBMS lain jika MySQL tidak dapat melakukannya.

Untuk memperjelas, tujuan saya hanyalah untuk menghindari waktu henti ketika fitur baru yang membutuhkan kolom tabel tambahan didorong ke produksi. Skema database apa pun akan berubah seiring waktu, itu hanya fakta kehidupan. Saya tidak mengerti mengapa kita harus menerima bahwa perubahan ini pasti mengakibatkan downtime; itu lemah.

Daniel
sumber
2
Harus bertanya-tanya berapa kali Anda akan mengubah tabel?
Allain Lalonde
1
IMHO, perubahan skema database dikaitkan dengan versi baru - mereka tidak diluncurkan secara sporadis seperti perubahan lainnya. Ini pasti masalah besar.
dkretz
9
@AllainLalonde - lebih dari 0 kali membuat pertanyaan ini sah, terutama jika waktu henti di sistem Anda akan mengakibatkan nyawa atau banyak uang. Dan bagaimanapun, persyaratan perangkat lunak baru kadang-kadang muncul.
Nathan Long

Jawaban:

60

Satu-satunya pilihan lain adalah melakukan secara manual apa yang dilakukan oleh banyak sistem RDBMS ...
- Membuat tabel baru

Anda kemudian dapat menyalin konten tabel lama ke bagian satu per satu. Sementara selalu berhati-hati terhadap INSERT / UPDATE / DELETE pada tabel sumber. (Bisa dikelola oleh pemicu. Meskipun ini akan menyebabkan perlambatan, ini bukan kunci ...)

Setelah selesai, ubah nama tabel sumber, kemudian ganti nama tabel baru. Lebih disukai dalam bertransaksi.

Setelah selesai, kompilasi ulang semua prosedur yang tersimpan, dll. Yang menggunakan tabel itu. Rencana eksekusi kemungkinan besar tidak akan berlaku lagi.

EDIT:

Beberapa komentar telah dibuat tentang batasan ini karena sedikit buruk. Jadi saya pikir saya akan meletakkan perspektif baru untuk menunjukkan mengapa seperti itu ...

  • Menambahkan bidang baru seperti mengganti satu bidang di setiap baris.
  • Kunci Bidang akan jauh lebih sulit daripada kunci Baris, apalagi kunci tabel.

  • Anda benar-benar mengubah struktur fisik pada disk, setiap rekaman bergerak.
  • Ini benar-benar seperti PEMBARUAN di tabel Utuh, tetapi dengan dampak yang lebih besar ...
MatBailie
sumber
2
Dan miliki rencana pengujian menyeluruh sebelum bertukar. Jika gagal, mulai lagi.
dkretz
2
Mengelola sinkronisasi melalui pemicu adalah ide yang bagus. Saya telah menggunakan MySQL begitu lama sehingga saya terus lupa bahwa mereka sekarang memiliki pemicu. Saya telah menggunakan teknik ini dan sekarang saya memiliki skrip hot-alter yang berfungsi. Dengan bilah kemajuan. Dan itu bekerja dengan MyISAM. Hidup itu baik.
Daniel
2
+1 Ini secara harfiah adalah apa yang dilakukan manajer SQL Enterprise di belakang layar ketika Anda membuat jenis tabel tertentu berubah di UI. Di SQL 2008, mereka benar-benar menambahkan peringatan sehingga pengguna TAHU melakukan tindakan drastis ini.
BradC
2
Anda belum menyebutkan apa pun tentang kunci asing yang mereferensikan tabel yang sedang diubah. Bukankah itu masalah?
Rafay
2
@MohammadRafayAleem - Dan bidang AUTOINCREMENT, dan tampilan, serta pemicu, dll. Namun demikian, pendekatannya masih bisa diterapkan.
MatBailie
42

Percona membuat alat bernama pt-online-schema-change yang memungkinkan hal ini dilakukan.

Ini pada dasarnya membuat salinan tabel dan memodifikasi tabel baru. Untuk menjaga agar tabel baru tetap sinkron dengan aslinya, ia menggunakan pemicu untuk memperbarui. Ini memungkinkan tabel asli untuk diakses sementara tabel baru disiapkan di latar belakang.

Ini serupa dengan metode yang disarankan Dems di atas, tetapi melakukannya secara otomatis.

Beberapa alat mereka memiliki kurva belajar, yaitu menghubungkan ke database, tetapi setelah Anda menguasainya, mereka adalah alat yang hebat untuk dimiliki.

Ex:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
SeanDowney
sumber
Sepertinya tautannya rusak. Saya menemukan tautan ini berfungsi.
Noam Ben Ari
25

Pertanyaan ini dari tahun 2009. Sekarang MySQL menawarkan solusi:

DDL Online (Bahasa Definisi Data)

Sebuah fitur yang meningkatkan performa, konkurensi, dan ketersediaan tabel InnoDB selama operasi DDL (terutama ALTER TABLE). Lihat Bagian 14.11, “InnoDB dan DDL Online” untuk detailnya.

Detailnya berbeda-beda sesuai dengan jenis operasinya. Dalam beberapa kasus, tabel dapat dimodifikasi secara bersamaan saat ALTER TABLE sedang berlangsung. Operasi tersebut mungkin dapat dilakukan tanpa melakukan penyalinan tabel, atau menggunakan jenis penyalinan tabel yang dioptimalkan secara khusus. Penggunaan ruang dikendalikan oleh opsi konfigurasi innodb_online_alter_log_max_size.

Ini memungkinkan Anda menyesuaikan keseimbangan antara kinerja dan konkurensi selama operasi DDL, dengan memilih apakah akan memblokir akses ke tabel seluruhnya (klausa LOCK = EXCLUSIVE), mengizinkan kueri tetapi tidak DML (klausa LOCK = SHARED), atau mengizinkan kueri penuh dan DML akses ke tabel (klausa LOCK = NONE). Ketika Anda menghilangkan klausa LOCK atau menentukan LOCK = DEFAULT, MySQL mengizinkan konkurensi sebanyak mungkin tergantung pada jenis operasinya.

Melakukan perubahan di tempat jika memungkinkan, daripada membuat salinan baru tabel, menghindari peningkatan sementara dalam penggunaan ruang disk dan overhead I / O yang terkait dengan menyalin tabel dan merekonstruksi indeks sekunder.

lihat Manual Referensi MySQL 5.6 -> InnoDB dan DDL Online untuk info lebih lanjut.

Tampaknya DDL online juga tersedia di MariaDB

Sebagai alternatif, Anda dapat menggunakan ALTER ONLINE TABLE untuk memastikan ALTER TABLE Anda tidak memblokir operasi bersamaan (tidak perlu mengunci). Ini setara dengan LOCK = NONE.

MariaDB KB tentang ALTER TABLE

Ivanov
sumber
3
Sayang sekali tidak ada cara selain suara untuk menempatkan ini ke atas, mengingat sebagian besar meniadakan semua jawaban lain semata-mata karena mereka tidak lagi mereferensikan versi MySQL saat ini.
Burhan Ali
14

Saya merekomendasikan Postgres jika itu adalah opsi. Dengan postgres pada dasarnya tidak ada waktu henti dengan prosedur berikut:

Fitur hebat lainnya adalah sebagian besar pernyataan DDL bersifat transaksional, sehingga Anda dapat melakukan seluruh migrasi dalam transaksi SQL, dan jika terjadi kesalahan, semuanya akan dibatalkan.

Saya menulis ini beberapa waktu yang lalu, mungkin ini dapat memberikan lebih banyak wawasan tentang manfaat lainnya.

mikelikespie
sumber
6
Postgres masih membuat kunci eksklusif pada alter, mencegah orang lain membaca dari tabel itu.
clofresh
5
Saya tidak setuju dengan sedikit "pada dasarnya tidak ada downtime". Seperti yang dikatakan clofresh, ALTER TABLE mengambil kunci eksklusif pada tabel yang memblokir semua pembacaan dan penulisan secara bersamaan. Dalam pengalaman saya, untuk tabel aktif sebagian besar waktu Anda bahkan tidak akan mendapatkan kunci (ALTER TABLE akan membuat kelaparan). Dan dengan transaksi, Anda dapat dengan mudah menemui jalan buntu jika Anda tidak terlalu berhati-hati. Karena itu saya sekarang selalu mengatur waktu henti ketika mengubah tabel yang ada di Postgres.
Pankrat
1
penjelasan yang lebih rinci: dba.stackexchange.com/questions/27153/… ini menyebutkan implikasi dari kunci eksklusif, dan beberapa cara untuk mengatasinya
John Douthat
4
Ya, mengubah tabel di postgres mengambil kunci eksklusif, tetapi karena operasi itu sendiri selesai dalam milidetik, hal ini praktis tidak relevan dalam banyak kasus. Saya secara pribadi telah menambahkan kolom ke tabel ratusan juta baris di tengah hari kerja tanpa waktu henti nol.
Noah Yetter
2
@cobbzilla Ya, DROP COLUMN sama cepatnya. Di bawah tenda, yang pada dasarnya dilakukannya adalah menandai kolom sebagai tersembunyi. Nilai yang ada di kolom tersebut sebelum dihapus masih ada di file data (dan terlihat oleh transaksi lain), dan akan tetap demikian kecuali dan sampai Anda melakukan VACUUM FULL.
Noah Yetter
7

Karena Anda bertanya tentang database lain, berikut beberapa informasi tentang Oracle.

Menambahkan kolom NULL ke tabel Oracle adalah operasi yang sangat cepat karena hanya memperbarui kamus data. Ini memegang kunci eksklusif di atas meja untuk waktu yang sangat singkat. Namun, ini akan membatalkan semua prosedur yang disimpan depedan, tampilan, pemicu, dll. Ini akan dikompilasi ulang secara otomatis.

Dari sana jika perlu Anda dapat membuat indeks menggunakan klausa ONLINE. Sekali lagi, hanya kunci kamus data yang sangat pendek. Ini akan membaca seluruh tabel mencari hal-hal untuk diindeks, tetapi tidak memblokir siapa pun saat melakukan ini.

Jika Anda perlu menambahkan kunci asing, Anda dapat melakukannya dan membuat Oracle memercayai Anda bahwa datanya benar. Jika tidak, perlu membaca seluruh tabel dan memvalidasi semua nilai yang bisa lambat (buat indeks Anda terlebih dahulu).

Jika Anda perlu meletakkan nilai default atau yang dihitung ke dalam setiap baris kolom baru, Anda harus menjalankan pembaruan besar-besaran atau mungkin program utilitas kecil yang mengisi data baru. Ini bisa lambat, terutama jika baris menjadi jauh lebih besar dan tidak lagi muat di bloknya. Penguncian dapat dikelola selama proses ini. Karena versi lama aplikasi Anda, yang masih berjalan, tidak mengetahui tentang kolom ini, Anda mungkin memerlukan pemicu licik atau untuk menentukan default.

Dari sana, Anda dapat melakukan peralihan pada server aplikasi Anda ke versi kode yang baru dan itu akan terus berjalan. Jatuhkan pemicu licik Anda.

Alternatifnya, Anda dapat menggunakan DBMS_REDEFINITION yang merupakan kotak hitam yang dirancang untuk melakukan hal semacam ini.

Semua ini sangat merepotkan untuk diuji, dll sehingga kami hanya mengalami gangguan Minggu pagi setiap kali kami merilis versi mayor.

WW.
sumber
3

Jika Anda tidak mampu menyediakan waktu henti untuk database Anda saat melakukan pembaruan aplikasi, Anda harus mempertimbangkan untuk mempertahankan cluster dua node untuk ketersediaan tinggi. Dengan pengaturan replikasi sederhana, Anda dapat melakukan hampir sepenuhnya perubahan struktural online seperti yang Anda sarankan:

  • menunggu semua perubahan direplikasi pada budak pasif
  • ubah budak pasif menjadi master aktif
  • lakukan perubahan struktural pada master lama
  • mereplikasi perubahan kembali dari master baru ke master lama
  • lakukan master swapping lagi dan penerapan aplikasi baru secara bersamaan

Ini tidak selalu mudah tetapi berhasil, biasanya dengan 0 waktu henti! Node kedua tidak harus hanya yang pasif, ia dapat digunakan untuk pengujian, melakukan statistik atau sebagai node fallback. Jika Anda tidak memiliki replikasi infrastruktur dapat diatur dalam satu mesin (dengan dua contoh MySQL).

jynus
sumber
1
Master lama berada di luar cluster atau di dalam cluster?
John Chornelius
2

Nggak. Jika Anda menggunakan tabel MyISAM, menurut pemahaman saya yang terbaik mereka hanya melakukan kunci tabel - tidak ada kunci catatan, mereka hanya mencoba untuk menjaga semuanya tetap cepat melalui kesederhanaan. (Tabel MySQL lain beroperasi secara berbeda.) Bagaimanapun, Anda dapat menyalin tabel ke tabel lain, mengubahnya, lalu mengubahnya, memperbarui perbedaan.

Ini adalah perubahan yang sangat besar sehingga saya ragu DBMS akan mendukungnya. Dapat dilakukan dengan data di tabel pada awalnya merupakan suatu keuntungan.

dkretz.dll
sumber
InnoDB menggunakan kunci baris - dev.mysql.com/doc/refman/5.0/en/internal-locking.html
Eran Galperin
Ya, MySQL adalah penyimpangannya. Itulah mengapa saya sangat spesifik tentang tabel "standar".
dkretz
Anda menulis - tabel MySQL standar hanya melakukan penguncian tabel - yang salah.
Eran Galperin
Bagaimana Anda menafsirkan ini tentang tabel MyISAM (yaitu standar MySQL) dari halaman yang Anda kutip? "MySQL menggunakan penguncian tingkat tabel untuk tabel MyISAM dan MEMORY, penguncian tingkat halaman untuk tabel BDB, dan penguncian tingkat baris untuk tabel InnoDB."
dkretz
beberapa mesin penyimpanan menggunakan penguncian tingkat baris, dan beberapa menggunakan penguncian tingkat tabel. Tidak ada mesin penyimpanan standar (mungkin yang Anda maksud adalah default di phpMyAdmin ...)
Eran Galperin
2

Solusi sementara...

Solusi lain bisa jadi, tambahkan tabel lain dengan kunci utama dari tabel asli, bersama dengan kolom baru Anda.

Isi kunci utama Anda ke tabel baru dan isi nilai untuk kolom baru di tabel baru Anda, dan ubah kueri Anda untuk bergabung dengan tabel ini untuk operasi pemilihan dan Anda juga perlu menyisipkan, perbarui secara terpisah untuk nilai kolom ini.

Ketika Anda bisa mendapatkan waktu henti, Anda dapat mengubah tabel asli, memodifikasi kueri DML Anda dan menjatuhkan tabel baru Anda yang dibuat sebelumnya

Jika tidak, Anda dapat menggunakan metode clustering, replikasi, alat pt-online-schema dari percona

Balasundaram
sumber
1

Menggunakan plugin Innodb, pernyataan ALTER TABLE yang hanya menambah atau menghapus indeks sekunder dapat dilakukan "dengan cepat", yaitu tanpa membangun kembali tabel.

Namun secara umum, di MySQL, ALTER TABLE apa pun melibatkan pembangunan kembali seluruh tabel yang dapat memakan waktu sangat lama (yaitu jika tabel memiliki sejumlah data yang berguna di dalamnya).

Anda benar-benar perlu mendesain aplikasi Anda sehingga pernyataan ALTER TABLE tidak perlu dilakukan secara teratur; Anda tentu tidak ingin ALTER TABLE dilakukan selama menjalankan aplikasi secara normal kecuali Anda siap menunggu atau Anda sedang mengubah tabel kecil.

MarkR
sumber
1

Saya akan merekomendasikan salah satu dari dua pendekatan:

  1. Rancang tabel database Anda dengan mempertimbangkan potensi perubahan. Misalnya, saya bekerja dengan Sistem Manajemen Konten, yang mengubah bidang data di konten secara teratur. Alih-alih membangun struktur basis data fisik agar sesuai dengan persyaratan bidang CMS awal, jauh lebih baik membangun dalam struktur yang fleksibel. Dalam kasus ini, menggunakan kolom teks blob (misalnya varchar (max)) untuk menyimpan data XML yang fleksibel. Ini membuat perubahan struktural menjadi sangat jarang. Perubahan struktural bisa jadi mahal, jadi ada manfaat yang harus dibayar di sini juga.

  2. Memiliki waktu pemeliharaan sistem. Sistem akan offline selama ada perubahan (bulanan, dll), dan perubahan dijadwalkan selama waktu yang paling jarang diperdagangkan pada hari itu (misalnya, 3-5 pagi). Perubahan dilakukan secara bertahap sebelum peluncuran produksi, sehingga Anda akan memiliki perkiraan jendela waktu henti yang tetap.

2a. Memiliki server yang redundan, sehingga ketika sistem mengalami downtime, seluruh situs tidak akan down. Ini akan memungkinkan Anda untuk "meluncurkan" pembaruan Anda secara terhuyung-huyung, tanpa menghapus seluruh situs.

Opsi 2 dan 2a mungkin tidak layak; mereka cenderung hanya untuk situs / operasi yang lebih besar. Namun, itu adalah opsi yang valid, dan saya pribadi telah menggunakan semua opsi yang disajikan di sini.

pearcewg.dll
sumber
1

Jika ada yang masih membaca ini atau kebetulan datang ke sini, inilah keuntungan besar menggunakan sistem database NoSQL seperti mongodb. Saya memiliki masalah yang sama berurusan dengan mengubah tabel untuk menambahkan kolom untuk fitur tambahan atau indeks pada tabel besar dengan jutaan baris dan tulisan tinggi. Ini akan berakhir dengan penguncian untuk waktu yang sangat lama sehingga melakukan ini di database LIVE akan membuat pengguna kami frustrasi. Di meja kecil Anda bisa lolos begitu saja.

Saya benci kenyataan bahwa kita harus "mendesain tabel kita agar tidak mengubahnya". Saya hanya tidak berpikir itu berfungsi di dunia situs web saat ini. Anda tidak dapat memprediksi bagaimana orang akan menggunakan perangkat lunak Anda, itulah sebabnya Anda mengubah hal-hal dengan cepat berdasarkan umpan balik pengguna. Dengan mongodb, Anda dapat menambahkan "kolom" sesuka hati tanpa downtime. Anda bahkan tidak benar-benar menambahkannya, Anda hanya memasukkan data dengan kolom baru dan melakukannya secara otomatis.

Layak untuk dicoba: www.mongodb.com

Brian Gruber
sumber
2
MySQL masih digunakan di banyak sistem, jadi pertanyaannya sebenarnya tentang bagaimana cara mencapai perubahan skema di SQL RDBMS, meskipun saya juga pendukung NoSQL yang bersemangat.
Alexy
1

Secara umum, jawabannya adalah "Tidak". Anda mengubah struktur tabel yang berpotensi memerlukan banyak pembaruan "dan saya sangat setuju dengan itu. Jika Anda berharap sering melakukan ini, saya akan menawarkan alternatif untuk kolom" tiruan "- gunakan VIEWs sebagai gantinya tabel untuk SELECTmemasukkan data. IIRC, mengubah definisi tampilan relatif ringan dan tipuan melalui tampilan dilakukan ketika rencana kueri dikompilasi. Biaya yang harus Anda lakukan adalah menambahkan kolom ke tabel baru dan membuat lihat JOINdi kolom.

Tentu saja ini hanya berfungsi jika Anda dapat menggunakan kunci asing untuk melakukan cascading penghapusan dan yang lainnya. Bonus lainnya adalah Anda dapat membuat tabel baru yang berisi kombinasi data dan mengarahkan pandangan ke sana tanpa mengganggu penggunaan klien.

Hanya pemikiran saja.

D. Shawley
sumber
1

Perbedaan antara Postgres dan MySQL dalam hal ini adalah bahwa di Postgres tidak membuat ulang tabel, tetapi memodifikasi kamus data yang mirip dengan Oracle. Oleh karena itu, operasinya cepat, sementara itu masih perlu mengalokasikan kunci tabel DDL eksklusif untuk waktu yang sangat singkat seperti yang dinyatakan di atas oleh orang lain.

Di MySQL, operasi tersebut akan menyalin data ke tabel baru sambil memblokir transaksi, yang merupakan masalah utama untuk DBA MySQL sebelum v. 5.6.

Kabar baiknya adalah sejak rilis MySQL 5.6, pembatasan sebagian besar telah dicabut dan Anda sekarang dapat menikmati kekuatan sebenarnya dari MYSQL DB.

Dmitriy Royzenberg
sumber
3
Sepertinya Anda mencoba menautkan ke referensi terkait perubahan di MySql 5.6, tetapi tidak berhasil. Silakan coba lagi.
dg99
1

Seperti yang telah disebutkan SeanDowney, pt-online-schema-changeini adalah salah satu alat terbaik untuk melakukan apa yang telah Anda jelaskan dalam pertanyaan di sini. Saya baru-baru ini melakukan banyak perubahan skema pada DB live dan berjalan cukup baik. Anda dapat membaca lebih lanjut tentang itu di posting blog saya di sini: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/ .

Rafay
sumber
1

Anda pasti harus mencobanya pt-online-schema-change. Saya telah menggunakan alat ini untuk melakukan migrasi di AWS RDS dengan banyak budak dan alat ini bekerja dengan sangat baik untuk saya. Saya menulis posting blog yang terperinci tentang bagaimana melakukan itu yang mungkin berguna bagi Anda.

Blog: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

Rafay
sumber
0

Kolom dummy adalah ide yang bagus jika Anda dapat memprediksi tipenya (dan membuatnya menjadi nullable). Periksa bagaimana mesin penyimpanan Anda menangani null.

MyISAM akan mengunci semuanya jika Anda bahkan menyebutkan nama meja secara sepintas, di telepon, di bandara. Itu hanya melakukan itu ...

Karena itu, kunci bukanlah masalah besar; selama Anda tidak mencoba menambahkan nilai default untuk kolom baru ke setiap baris, tetapi membiarkannya sebagai null, dan mesin penyimpanan Anda cukup pintar untuk tidak menulisnya, Anda akan baik-baik saja dengan kunci yang hanya ditahan cukup lama untuk memperbarui metadata. Jika Anda mencoba untuk menulis nilai baru, ya, Anda bersulang.

SquareCog
sumber
1
Saya mencoba menambahkan kolom NULL ke tabel InnoDB dan harus membangun kembali seluruh tabel; bukan operasi "perbarui metadata" yang sederhana.
Daniel
Saya pikir idenya adalah untuk memasukkan kolom ekstra, nullable, dalam database ketika dirancang, sehingga jika fitur baru diperlukan, seseorang dapat "menambahkan" kolom baru hanya dengan mulai menggunakannya. Itu tidak akan memiliki nama yang bagus, tetapi jika tipe data dipilih / diprediksi dengan benar, itu akan berfungsi.
supercat
0

TokuDB dapat menambah / menjatuhkan kolom dan menambahkan indeks "panas", tabel sepenuhnya tersedia selama proses tersebut. Ini tersedia melalui www.tokutek.com

tmcallaghan.dll
sumber
-6

Tidak juga.

Anda SUDAH mengubah struktur dasar tabel, dan itu adalah sedikit informasi yang cukup penting bagi sistem yang mendasarinya. Anda juga (kemungkinan) memindahkan banyak data di sekitar disk.

Jika Anda berencana sering melakukan ini, lebih baik Anda hanya mengisi tabel dengan kolom "dummy" yang tersedia untuk digunakan di masa mendatang.

Will Hartung
sumber
3
Mengisi meja dengan kolom dummy tampaknya menjadi ide yang sangat buruk.
Jost