Dizinlenmiş sütun içeren büyük bir tabloda ALTER TABLE


14

Bir VARCHAR (20) sütun ile büyük bir tablo var ve bir VARCHAR (50) sütun olmak için değiştirmek gerekir. Genellikle, bu özel tabloda bir ALTER TABLE (bir TINYINT ekleyerek) işleminin tamamlanması yaklaşık 90-120 dakika sürer, bu yüzden veritabanının kullanıcılarını etkilemekten kaçınmak için bunu sadece bir Cumartesi veya Pazar gecesi yapabilirim. Mümkünse o zamandan önce bu değişikliği yapmak istiyorum.

Sütun ayrıca, ALTER TABLE'ı daha yavaş yapacağını düşündüğüm dizine eklenir, çünkü sütun uzunluğunu değiştirdikten sonra dizini yeniden oluşturmak zorundadır.

Web uygulaması bir MySQL çoğaltma ortamında (26 slave ve bir master) kurulur. Bir yerde okuduğumda, bir yöntemin her köle üzerinde ALTER TABLE (kullanıcılar üzerindeki etkiyi en aza indirerek) gerçekleştirmek olduğunu hatırlıyorum, sonra bunu Master üzerinde yapıyorum, ancak bu daha sonra ALTER TABLE komutunu slave'lere kopyalamaya çalışmıyor mu?

Benim sorum şu: Kullanıcılarımı minimum kesintiyle bu tabloyu değiştirmem için en iyi yol nedir?

Düzenleme: tablo InnoDB'dir.


bu tinyint sütununu eklemek aslında varsayılan değeri olan bir sütun eklemek anlamına mı geliyordu? Çünkü büyük bir masada bunu yapmak uzun zaman alabilir ..
Marian

Yanıtlar:


13

Biraz maceracıysanız, görebileceğiniz aşamalarda ALTER TABLE'ı uygulayarak işleri elinize alabilirsiniz. Değiştirmek istediğiniz tablonun WorkingTable olduğunu varsayalım. Değişiklikleri şu aşamalarda yapabilirsiniz:

#
#  Script 1
#  Alter table structure of a single column of a large table
#
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Bunu tüm kölelerde yapabilirsiniz. Usta ne olacak ??? Bunun kölelere çoğalmasını nasıl önlersiniz? Basit: SQL'i master'ın ikili günlüklerine göndermeyin. ALTER TABLE şeylerini yapmadan önce oturumdaki ikili günlük kaydını kapatmanız yeterlidir:

#
#  Script 2
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#
SET SQL_LOG_BIN = 0;
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Fakat bekle !!! Bu komutları işlerken gelen yeni veriler ne olacak ??? İşlemin başlangıcında tabloyu yeniden adlandırmak hile yapmalıdır. Bu açıdan yeni verilerin girmesini önlemek için bu kodu biraz değiştirelim:

#
#  Script 3
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#  and preventing new data from entering into the old table
#
SET SQL_LOG_BIN = 0;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
CREATE TABLE WorkingTableNew LIKE WorkingTableOld;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;
  • Komut dosyası 1, ikili günlükleri etkin olmayan herhangi bir bağımlı üzerinde yürütülebilir
  • Komut dosyası 2, ikili günlükleri etkin olan herhangi bir slave üzerinde yürütülebilir
  • Komut dosyası 3 bir ana bilgisayarda veya başka bir yerde yürütülebilir

Bir şans ver !!!


2
Gördüğüm bir sorun tablo bir 'auto_increment' alanı içeriyor olmasıdır. Temel bir test yaptım ve TABLO OLUŞTURDUĞUNU görünce şaşırdım .. GİBİ auto_increment değerini yeni tabloya kopyalamadı
Derek Downey

1
@ Test: iyi yakalamak ve harika yorum !!!. Ben auto_increment değerini bilgi_schema.tables sütun AUTO_INCREMENT alabilirsiniz alabilirsiniz. Tablonun AUTO_INCREMENT alanı yoksa, information_schema.tables içindeki AUTO_INCREMENT sütunu NULL olur. Aksi takdirde, gereken AUTO_INCREMENT değerini içerecektir. Sanırım bu NULL olmayan değeri ayıklamak ve ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber> yapmak için komut dosyası olabilir; geçici tabloyu yeniden WorkingSet olarak yeniden adlandırmadan hemen önce.
RolandoMySQLDBA

@RolandoMySQLDBA Yapılabilecek diğer bir şey, "show create table WorkingTable" ifadelerini kopyalayarak WorkingTableNew'i oluşturmak ve sizin için güvenli bir sayı ile ilerleterek auto_increment alanının değerini değiştirmek ve ayrıca varchar (50) sütununu değiştirmek. ) ve ardından her ikisini de tek bir deyimde yeniden adlandırın "yeniden adlandır tablo WorkingTable to WorkingTableOld, tabloyu yeniden adlandır WorkingTableNew to WorkingTable" Her iki yeniden adlandırmayı tek bir komutta çalıştırmak hiçbir ekleme başarısız olmayacağını garanti eder (1000 ekleri / tabloları alan tablo için bu test) "içine ... ekle" komutunu kullanabilirsiniz
Gautam Somani

4

Benim tahminim belgelerinde sadece bir uzunluk sınırlaması artan olurdu varcharbir sütun ekleyerek aynı sorun neden olmaz:

Bazı işlemler için, geçici bir tablo gerektirmeyen yerinde bir ALTER TABLE mümkündür:

Ancak bu SO sorusu hakkındaki yorumlarda çelişkili görünmektedir .

DÜZENLE

En azından 5.0 üzerinde, uzunluğu artırmak gerçekten geçici bir tablo (veya diğer eşit derecede pahalı bir işlem) gerektirdiğini onaylayabilirsiniz düşünüyorum:

test ortamı:

create table my_table (id int auto_increment primary key, varchar_val varchar(10));
insert into my_table (varchar_val)
select 'HELLO'
from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s5,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s6;

sonuç:

alter table my_table modify varchar_val varchar(20);
Query OK, 1000000 rows affected (2.91 sec)

alter table my_table add int_val int;
Query OK, 1000000 rows affected (2.86 sec)

Varchar alan boyutunu değiştirmek, yeni boyutu aşmadığınızı kontrol etmeyi içerir. Boyut artışları için bu kontrol optimize edilebilir.
BillThor

3

Düşündüm ki, ENGINE=INNODB

Yabancı anahtar kısıtlamalarınız varsa, eski tabloyu işaret eden kısıtlamalarınız olmadan değiştirilemez ve yeniden adlandırılamaz (şimdi yeniden adlandırılmıştır). Daha sonra değiştirmeniz veya süre boyunca kısıtlamaları bırakmanız gerekir.


Shabang !!! Bu çok doğru. Bu durumda, yalnızca orijinal tablonun üzerinde değişiklik tablosu yapılarak takılabilir. En azından, ALTER TABLE olmadan önce devre dışı bırakan kısıtlamalarla oyun oynamak zorunda kalabilirsiniz. Bu çok iyi yakalamak için +1 !!!
RolandoMySQLDBA
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.