DELETE komutu 30.000.000 satırlık tabloda tamamlanmadı


22

Bir veritabanını miras aldım ve onu temizlemek ve hızlandırmak için arıyorum. Programlayıcı adına yapılan bir hata nedeniyle önemsiz verilerden oluşan 30.000.000 satır içeren bir tablom var. Herhangi bir yeni ve daha optimize edilmiş dizin eklemeden önce, tabloyu MyISAM'dan InnoDB'ye dönüştürdüm ve önemsiz veriler içeren birçok satırı silmek istiyorum.

Veritabanı MySQL 5.0 ve sunucuya root erişimim var. Bu komutları önce Adminer'den sonra da phpMyAdmin'de çalıştırdım, ikisi de aynı sonuçlarla.

Çalıştırdığım komut,

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

Temel olarak, bu sütunda bir çizgi ile başlayan herhangi bir şeyi silin -.

Yaklaşık 3-5 dakika sürüyor ve işlem listesini görüntülediğimde işlem bitiyor.

Sonra koşarım

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

ve milyonlarca satır döndürür.

Delete deyim neden tamamlanmıyor?

PS, MySQL 5.0'ın güncel olmadığının farkındayım. DB'yi MySQL 5.6 w InnoDB'ye (belki MariaDB 10 w XtraDB) taşımaya çalışıyorum, ancak bu gerçekleşene kadar, DB ile olduğu gibi cevap vermeye çalışıyorum.

-

Düzenleme kaldırıldı, cevabımı görün.

Yanıtlar:


24

Lütfen InnoDB Mimarisine bakınız (Percona CTO Vadim Tkachenko'nun resmi)

InnoDB Tesisat

Sildiğiniz satırlar geri alma günlüklerine yazılır. İbdata1 dosyası şu anda silme süresi boyunca büyüyor olmalıdır. Mysqlperformanceblog.com'aReasons for run-away main Innodb Tablespace göre :

  • Birçok İşlem Değişikliği
  • Çok Uzun İşlemler
  • Gecikme Boşaltma İpliği

Senin durumunda, # 1 nedeni, satırları sildiğiniz için geri alma alanının bir kısmı ile birlikte bir geri alma bölümünü işgal eder. Silme bitinceye kadar bu satırların ibdata1 içinde oturması gerekir. Bu alan mantıksal olarak atıldı, ancak disk alanı geri çekilmiyor.

Şu an o silmeyi öldürmen gerekiyor. Silme sorgusunu bir kez öldürdüğünüzde, silinen satırları geri alır.

Bunun yerine bunu yapın:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Bunu önce tablonun MyISAM versiyonuna karşı yapmış olabilirsiniz. Ardından, InnoDB'ye dönüştürün.


21

Bence gerekli olan cevabı overcomplicated olabileceğini düşünüyorum benim durumumda . Roland & Rick James'in geçici bir tablo oluştururken doğru olduklarından şüphem yok, yalnızca filtreyi geçen satırları enjekte NOT LIKE '-%'ediyorum, ama benim için çözüm "daha kolaydı" çünkü şu ana kadar ve farkında olmadığım önemli bir hata vardı. özür dilerim.

Sorguyu mysqletkileşimli istemde çalıştırdım ve hata mesajını fark ettim.

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

Googleing hata sayesinde , çözümün dosya innodb_buffer_pool_sizearacılığıyla artması /etc/my.cnfve mysql arka planının yeniden başlatılması olduğunu gördüm . Sunucum için varsayılan olarak ayarlandı 8Mve 1Gartırdım (sunucuda 32 GB ve şu anda InnoDB olan tek tablodur).

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

Sonra komutu çalıştırıp 23 milyon kaydı ~ 27 dakikada sildim.

Neyin innodb_buffer_pool_sizeayarlanması gerektiğini merak edenler için, ne kadar RAM’iniz olduğunu not edin ve ardından GB’lerde önerilen bir tahmin veren bu konuya göz atın .


12

Roland'ın önerisi, her ikisini de aynı anda yaparak bazılarını hızlandırabilir:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Ama işte sonsuza dek sürmek yerine, büyük parçaların nasıl silindiğini açıklayan bir blog: http://mysql.rjweb.org/doc.php/deletebig Oyunun özeti, PK üzerinden masadan yürümek, 1K yapıyor aynı anda satır. (Tabii ki dikkat edilmesi gereken daha fazla detay var.)

Ve bu blog InnoDB'ye dönüşümdeki potansiyel kazancı ele alıyor: http://mysql.rjweb.org/doc.php/myisam2innodb


5

İlk içgüdüm, sorgu sonuçlarının sayısını sınırlandırarak ve sorguyu birden çok kez çalıştırarak birden çok daha küçük silme yapmak olacaktı:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

Bu yaklaşımın bir sakıncası: Her silme daha uzun ve daha uzun sürecektir. Bunun nedeni, eşleşmeyen daha fazla satıra atlaması gerekmesidir WHERE.
Rick James

Doğru, ancak bu işlem çok sık gerçekleşmiyorsa, birden çok tam tablo taraması, çözülen orijinal sorun kadar kötü olmamalıdır; bu nedenle, sorgunun günlük boyutunu geri alma nedeniyle hiçbir zaman tamamlaması gerekmez.
kristianp

Geçerli nokta (Ben LIMITaşağı yapardım ; 10000 diyelim.)
Rick James

4

En kolay çözüm basitçe bunu yapmamaktır - daha kolay işlenebilen daha küçük bir silme işlemi yapın.

Bu durumda, formun ardışık silmelerini denemeyi tavsiye ederdim:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

Belki böyle bir şey yapabilirsin:

  • Adlı yeni bir alan ekleyin deleted.
  • Gibi bir güncelleme yapın UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • cronBunu gece saatlerinde silmek için ayarlayın .

Güncelleme , silme işlemine kadar sürebilir.
Rick James
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.