Perintah DELETE tidak menyelesaikan pada 30.000.000 tabel baris


22

Saya telah mewarisi database dan saya ingin membersihkan dan mempercepatnya. Saya memiliki tabel yang berisi 30.000.000 baris, banyak di antaranya adalah data sampah yang disisipkan karena kesalahan atas nama programmer kami. Sebelum saya menambahkan indeks baru yang lebih dioptimalkan, saya mengonversi tabel dari MyISAM ke InnoDB dan saya ingin menghapus banyak baris yang berisi data sampah.

Basis datanya adalah MySQL 5.0 dan saya memiliki akses root ke server. Saya pertama kali menjalankan perintah ini melalui Adminer dan kemudian phpMyAdmin, keduanya dengan hasil yang sama.

Perintah yang saya jalankan adalah,

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

Pada dasarnya, hapus apa pun di kolom ini yang dimulai dengan tanda hubung -.

Ini berjalan sekitar 3-5 menit dan kemudian ketika saya melihat daftar proses, itu hilang.

Saya kemudian lari,

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

dan mengembalikan jutaan baris.

Mengapa pernyataan penghapusan saya tidak lengkap?

NB, saya tahu betapa ketinggalannya MySQL 5.0. Saya sedang berusaha untuk memindahkan DB ke MySQL 5.6 w InnoDB (mungkin MariaDB 10 w XtraDB) tetapi sampai itu terjadi, saya ingin menjawab ini dengan DB apa adanya.

-

Edit dihapus, lihat jawaban saya.

Jawaban:


24

Silakan lihat Arsitektur InnoDB (gambar dari Percona CTO Vadim Tkachenko)

Pipa InnoDB

Baris yang Anda hapus sedang ditulis ke dalam batalkan log. File ibdata1 seharusnya tumbuh sekarang selama durasi penghapusan. Menurut mysqlperformanceblog.comReasons for run-away main Innodb Tablespace :

  • Banyak Perubahan Transaksional
  • Transaksi Sangat Panjang
  • Thread Pembersihan Tertinggal

Dalam kasus Anda, alasan # 1 akan menempati satu segmen rollback bersama dengan beberapa ruang undo karena Anda menghapus baris. Baris-baris itu harus duduk di ibdata1 sampai penghapusan selesai. Ruang itu secara logis dibuang tetapi ruang disk tidak menyusut kembali.

Anda harus membunuh penghapusan itu sekarang. Setelah Anda membunuh permintaan penghapusan, itu akan mengembalikan baris yang dihapus.

Anda melakukan ini sebagai gantinya:

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;

Anda bisa melakukan ini terhadap versi tabel MyISAM terlebih dahulu. Kemudian, konversikan ke InnoDB.


21

Saya pikir kita mungkin telah memperumit jawaban yang diperlukan dalam kasus saya . Saya tidak ragu bahwa Roland & Rick James benar dengan pembuatan tabel sementara mereka, hanya menyuntikkan baris yang melewati filter NOT LIKE '-%'tetapi solusi untuk saya "lebih mudah" karena ada kesalahan penting yang saya tidak sadari sampai sekarang dan untuk saya minta maaf.

Saya menjalankan kueri di mysqlprompt interaktif dan memperhatikan pesan kesalahan,

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

Melalui Googleing kesalahan, saya menemukan solusinya adalah meningkatkan innodb_buffer_pool_sizemelalui /etc/my.cnffile dan me-reboot daemon mysql. Untuk server saya diatur ke default 8Mdan saya meningkatkannya ke 1G(server memiliki 32GB dan ini adalah satu-satunya tabel yang saat ini InnoDB).

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

Kemudian saya dapat menjalankan perintah dan menghapus 23 juta catatan dalam ~ 27 menit.

Bagi mereka yang penasaran dengan apa yang innodb_buffer_pool_sizeharus diatur, catat berapa banyak RAM yang Anda miliki dan lihat thread ini yang memberikan perkiraan perkiraan dalam GB.


12

Saran Roland dapat dipercepat beberapa dengan melakukan kedua hal sekaligus:

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;

Tapi di sini ada sebuah blog yang menjelaskan cara melakukan DELETE besar dalam potongan, daripada mengambil selamanya: http://mysql.rjweb.org/doc.php/deletebig Intinya adalah berjalan melalui meja melalui PK, melakukan 1K baris sekaligus. (Tentu saja ada lebih banyak detail yang harus diperhatikan.)

Dan blog ini membahas potensi gangguan dalam konversi ke InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb


5

Insting pertama saya adalah melakukan lebih dari satu, menghapus lebih kecil dengan membatasi jumlah hasil query, dan menjalankan query berkali-kali:

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

Kelemahan dari pendekatan ini: Setiap penghapusan akan memakan waktu lebih lama dan lebih lama. Ini karena harus melewati lebih banyak dan lebih banyak baris yang tidak cocok dengan WHERE.
Rick James

Benar, tetapi jika proses ini tidak terlalu sering terjadi, beberapa pemindaian tabel lengkap tidak boleh seburuk masalah semula diselesaikan, yaitu bahwa kueri tidak pernah selesai karena membatalkan ukuran log.
kristianp

Poin yang valid. (Saya akan membuat yang LIMITlebih rendah; katakanlah 10000.)
Rick James

4

Solusi termudah adalah dengan tidak melakukan itu - lakukan penghapusan yang lebih kecil, yang dapat lebih mudah diproses.

Dalam hal ini saya akan merekomendasikan mencoba menghapus berurutan dari formulir:

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

2

Mungkin Anda bisa melakukan sesuatu seperti ini:

  • Tambahkan bidang baru yang disebut deleted.
  • Lakukan pembaruan seperti UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Setel cronuntuk menghapus ini di malam hari.

Pembaruan dapat berlangsung selama penghapusan.
Rick James
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.