ALTER TABLE, masayı kilitlemeden mi?


107

MySQL'de bir ALTER TABLE deyimi yaparken, tüm tablo, ifade süresi boyunca okuma kilitlidir (eşzamanlı okumalara izin verir, ancak eşzamanlı yazmaları yasaklar). Büyük bir masaysa, INSERT veya UPDATE ifadeleri çoooook uzun bir süre engellenebilir. Tablonun işlem boyunca hala güncellenebilir olmasını sağlayacak şekilde sütun eklemek gibi "sıcak değişiklik" yapmanın bir yolu var mı?

Çoğunlukla MySQL için bir çözümle ilgileniyorum, ancak MySQL bunu yapamazsa diğer RDBMS ile ilgilenirim.

Açıklığa kavuşturmak gerekirse, amacım fazladan bir tablo sütunu gerektiren yeni bir özellik üretime gönderildiğinde kesinti sürelerinden kaçınmaktır. Herhangi veritabanı şeması olacak hayatın bir gerçektir ki, zaman içinde değişebilir. Neden bu değişikliklerin kaçınılmaz olarak kesinti süresine yol açması gerektiğini kabul etmemiz gerektiğini anlamıyorum; bu sadece zayıf.


2
Masayı kaç kez değiştireceğinizi merak mı ediyorsunuz?
Allain Lalonde

1
IMHO, veritabanı şeması değişiklikleri tamamen yeni sürümlerle ilişkilidir - diğer değişikliklerin yaptığı gibi düzensiz olarak sunulmazlar. Kaçınılmaz olarak büyük bir anlaşma.
dkretz

9
@AllainLalonde - 0'dan fazla kez bu soruyu yasal hale getirir, özellikle de sisteminizdeki kesinti hayatlara veya çok paraya mal olacaksa. Ve her halükarda, bazen yeni yazılım gereksinimleri ortaya çıkıyor.
Nathan Long

Yanıtlar:


60

Diğer tek seçenek, birçok RDBMS sisteminin zaten yaptığını manuel olarak yapmaktır ...
- Yeni bir tablo oluştur

Daha sonra eski tablonun içeriğini bir seferde bir yığın üzerine kopyalayabilirsiniz. Her zaman kaynak tablodaki herhangi bir INSERT / UPDATE / DELETE konusunda dikkatli olun. (Bir tetikleyici tarafından yönetilebilir. Bu yavaşlamaya neden olsa da kilit değildir ...)

Bitirdiğinizde, kaynak tablonun adını değiştirin, ardından yeni tablonun adını değiştirin. Tercihen bir işlemde.

Bittiğinde, bu tabloyu kullanan saklı yordamları vb. Yeniden derleyin. Uygulama planları büyük olasılıkla artık geçerli olmayacak.

DÜZENLE:

Bu sınırlamanın biraz zayıf olduğuna dair bazı yorumlar yapılmıştır. Bu yüzden neden böyle olduğunu göstermek için ona yeni bir bakış açısı koyacağımı düşündüm ...

  • Yeni bir alan eklemek, her satırda bir alanı değiştirmek gibidir.
  • Alan Kilitleri, Satır kilitlerinden çok daha zor olurdu, masa kilitlerini boşverin.

  • Aslında diskteki fiziksel yapıyı değiştiriyorsunuz, her kayıt hareket ediyor.
  • Bu gerçekten Tüm tablodaki bir GÜNCELLEME gibidir, ancak daha fazla etkiyle ...

2
Değiştirmeden önce kapsamlı bir test planınız olsun. Başarısız olursa, baştan başlayın.
dkretz

2
Senkronizasyonu tetikleyiciler aracılığıyla yönetmek güzel bir fikirdi. MySQL'i o kadar uzun süredir kullanıyorum ki şimdi tetikleyicileri olduğunu unutup duruyorum. Bu tekniği kullandım ve şimdi işlevsel bir sıcak değiştirme betiğim var. Bir ilerleme çubuğu ile. Ve MyISAM ile çalışır. Hayat güzel.
Daniel

2
+1 Bu, kullanıcı arayüzünde belirli türlerde tablo değişiklikleri yaptığınızda SQL Enterprise yöneticisinin perde arkasında yaptığı şeydir. SQL 2008'de, kullanıcının bu sert eylemi gerçekleştirdiğini BİLMESİ için aslında bir uyarı eklediler.
BradC

2
Değiştirilen tablolara atıfta bulunan yabancı anahtarlar hakkında hiçbir şeyden bahsetmediniz. Bu bir sorun olmaz mıydı?
Rafay

2
@MohammadRafayAleem - Ve AUTOINCREMENT alanları, görünümler ve tetikleyiciler, vb. Ancak öyle bile olsa, yaklaşım hala uygulanabilir.
MatBailie

42

Percona, bunun yapılmasını sağlayan pt-online-schema-change adlı bir araç yapar .

Esasen tablonun bir kopyasını oluşturur ve yeni tabloyu değiştirir. Yeni tabloyu orijinal ile senkronize tutmak için, güncellemek için tetikleyiciler kullanır. Bu, yeni tablo arka planda hazırlanırken orijinal tabloya erişim sağlar.

Bu, yukarıda Dems tarafından önerilen yönteme benzer, ancak bunu otomatik bir şekilde yapar.

Araçlarından bazılarının bir öğrenme eğrisi vardır, yani veritabanına bağlanır, ancak bunu bir kez indirdiğinizde, sahip olunması gereken harika araçlardır.

Ör:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends

Görünüşe göre bağlantı kopmuş. Bu bağlantıyı çalışıyor buldum .
Noam Ben Ari

25

2009'dan bu soru. Şimdi MySQL bir çözüm sunuyor:

Çevrimiçi DDL (Veri Tanımlama Dili)

DDL (öncelikle ALTER TABLE) işlemleri sırasında InnoDB tablolarının performansını, eşzamanlılığını ve kullanılabilirliğini artıran bir özellik. Ayrıntılar için Bölüm 14.11, "InnoDB ve Çevrimiçi DDL" ye bakın.

Detaylar işlemin türüne göre değişiklik gösterir. Bazı durumlarda, ALTER TABLE devam ederken tablo eşzamanlı olarak değiştirilebilir. İşlem, bir tablo kopyası yapılmadan veya özel olarak optimize edilmiş bir tablo kopyası türü kullanılmadan gerçekleştirilebilir. Alan kullanımı innodb_online_alter_log_max_size yapılandırma seçeneği ile kontrol edilir.

DDL işlemi sırasında, tabloya erişimi tamamen engellemeyi (LOCK = EXCLUSIVE yan tümcesi), sorgulara izin verip DML'ye izin vermemeyi (LOCK = SHARED yan tümcesi) veya tam sorgu ve DML'ye izin vermeyi seçerek, performans ve eşzamanlılık arasındaki dengeyi ayarlamanıza olanak tanır. tabloya erişim (LOCK = NONE cümlesi). LOCK yan tümcesini atladığınızda veya LOCK = DEFAULT seçeneğini belirlediğinizde, MySQL işlemin türüne bağlı olarak mümkün olduğunca fazla eşzamanlılığa izin verir.

Tablonun yeni bir kopyasını oluşturmak yerine, mümkün olduğunda yerinde değişiklik yapmak, tablonun kopyalanması ve ikincil dizinlerin yeniden yapılandırılmasıyla ilişkili disk alanı kullanımında ve G / Ç ek yükünde geçici artışları önler.

Daha fazla bilgi için bkz. MySQL 5.6 Referans Kılavuzu -> InnoDB ve Çevrimiçi DDL .

Görünüşe göre çevrimiçi DDL, MariaDB'de de mevcut

Alternatif olarak, ALTER TABLE'ınızın eşzamanlı işlemleri engellemediğinden (kilit almadığından) emin olmak için ALTER ONLINE TABLE'ı kullanabilirsiniz. LOCK = NONE ile eşdeğerdir.

ALTER TABLE hakkında MariaDB KB


3
Bunu en üste çıkarmak için oy kullanmaktan başka bir yolun olmaması utanç verici, çünkü çoğunlukla diğer tüm cevapları tamamen geçersiz kılıyor çünkü artık MySQL'in mevcut sürümüne referans vermiyorlar.
Burhan Ali


14

Bu bir seçenekse Postgres'i öneririm. Postgres ile, aşağıdaki prosedürlerde esasen kesinti olmaz:

Diğer bir harika özellik, çoğu DDL ifadesinin işlemsel olmasıdır, bu nedenle bir SQL işlemi içinde tüm bir geçiş yapabilirsiniz ve bir şeyler ters giderse, her şey geri alınır.

Yazdığım bu belki de diğer esasına ilişkin bazı daha fazla fikir tutabilir, biraz önce.


6
Postgres, başkalarının bu tablodan okumasını engelleyerek, alter üzerinde özel bir kilit oluşturmaya devam ediyor.
clofresh

5
"Esasen kesinti yok" bitine katılmıyorum. Clofresh'in dediği gibi, ALTER TABLE, tüm eşzamanlı okuma ve yazma işlemlerini engelleyen tabloda özel bir kilit yakalar. Tecrübelerime göre, aktif masalar için çoğu zaman kilidi bile alamazsınız (ALTER TABLE açlıktan ölecektir). Ve işlemlerle, çok dikkatli değilseniz kolayca kilitlenmelerle sonuçlanabilirsiniz. Bu nedenle, Postgres'te mevcut tabloları değiştirirken artık her zaman duruş sürelerini ayarlıyorum.
Pankrat

1
daha ayrıntılı bir açıklama: dba.stackexchange.com/questions/27153/… özel kilidin çıkarımlarından ve bunun üzerinden geçmenin bazı yollarından bahsediyor
John Douthat

4
Evet, postgres'de bir tabloyu değiştirmek özel bir kilit sağlar, ancak işlemin kendisi milisaniyeler içinde tamamlandığı için çoğu durumda bu pratik olarak önemsizdir. İş gününün ortasında yüz milyon satırlık tablolara, sıfır kesinti ile sonuçlanan sütunlar ekledim.
Nuh Yetter

2
@cobbzilla Evet, DROP COLUMN da aynı hızda. Kaputun altında temelde yaptığı şey sütunu gizli olarak işaretlemektir. Bu sütunda bırakılmadan önce var olan değerler hala veri dosyalarındadır (ve diğer işlemler tarafından görülebilir) ve siz bir VAKUM DOLU yapana kadar ve bu durumda kalacaktır.
Nuh Yetter

7

Diğer veritabanları hakkında soru sorduğunuz için, işte Oracle hakkında bazı bilgiler.

Oracle tablosuna bir NULL sütunu eklemek, yalnızca veri sözlüğünü güncellediği için çok hızlı bir işlemdir. Bu, çok kısa bir süre için masanın üzerinde özel bir kilit oluşturur. Bununla birlikte, depedant depolanan prosedürleri, görünümleri, tetikleyicileri vb. Geçersiz kılacaktır. Bunlar otomatik olarak yeniden derlenecektir.

Buradan gerekirse ONLINE maddesini kullanarak dizin oluşturabilirsiniz. Yine, sadece çok kısa veri sözlüğü kilitlenir. Dizine eklenecek şeyleri arayan tüm tabloyu okur, ancak bunu yaparken kimseyi engellemez.

Bir yabancı anahtar eklemeniz gerekiyorsa, bunu yapabilir ve Oracle'ın verilerin doğru olduğuna dair size güvenmesini sağlayabilirsiniz. Aksi takdirde, tüm tabloyu okuması ve yavaş olabilecek tüm değerleri doğrulaması gerekir (önce dizininizi oluşturun).

Yeni sütunun her satırına varsayılan veya hesaplanan bir değer koymanız gerekiyorsa, büyük bir güncelleme veya belki de yeni verileri dolduran küçük bir yardımcı program çalıştırmanız gerekir. Bu yavaş olabilir, özellikle sıralar çok büyürse ve artık bloklarına sığmazsa. Bu işlem sırasında kilitlenme yönetilebilir. Uygulamanızın halen çalışmakta olan eski sürümünün bu sütunu bilmemesi nedeniyle sinsi bir tetikleyiciye veya bir varsayılan belirtmeye ihtiyacınız olabilir.

Oradan, uygulama sunucularınızda kodun yeni sürümüne geçiş yapabilirsiniz ve kod çalışmaya devam eder. Sinsi tetiğinizi bırakın.

Alternatif olarak, bu tür şeyleri yapmak için tasarlanmış bir kara kutu olan DBMS_REDEFINITION'ı kullanabilirsiniz.

Tüm bunları test etmek o kadar zahmetli ki, büyük bir sürümü yayınladığımızda Pazar sabahı erken saatlerde bir kesinti yaşıyoruz.


3

Uygulama güncellemeleri yaparken veritabanınız için kesinti süresini karşılayamıyorsanız, yüksek kullanılabilirlik için iki düğümlü bir küme tutmayı düşünmelisiniz. Basit bir çoğaltma kurulumuyla, önerdiğiniz gibi neredeyse tamamen çevrimiçi yapısal değişiklikler yapabilirsiniz:

  • tüm değişikliklerin pasif bir köle üzerinde kopyalanmasını bekleyin
  • pasif köleyi aktif yönetici olarak değiştirin
  • yapısal değişiklikleri eski ustaya yap
  • değişiklikleri yeni ana makineden eski ana ana bilgisayara kopyala
  • ana değiştirmeyi tekrar yapın ve yeni uygulama dağıtımını aynı anda yapın

Her zaman kolay değildir, ancak genellikle 0 kesinti ile çalışır! İkinci düğümün yalnızca pasif olması gerekmez, test etmek, istatistik yapmak veya geri dönüş düğümü olarak kullanılabilir. Altyapınız yoksa, tek bir makinede (iki MySQL örneğiyle) çoğaltma kurulabilir.


1
Eski ana birim kümenin dışında mı yoksa kümenin içinde mi?
John Chornelius

2

Hayır! MyISAM tablolarını kullanıyorsanız, en iyi anladığım kadarıyla sadece masa kilitleri yaparlar - kayıt kilitleri yoktur, sadece basitlik yoluyla her şeyi hiper hızlı tutmaya çalışırlar. (Diğer MySQL tabloları farklı şekilde çalışır.) Her durumda, tabloyu başka bir tabloya kopyalayabilir, değiştirebilir ve ardından farklılıklar için güncelleyerek onları değiştirebilirsiniz.

Bu o kadar büyük bir değişiklik ki, herhangi bir DBMS'nin onu destekleyeceğinden şüpheliyim. İlk etapta tablodaki verilerle bunu yapabilmek bir fayda olarak kabul edilir.



Evet, MySQL sapmadır. Bu yüzden "standart" tablolar konusunda spesifik davrandım.
dkretz

Yazdınız - standart MySQL tabloları yalnızca masa kilitleri yapıyor - ki bu yanlış.
Eran Galperin

Bunu, alıntı yaptığınız sayfadaki MyISAM (yani MySQL standardı) tabloları hakkında nasıl yorumluyorsunuz? "MySQL, MyISAM ve MEMORY tabloları için tablo düzeyinde kilitleme, BDB tabloları için sayfa düzeyinde kilitleme ve InnoDB tabloları için satır düzeyinde kilitleme kullanır."
dkretz

bazı depolama motorları sıra seviyesinde kilitleme kullanır ve bazıları masa seviyesinde kilitleme kullanır. Standart bir depolama motoru yok (belki phpMyAdmin'de varsayılanı kastettiniz ...)
Eran Galperin

2

Geçici çözüm...

Diğer çözüm, yeni sütununuzla birlikte orijinal tablonun birincil anahtarını içeren başka bir tablo eklemek olabilir.

Birincil anahtarınızı yeni tabloya doldurun ve yeni tablonuzdaki yeni sütun için değerleri doldurun ve belirli işlemler için bu tabloya katılmak üzere sorgunuzu değiştirin ve ayrıca bu sütun değeri için ayrı ayrı eklemeniz, güncellemeniz gerekir.

Kesinti süresi elde edebildiğinizde, orijinal tabloyu değiştirebilir, DML sorgularınızı değiştirebilir ve daha önce oluşturulan yeni tablonuzu bırakabilirsiniz.

Aksi takdirde, percona'dan kümeleme yöntemi, çoğaltma, pt-çevrimiçi-şema aracı için gidebilirsiniz.


1

Innodb eklentisi kullanılarak, yalnızca ikincil dizinleri ekleyen veya çıkaran ALTER TABLE deyimleri "hızlı", yani tabloyu yeniden oluşturmadan yapılabilir.

Genel olarak konuşursak, MySQL'de herhangi bir ALTER TABLE, çok uzun bir zaman alabilen tüm tablonun yeniden oluşturulmasını içerir (yani, tabloda yararlı miktarda veri varsa).

ALTER TABLE ifadelerinin düzenli olarak yapılması gerekmeyecek şekilde uygulamanızı gerçekten tasarlamanız gerekiyor; Beklemeye hazır olmadığınız veya küçük tabloları değiştirmediğiniz sürece uygulamanın normal çalışması sırasında kesinlikle ALTER TABLE yapılmasını istemezsiniz.


1

İki yaklaşımdan birini tavsiye ederim:

  1. Veritabanı tablolarınızı potansiyel değişiklikleri göz önünde bulundurarak tasarlayın. Örneğin, içerikteki veri alanlarını düzenli olarak değiştiren İçerik Yönetim Sistemleri ile çalıştım. Fiziksel veritabanı yapısını ilk CMS saha gereksinimlerini karşılayacak şekilde oluşturmak yerine, esnek bir yapıda inşa etmek çok daha iyidir. Bu durumda, esnek XML verilerini tutmak için bir blob metin alanı (örneğin varchar (max)) kullanma. Bu, yapısal değişiklikleri çok daha az sıklıkta yapar. Yapısal değişiklikler maliyetli olabilir, bu nedenle burada da maliyetin bir faydası vardır.

  2. Sistem bakım süresine sahip olun. Sistem değişiklikler sırasında (aylık vb.) Çevrimdışı olur ve değişiklikler günün en yoğun trafiğe maruz kalan saatinde (örneğin, öğleden sonra 3-5: 00'da) planlanır. Değişiklikler, üretim sunumundan önce aşamalandırılır, bu nedenle duruş süreleri için iyi bir sabit pencere tahminine sahip olursunuz.

2a. Yedek sunuculara sahip olun, böylece sistem kesintiye uğradığında, tüm site kapanmaz. Bu, tüm siteyi kapatmadan güncellemelerinizi kademeli bir şekilde "yaymanıza" olanak tanır.

Seçenek 2 ve 2a uygun olmayabilir; bunlar yalnızca daha büyük siteler / işlemler için olma eğilimindedir. Ancak bunlar geçerli seçenekler ve burada sunulan tüm seçenekleri kişisel olarak kullandım.


1

Hala bunu okuyan biri varsa veya buraya gelirse, bu mongodb gibi bir NoSQL veritabanı sistemi kullanmanın en büyük yararıdır. Milyonlarca satır ve yüksek yazma içeren büyük bir tabloya ek özellikler için sütunlar veya dizinler eklemek için tabloyu değiştirirken aynı sorunu yaşadım. Sonunda çok uzun bir süre kilitlenecek ve bunu LIVE veritabanında yapmak kullanıcılarımızı hayal kırıklığına uğratacaktır. Küçük masalarda bundan kurtulabilirsiniz.

"Tablolarımızı değiştirmekten kaçınmak için tasarlamamız" gerektiğinden nefret ediyorum. Bugünün web sitesi dünyasında işe yaradığını sanmıyorum. İnsanların yazılımınızı nasıl kullanacağını tahmin edemezsiniz, bu yüzden kullanıcı geri bildirimlerine göre işleri hızla değiştirirsiniz. Mongodb ile, herhangi bir kesinti olmadan istediğiniz zaman "sütunlar" ekleyebilirsiniz. Onları gerçekten eklemiyorsunuz, sadece yeni sütunlarla veri eklersiniz ve otomatik olarak yapar.

Kontrol etmeye değer: www.mongodb.com


2
MySQL hala birçok sistemde kullanılıyor, bu yüzden soru gerçekten de hararetli bir NoSQL destekçisi olsam da SQL RDBMS'de şema değişikliğinin nasıl sağlanacağıyla ilgili.
Alexy

1

Genel olarak cevap "Hayır" olacaktır. Tablonun yapısını değiştiriyorsunuz, bu da potansiyel olarak çok fazla güncelleme gerektirecektir "ve kesinlikle buna katılıyorum. Bunu sık sık yapmayı düşünüyorsanız," kukla "sütunlara bir alternatif sunacağım - VIEWbunun yerine s kullanın için tabloların SELECTverileri ing. IIRC, bir görünüm tanımını değiştirme nispeten hafiftir ve sorgu planı derlenmiş bir görünümü aracılığıyla dolaylı söz yapılır. gider yeni bir tabloya sütun eklemek ve yapmak zorunda olduğunu ifade etti JOINsütunda görünüm .

Elbette bu, yalnızca silme işlemlerinin basamaklandırılması ve başka şeyler yapmak için yabancı anahtarlar kullanabiliyorsanız işe yarar. Diğer bonus, verilerin bir kombinasyonunu içeren yeni bir tablo oluşturabilmeniz ve müşteri kullanımını bozmadan görünümü ona yönlendirebilmenizdir.

Sadece bir düşünce.


1

Bu bakımdan Postgres ve MySQL arasındaki fark, Postgres'te bir tabloyu yeniden oluşturmaması, Oracle'a benzer veri sözlüğünü değiştirmesidir. Bu nedenle, işlem hızlıdır, ancak yine de yukarıda başkaları tarafından belirtildiği gibi çok kısa bir süre için özel bir DDL tablo kilidi tahsis etmek gerekir.

MySQL'de işlem, işlemleri bloke ederken verileri yeni bir tabloya kopyalayacaktır; bu, v. 5.6'dan önceki MySQL DBA'ları için temel sorun olmuştur.

İyi haber şu ki, MySQL 5.6 sürümünden bu yana kısıtlama çoğunlukla kaldırıldı ve artık MYSQL DB'nin gerçek gücünden yararlanabilirsiniz.


3
MySql 5.6'daki bir değişiklikle ilgili bir referansa bağlanmaya çalışıyormuşsunuz gibi görünüyor, ancak işe yaramadı. Lütfen tekrar deneyin.
dg99



0

Kukla sütunlar, türlerini tahmin edebiliyorsanız (ve onları null yapılabilir hale getirebiliyorsanız) iyi bir fikirdir. Depolama motorunuzun boş değerleri nasıl işlediğini kontrol edin.

MyISAM, telefonda, havaalanından geçerken bir masa isminden bahsetseniz bile, her şeyi kilitleyecektir. Sadece bunu yapar ...

Bununla birlikte, kilitler gerçekten o kadar da önemli değil; Her satıra yeni sütun için varsayılan bir değer eklemeye çalışmadığınız, ancak boş olarak kalmasına izin verdiğiniz ve depolama motorunuz yazmaya devam etmeyecek kadar akıllı olduğu sürece, yalnızca bir kilitle tamam olmalısınız meta verileri güncellemek için yeterince uzun süre tutuldu. Yeni bir değer yazmaya çalışırsanız, kızardınız demektir.


1
InnoDB tablosuna bir NULL sütun eklemeyi denedim ve tüm tabloyu yeniden oluşturmak zorunda kaldı; basit bir "meta verileri güncelleme" işlemi değildir.
Daniel

Bence fikir, tasarlandığı zaman veritabanına fazladan, boş değer atanabilir sütunlar eklemekti, böylece yeni bir özellik gerekliyse, onu kullanmaya başlayarak yeni sütun "ekleyebilir". Güzel bir adı olmayacak, ancak veri türü doğru seçilmişse / tahmin edilmişse çalışmalıdır.
supercat

0

TokuDB, sütunlar ekleyebilir / bırakabilir ve "sıcak" dizinleri ekleyebilir, tablo işlem boyunca tamamen kullanılabilir. Www.tokutek.com adresinden temin edilebilir.


-6

Pek sayılmaz.

Sonuçta, tablonun temel yapısını değiştiriyorsunuz ve bu, temeldeki sistem için oldukça önemli olan bir bilgi parçasıdır. Ayrıca (muhtemelen) verilerin çoğunu diskte hareket ettiriyorsunuz.

Bunu çok yapmayı planlıyorsanız, tabloyu ileride kullanabileceğiniz "sahte" sütunlarla doldurmanız daha iyi olur.


3
Bir tabloyu kukla sütunlarla doldurmak gerçekten kötü bir fikir gibi görünüyor.
Jost
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.