MySQL - InnoDB için ALTER TABLE'a ulaşmanın en hızlı yolu


12

Değiştirmek istediğim bir InnoDB tablo var. Tabloda ~ 80M satır vardır ve birkaç endeksten çıkılır.

Sütunlardan birinin adını değiştirmek ve birkaç dizin daha eklemek istiyorum.

  • Bunu yapmanın en hızlı yolu nedir?
  • "Düz" alter table, en hızlı çözüm mü?

Şu anda umurumda olan tek şey hız :)


Lütfen SHOW CREATE TABLE tblname\Gdeğiştirilmesi gereken sütunu, sütunun veri türünü ve sütunun yeni adını gösterin.
RolandoMySQLDBA

işte burada: pastie.org/3078349 yeniden adlandırılması gereken sütun sent_atve birkaç dizin daha eklemek
koştu

sent_at'ın adı neye değiştirilmeli?
RolandoMySQLDBA

diyelim: new_sent_at
koştu

Yanıtlar:


14

ALTER TABLOSUNU hızlandırmanın kesin bir yolu, gereksiz dizinleri kaldırmaktır

İşte tablonun yeni bir sürümünü yüklemek için ilk adımlar

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
;

Lütfen aşağıdakilere dikkat edin:

  • Source_persona_index'i diğer 4 dizindeki ilk sütun olduğu için bıraktım

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • Diğer 2 dizindeki ilk sütun olduğu için target_persona_index'i bıraktım

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • İlk 2 sütun da target_persona_relation_type_message_id_index içinde olduğundan target_persona_relation_type_index'i bıraktım

Tamam Bu gereksiz dizinlerle ilgilenir. Düşük kardinaliteye sahip indeksler var mı? İşte bunu belirlemenin yolu:

Aşağıdaki sorguları çalıştırın:

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;

Sorunuza göre, yaklaşık 80.000.000 satır var. Genel bir kural olarak, seçilen sütunların esas değeri tablo satır sayısının% 5'inden büyükse MySQL Query Optimizer bir dizin kullanmaz. Bu durumda, bu 4.000.000 olurdu.

  • Eğer COUNT(DISTINCT sent_at)> 4.000.000
    • sonra ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • Eğer COUNT(DISTINCT message_id)> 4.000.000
    • sonra ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • Eğer COUNT(DISTINCT target_object_id)> 4.000.000
    • sonra ALTER TABLE s_relations_new DROP INDEX target_object_index;

Bu dizinlerin kullanışlılığı veya işe yaramazlığı belirlendikten sonra, verileri yeniden yükleyebilirsiniz

#
# 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;

Hepsi bu, değil mi? HAYIR !!!

Web siteniz bu süre boyunca tamamsa, s_relations_new yüklemesi sırasında s_relations ile çalışan INSERT'ler olabilir. Bu eksik satırları nasıl geri alabilirsiniz?

Git ve s_relations_new içindeki maksimum kimliği bulun ve bu kimlikten sonraki her şeyi s_relations'den ekleyin. Tablonun dondurulduğundan ve yalnızca bu güncelleştirme için kullanıldığından emin olmak için, s_relation_new'e eklenen son satırları almak için biraz çalışmamanız gerekir. İşte yapmanız gerekenler:

İşletim sisteminde, başka hiç kimsenin oturum açamaması, ancak root @ localhost (TCP / IP'yi devre dışı bırakır) için mysql'yi yeniden başlatın:

$ service mysql restart --skip-networking

Ardından, mysql'e giriş yapın ve bu son satırları yükleyin:

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;

Ardından, mysql'yi normal olarak yeniden başlatın

$ service mysql restart

Şimdi, mysql'i indiremiyorsanız, s_relations üzerinde bir yem ve anahtar yapmanız gerekecektir. MySQL'e giriş yapın ve aşağıdakileri yapın:

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;

Bir şans ver !!!

CAVEAT: Bu işlemden memnun kaldığınızda, eski masayı en kısa sürede düşürebilirsiniz:

mysql> DROP TABLE s_relations_old;

12

Doğru cevap kullandığınız MySQL motorunun sürümüne bağlıdır.

5.6+ kullanılıyorsa, yeniden adlandırma ve dizin ekleme / kaldırma işlemleri çevrimiçi olarak gerçekleştirilir , yani tablonun tüm verileri kopyalanmadan.

ALTER TABLEHer zamanki gibi kullanın , çoğunlukla yeniden adlandırmalar ve dizin düşüşleri için anında ve dizin ekleme için makul derecede hızlı olacaktır (tüm tabloyu bir kez okumak kadar hızlı).

5.1+ kullanıyorsanız ve InnoDB eklentisi etkinse, indeks ekleme / kaldırma da çevrimiçi olacaktır. Yeniden adlandırmalardan emin değilim.

Eski sürümü kullanıyorsanız ALTER TABLE, hala en hızlı olanıdır - ancak muhtemelen tüm verileriniz kaputun altındaki geçici bir tabloya yeniden ekleneceği için korkunç bir şekilde yavaş olacaktır.

Sonunda, efsane çürütme zamanı. Ne yazık ki burada cevaplar hakkında yorum yapmak için yeterli karmam yok, ancak en çok oy alan cevabı düzeltmenin önemli olduğunu hissediyorum. Bu yanlış :

Genel bir kural olarak, seçilen sütunların esas değeri tablo satır sayısının% 5'inden büyükse MySQL Query Optimizer bir dizin kullanmaz

Aslında bu başka bir yol etrafında .

Endeksler birkaç satır seçmek için yararlıdır , bu nedenle yüksek kardinaliteye sahip olmaları önemlidir , bu da birçok farklı değer ve aynı değere sahip istatistiksel olarak az sayıda satır anlamına gelir.


Bağlantı eklentisi InnoDb belgelerinde (rep limitleri nedeniyle yapıştırmak olamazdı).
mezis

2
MySQL 5.5'te RENAME TABLEanında buldum (beklendiği gibi), ancak CHANGE COLUMNbirincil anahtarı yeniden adlandırmak için tam bir kopya yaptı ... 7 saat! Muhtemelen sadece birincil anahtar olduğu için mi? İyi değil.
KCD

2

Maria DB 10.1.12 ile aynı sorunu vardı, sonra belgeleri okuduktan sonra tablo kopyasını ortadan kaldırır "yerinde" işlemi gerçekleştirmek için bir seçenek olduğunu buldum. Bu seçenek ile değiştirme tablosu çok hızlıdır. Benim durumumda:

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

bu çok hızlı. Algoritma seçeneği olmasaydı asla sonlanmazdı.

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


0

Sütun yeniden adlandırma için,

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

iyi olmalı ve kesinti süresi taşımamalıdır.

Dizinler için CREATE INDEX deyimi tabloyu kilitler. Bahsettiğiniz gibi kullanılmayan bir köle ise, bu bir sorun değildir.

Diğer bir seçenek, uygun sütun adlarına ve dizinlerine sahip yepyeni bir tablo oluşturmak olacaktır. Sonra tüm verileri kopyalayabilir, ardından bir dizi

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

Bu, alanın iki katını geçici olarak kullanma maliyetiyle çalışmama süresini en aza indirir.


1
MySQL içindeki DDL işlemsel değildir. Her DDL deyimi bir COMMIT'i tetikler. : Bu konuda yazdım dba.stackexchange.com/a/36799/877
RolandoMySQLDBA

0

Ben de bu sorunu var ve bu SQL kullanılır:

/*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;   

Umarım birine yardımcı olabilir

Saygılarımızla,

Niyet

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.