Lệnh XÓA không hoàn thành trên bảng 30.000.000


22

Tôi đã kế thừa một cơ sở dữ liệu và đang tìm cách dọn dẹp và tăng tốc nó. Tôi có một bảng chứa 30.000.000 hàng, nhiều trong số đó là dữ liệu rác được chèn do lỗi thay cho lập trình viên của chúng tôi. Trước khi tôi thêm bất kỳ chỉ mục mới, tối ưu hóa nào hơn, tôi đã chuyển đổi bảng từ MyISAM sang InnoDB và đang tìm cách xóa rất nhiều hàng có chứa dữ liệu rác.

Cơ sở dữ liệu là MySQL 5.0 và tôi có quyền truy cập root vào máy chủ. Lần đầu tiên tôi chạy các lệnh này thông qua Adminer và sau đó là phpMyAdmin, cả hai đều có cùng kết quả.

Lệnh tôi đang chạy là,

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

Về cơ bản, xóa bất cứ thứ gì trong cột này bắt đầu bằng dấu gạch ngang -.

Nó chạy trong khoảng 3-5 phút và sau đó khi tôi xem danh sách quy trình, nó đã biến mất.

Tôi sau đó chạy

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

và nó trả về hàng triệu hàng.

Tại sao tuyên bố xóa của tôi không hoàn thành?

PS, tôi biết rằng MySQL 5.0 đã lỗi thời như thế nào. Tôi đang làm việc để chuyển DB sang MySQL 5.6 w InnoDB (có thể là MariaDB 10 w XtraDB) nhưng cho đến khi điều đó xảy ra, tôi đang tìm cách trả lời điều này với DB.

-

Chỉnh sửa loại bỏ, xem câu trả lời của tôi.

Câu trả lời:


24

Vui lòng xem Kiến trúc của InnoDB (ảnh từ Percona CTO Vadim Tkachenko)

Hệ thống nước InnoDB

Các hàng bạn đang xóa đang được ghi vào nhật ký hoàn tác. Tệp ibdata1 sẽ được phát triển ngay bây giờ trong suốt thời gian xóa. Theo thông tin của mysqlperformanceblog.comReasons for run-away main Innodb Tablespace :

  • Rất nhiều thay đổi giao dịch
  • Giao dịch rất dài
  • Lagging Purge Chủ đề

Trong trường hợp của bạn, lý do số 1 sẽ chiếm một phân đoạn rollback cùng với một số khoảng trống hoàn tác do bạn đang xóa các hàng. Những hàng đó phải nằm trong ibdata1 cho đến khi xóa xong. Không gian đó được loại bỏ một cách hợp lý nhưng không gian đĩa không bị thu hẹp lại.

Bạn cần phải xóa nó ngay bây giờ. Khi bạn hủy bỏ truy vấn xóa, nó sẽ khôi phục các hàng đã xóa.

Bạn làm điều này thay vào đó:

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;

Bạn có thể đã làm điều này so với phiên bản MyISAM của bảng trước tiên. Sau đó, chuyển đổi nó thành InnoDB.


21

Tôi nghĩ rằng chúng tôi có thể đã quá phức tạp câu trả lời được yêu cầu trong trường hợp của tôi . Tôi không nghi ngờ rằng cả Roland & Rick James đều đúng với việc họ tạo một bảng tạm thời, chỉ tiêm các hàng vượt qua bộ lọc NOT LIKE '-%'nhưng giải pháp cho tôi là "dễ dàng hơn" vì có một lỗi quan trọng mà tôi không biết cho đến bây giờ và cho rằng tôi xin lỗi.

Tôi đã chạy truy vấn trong mysqldấu nhắc tương tác và nhận thấy thông báo lỗi,

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

Thông qua Google lỗi, tôi thấy giải pháp là tăng innodb_buffer_pool_sizethông qua /etc/my.cnftệp và khởi động lại daemon mysql. Đối với máy chủ của tôi, nó được đặt thành mặc định 8Mvà tôi đã tăng nó lên 1G(máy chủ có 32GB và đây là bảng duy nhất hiện tại là InnoDB).

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

Sau đó, tôi đã có thể chạy lệnh và xóa 23 triệu bản ghi trong ~ 27 phút.

Đối với những người tò mò innodb_buffer_pool_sizenên đặt cái gì , hãy lưu ý xem bạn có bao nhiêu RAM và sau đó hãy xem chủ đề này đưa ra ước tính được đề xuất tính bằng GB.


12

Đề xuất của Roland có thể được tăng tốc một số bằng cách làm cả hai việc cùng một lúc:

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;

Nhưng đây là một blog giải thích làm thế nào để thực hiện các thao tác XÓA lớn trong các khối, thay vì dường như mất mãi mãi: http://mysql.rjweb.org/doc.php/deletebig Ý chính là đi qua bàn thông qua PK, thực hiện 1K hàng cùng một lúc. (Tất nhiên có nhiều chi tiết cần chú ý hơn.)

Và blog này đề cập đến các vấn đề tiềm năng trong việc chuyển đổi sang InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb


5

Bản năng đầu tiên của tôi sẽ là thực hiện nhiều thao tác xóa nhỏ hơn bằng cách giới hạn số lượng kết quả truy vấn và chạy truy vấn nhiều lần:

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

Một nhược điểm của phương pháp này: Mỗi lần xóa sẽ mất nhiều thời gian hơn. Điều này là do nó cần bỏ qua ngày càng nhiều hàng không khớp với WHERE.
Rick James

Đúng, nhưng nếu quá trình này không xảy ra quá thường xuyên, nhiều lần quét bảng hoàn chỉnh sẽ không tệ như vấn đề ban đầu đang được giải quyết, đó là truy vấn không bao giờ hoàn thành do hoàn tác kích thước nhật ký.
kristianp

Điểm hợp lệ. (Tôi sẽ LIMIThạ thấp hơn; nói 10000.)
Rick James

4

Giải pháp đơn giản nhất là đơn giản là không làm điều đó - thực hiện xóa nhỏ hơn, có thể dễ dàng xử lý hơn.

Trong trường hợp này, tôi đã khuyên bạn nên thử xóa các biểu mẫu liên tiếp:

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

2

Có lẽ bạn có thể làm một cái gì đó như thế này:

  • Thêm một lĩnh vực mới được gọi là deleted.
  • Làm một bản cập nhật như thế nào UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Đặt cronđể xóa điều này vào ban đêm.

Việc cập nhật có thể mất chừng nào việc xóa.
Rick James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.