it-swarm.asia

MySQL - cara tercepat untuk mengubah tabel untuk InnoDB

Saya memiliki tabel InnoDB yang ingin saya ubah. Tabel ini memiliki ~ 80 juta baris, dan keluar dari beberapa indeks.

Saya ingin mengubah nama salah satu kolom dan menambahkan beberapa indeks lagi.

  • Apa cara tercepat untuk melakukannya (dengan asumsi saya bisa menderita downtime bahkan - server adalah budak yang tidak digunakan)?
  • Is a "plain" alter table, solusi tercepat?

Saat ini, yang saya pedulikan hanyalah kecepatan :)

12
Ran

Salah satu cara pasti untuk mempercepat ALTER TABLE adalah dengan menghapus indeks yang tidak perlu

Berikut adalah langkah-langkah awal untuk memuat versi baru tabel

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;

Harap perhatikan hal berikut:

  • Saya menjatuhkan source_persona_index karena itu adalah kolom pertama dalam 4 indeks lainnya

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • Saya menjatuhkan target_persona_index karena ini adalah kolom pertama dalam 2 indeks lainnya

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • Saya menjatuhkan target_persona_relation_type_index karena 2 kolom pertama juga di target_persona_relation_type_message_id_index

OK Itu menangani indeks yang tidak perlu. Apakah ada indeks yang memiliki kardinalitas rendah? Inilah cara untuk menentukan itu:

Jalankan pertanyaan berikut:

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;

Menurut pertanyaan Anda, ada sekitar 80.000.000 baris. Sebagai aturan praktis, Pengoptimal Permintaan MySQL tidak akan menggunakan indeks jika kardinalitas kolom yang dipilih lebih besar dari 5% dari jumlah baris tabel. Dalam hal ini, itu akan menjadi 4.000.000.

  • Jika COUNT(DISTINCT sent_at)> 4.000.000
    • lalu ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • Jika COUNT(DISTINCT message_id)> 4.000.000
    • lalu ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • Jika COUNT(DISTINCT target_object_id)> 4.000.000
    • lalu ALTER TABLE s_relations_new DROP INDEX target_object_index;

Setelah kegunaan atau kegunaan dari indeks tersebut telah ditentukan, Anda dapat memuat ulang data

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;

Itu dia kan? TIDAK !!!

Jika situs web Anda telah menyala sepanjang waktu ini, mungkin ada INSERT yang berjalan terhadap s_relations selama pemuatan s_relations_new. Bagaimana Anda bisa mendapatkan kembali baris yang hilang itu?

Pergi temukan id maksimum di s_relations_new dan tambahkan semuanya setelah ID itu dari s_relations. Untuk memastikan bahwa tabel dibekukan dan hanya digunakan untuk pembaruan ini, Anda harus memiliki sedikit downtime demi mendapatkan baris terakhir yang dimasukkan ke s_relation_new. Inilah yang Anda lakukan:

Di OS, mulai ulang mysql sehingga tidak ada orang lain yang bisa masuk tetapi root @ localhost (menonaktifkan TCP/IP):

$ service mysql restart --skip-networking

Selanjutnya, masuk ke mysql dan muat baris terakhir itu:

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

Kemudian, restart mysql secara normal

$ service mysql restart

Sekarang, jika Anda tidak dapat menghapus mysql, Anda harus melakukan umpan-dan-aktifkan pada s_relations. Cukup login ke mysql dan lakukan hal berikut:

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

Cobalah !!!

CAVEAT: Setelah Anda puas dengan operasi ini, Anda dapat menjatuhkan meja lama sesegera mungkin:

mysql> DROP TABLE s_relations_old;
14
RolandoMySQLDBA

Jawaban yang benar tergantung pada versi mesin MySQL yang Anda gunakan.

Jika menggunakan 5.6+, penggantian nama dan penambahan/penghapusan indeks dilakukan online , yaitu tanpa menyalin semua data tabel.

Cukup gunakan ALTER TABLE seperti biasa, sebagian besar instan untuk penggantian nama dan penurunan indeks, dan cukup cepat untuk penambahan indeks (secepat membaca semua tabel sekali).

Jika menggunakan 5.1+, dan plugin InnoDB diaktifkan, menambahkan/menghapus indeks akan online juga. Tidak yakin tentang penggantian nama.

Jika menggunakan versi yang lebih lama, ALTER TABLE masih yang tercepat — tetapi mungkin akan sangat lambat karena semua data Anda akan dimasukkan kembali ke tabel sementara di bawah tenda.

Akhirnya, waktu untuk menghilangkan prasangka mitos. Sayangnya saya tidak memiliki cukup karma di sini untuk mengomentari jawaban, tetapi saya merasa penting untuk memperbaiki jawaban yang paling banyak dipilih. Ini salah :

Sebagai aturan praktis, Pengoptimal Permintaan MySQL tidak akan menggunakan indeks jika kardinalitas kolom yang dipilih lebih besar dari 5% dari jumlah baris tabel

Ini sebenarnya sebaliknya .

Indeks berguna untuk memilih sedikit baris, jadi penting mereka memiliki tinggi kardinalitas, yang berarti banyak nilai berbeda dan secara statistik beberapa baris dengan nilai yang sama.

12
mezis

Saya memiliki masalah yang sama dengan Maria DB 10.1.12, kemudian setelah membaca dokumentasi saya menemukan bahwa ada opsi untuk melakukan operasi "di tempat" yang menghilangkan salinan tabel. Dengan opsi ini, tabel alter sangat cepat. Dalam kasus saya itu adalah:

alter table user add column (resettoken varchar(256),
  resettoken_date date, resettoken_count int), algorithm=inplace;

ini sangat cepat. Tanpa opsi algoritma itu tidak akan pernah berakhir.

https://mariadb.com/kb/en/mariadb/alter-table/

2
Saule

Untuk mengganti nama kolom,

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

harus baik dan tidak membawa downtime.

Untuk indeks, pernyataan CREATE INDEX akan mengunci tabel. Jika itu adalah budak yang tidak digunakan seperti yang Anda sebutkan, itu bukan masalah.

Satu opsi lainnya adalah membuat tabel baru yang memiliki nama dan indeks kolom yang tepat. Kemudian Anda bisa menyalin semua data ke dalamnya, lalu jalankan serangkaian

BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;

Ini akan meminimalkan waktu henti dengan biaya sementara menggunakan ruang dua kali lipat.

0
TetonSig

Saya punya masalah ini juga dan saya menggunakan SQL ini:

/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;

/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;

/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy 
    ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
    ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;

/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;

/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
    ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;

/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;   

Saya harap ini bisa membantu seseorang

Salam,

Akan

0
William Rossier