Làm cách nào để cải thiện hiệu năng của InnoDB DELETE?


9

Vì vậy, tôi có bảng kiểm toán này (theo dõi các hành động trên bất kỳ bảng nào trong cơ sở dữ liệu của tôi):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

và tôi cần bắt đầu lưu trữ các mục đã lỗi thời. Bảng đã tăng lên khoảng 50 triệu hàng, vì vậy cách nhanh nhất tôi có thể xóa các hàng là xóa một bảng tại một thời điểm (dựa trên tableName).

Điều này hoạt động khá tốt nhưng trên một số bảng nặng, nó sẽ không hoàn thành. Truy vấn của tôi xóa tất cả các mục có deletehành động liên quan trên kết hợp tupleID / tableName:

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

Tôi để nó chạy trên máy chủ của tôi trong 3 ngày và nó không bao giờ hoàn thành cho bảng lớn nhất. Đầu ra giải thích (nếu tôi chuyển sang xóa để chọn:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

Vì vậy, 4 triệu hàng không nên mất 3 ngày để xóa, tôi nghĩ vậy. Tôi có innodb_buffer_pool_size được đặt thành 3GB và máy chủ không được đặt để sử dụng one_file_per_table. Những cách nào khác tôi có thể cải thiện hiệu suất xóa InnoDB? (Chạy MySQL 5.1.43 trên Mac OSX)

Câu trả lời:


11

Bạn có thể xóa dữ liệu theo lô.

Trong SQL Server, cú pháp là delete top Xcác hàng từ một bảng. Sau đó, bạn thực hiện theo vòng lặp, với một giao dịch cho mỗi đợt (tất nhiên nếu bạn có nhiều hơn một câu lệnh), vì vậy, để giữ cho các giao dịch ngắn và chỉ duy trì khóa trong thời gian ngắn.

Trong cú pháp MySQL: DELETE FROM userTable LIMIT 1000

Có những hạn chế về điều đó (chẳng hạn như không thể sử dụng LIMITtrong việc xóa với các phép nối), nhưng trong trường hợp này bạn có thể làm theo cách đó.

Có một mối nguy hiểm khác để sử dụng LIMITvới DELETEkhi nói đến nhân rộng; các hàng bị xóa đôi khi không bị xóa theo cùng một thứ tự trên nô lệ vì nó đã bị xóa trên bản gốc.


6

Hãy thử sử dụng một cách tiếp cận bảng tạm thời. Hãy thử một cái gì đó như thế này:

Bước 1) CREATE TABLE track_table_new LIKE track_table;

Bước 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

Bước 3) ALTER TABLE track_table RENAME track_table_old;

Bước 4) ALTER TABLE track_table_new RENAME track_table;

Bước 5) DROP TABLE track_table_old;

Tôi không bao gồm trường tuple trong Bước 2. Vui lòng xem nếu điều này tạo ra hiệu ứng mong muốn. Nếu đây là những gì bạn muốn, bạn có thể muốn bỏ hoàn toàn trường tuple trừ khi bạn sử dụng trường tuple vì những lý do khác.


Đó là một giải pháp thú vị. Tôi cần trường tuple trong bảng. tableName / tupleID là khóa ngoại không xác định của bảng đang được ghi. Không xác định vì cho đến gần đây, bảng này là MyISAM, không hỗ trợ khóa ngoại.
Derek Downey

1

Việc xóa các hàng không mong muốn trong lô sẽ giúp hoạt động khác khả thi. Nhưng việc xóa hoạt động của bạn có điều kiện, vì vậy hãy đảm bảo rằng có một chỉ mục thích hợp trên các cột theo điều kiện.

Bởi vì MySQL không hỗ trợ chức năng hoàn toàn lỏng lẻo chỉ số quét, bạn có thể thử để điều chỉnh thứ tự cho KEY actionDate (action, date_insert)đến KEY actionDate (date_insert, action). Với tiền tố là 'date_insert', MySQL nên sử dụng chỉ mục này để quét các hàng trước điều kiện thời gian của bạn.

Với chỉ mục như vậy, bạn có thể viết SQL dưới dạng:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch

1
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-Fist, từ giải thích của bạn key_len rất lớn => bạn cần hạ cấp kích thước càng nhỏ càng tốt. Đối với truy vấn của bạn, tôi nghĩ cách tốt nhất là thay đổi loại dữ liệu của trường hành động từ char (12) sang tinyint, vì vậy ánh xạ dữ liệu trông như sau:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

và bạn cũng có thể thay đổi table_id thay vì tablename. DDL cho hiệu suất tốt nhất có thể:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

vì vậy truy vấn có thể chạy như sau:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

Nhưng cách nhanh nhất là sử dụng phân vùng. để bạn có thể thả phân vùng. Hiện tại, bảng của tôi đã có khoảng hơn 40 triệu hàng. và cập nhật hàng giờ (400k hàng cập nhật cho mỗi lần) và tôi có thể thả phân vùng current_date và tải lại dữ liệu vào bảng. lệnh thả rất nhanh (<100ms). Hy vọng điều này giúp đỡ.

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.