Lời nói đầu
Ứng dụng của chúng tôi chạy một số luồng thực thi DELETE
các truy vấn song song. Các truy vấn ảnh hưởng đến dữ liệu bị cô lập, tức là không có khả năng DELETE
xảy ra đồng thời trên cùng một hàng từ các luồng riêng biệt. Tuy nhiên, trên mỗi tài liệu, MySQL sử dụng khóa được gọi là khóa 'khóa tiếp theo' cho các DELETE
câu lệnh, khóa cả khóa khớp và một số khoảng cách. Điều này dẫn đến khóa chết và giải pháp duy nhất mà chúng tôi tìm thấy là sử dụng READ COMMITTED
mức cô lập.
Vấn đề
Vấn đề phát sinh khi thực hiện các DELETE
câu lệnh phức tạp với JOIN
s của các bảng lớn. Trong một trường hợp cụ thể, chúng tôi có một bảng với các cảnh báo chỉ có hai hàng, nhưng truy vấn cần bỏ tất cả các cảnh báo thuộc về một số thực thể cụ thể từ hai INNER JOIN
bảng ed riêng biệt . Truy vấn như sau:
DELETE pw
FROM proc_warnings pw
INNER JOIN day_position dp
ON dp.transaction_id = pw.transaction_id
INNER JOIN ivehicle_days vd
ON vd.id = dp.ivehicle_day_id
WHERE vd.ivehicle_id=? AND dp.dirty_data=1
Khi bảng day_poseition đủ lớn (trong trường hợp thử nghiệm của tôi có 1448 hàng) thì mọi giao dịch ngay cả với READ COMMITTED
chế độ cách ly sẽ chặn toàn bộ proc_warnings
bảng.
Vấn đề luôn được sao chép trên dữ liệu mẫu này - http://yadi.sk/d/QDuwBtpW1BxB9 cả trong MySQL 5.1 (đã kiểm tra ngày 5.1.59) và MySQL 5.5 (đã kiểm tra trên MySQL 5.5.24).
EDIT: Dữ liệu mẫu được liên kết cũng chứa lược đồ và chỉ mục cho các bảng truy vấn, được sao chép ở đây để thuận tiện:
CREATE TABLE `proc_warnings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned NOT NULL,
`warning` varchar(2048) NOT NULL,
PRIMARY KEY (`id`),
KEY `proc_warnings__transaction` (`transaction_id`)
);
CREATE TABLE `day_position` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_day_id` int(10) unsigned DEFAULT NULL,
`dirty_data` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `day_position__trans` (`transaction_id`),
KEY `day_position__is` (`ivehicle_day_id`,`sort_index`),
KEY `day_position__id` (`ivehicle_day_id`,`dirty_data`)
) ;
CREATE TABLE `ivehicle_days` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d` date DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ivehicle_days__is` (`ivehicle_id`,`sort_index`),
KEY `ivehicle_days__d` (`d`)
);
Truy vấn trên mỗi giao dịch như sau:
Giao dịch 1
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=2 AND dp.dirty_data=1;
Giao dịch 2
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1;
Một trong số chúng luôn thất bại với lỗi 'Khóa thời gian chờ vượt quá ...'. Các hàng information_schema.innodb_trx
chứa sau:
| trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_wait | trx_mysql_thread_id | trx_query |
| '1A2973A4' | 'LOCK WAIT' | '2012-12-12 20:03:25' | '1A2973A4:0:3172298:2' | '2012-12-12 20:03:25' | '2' | '3089' | 'DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1' |
| '1A296F67' | 'RUNNING' | '2012-12-12 19:58:02' | NULL | NULL | '7' | '3087' | NULL |
information_schema.innodb_locks
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
| '1A2973A4:0:3172298:2' | '1A2973A4' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
| '1A296F67:0:3172298:2' | '1A296F67' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
Như tôi có thể thấy cả hai truy vấn đều muốn X
khóa độc quyền trên một hàng với khóa chính = 53. Tuy nhiên, không ai trong số chúng phải xóa các hàng khỏi proc_warnings
bảng. Tôi chỉ không hiểu tại sao chỉ số bị khóa. Ngoài ra, chỉ mục không bị khóa khi proc_warnings
bảng trống hoặc day_position
bảng chứa số lượng hàng ít hơn (tức là một trăm hàng).
Điều tra sâu hơn là chạy EXPLAIN
qua SELECT
truy vấn tương tự . Nó cho thấy rằng trình tối ưu hóa truy vấn không sử dụng chỉ mục cho proc_warnings
bảng truy vấn và đó là lý do duy nhất tôi có thể tưởng tượng tại sao nó chặn toàn bộ chỉ mục khóa chính.
Trường hợp đơn giản
Vấn đề cũng có thể được sao chép trong trường hợp đơn giản hơn khi chỉ có hai bảng có vài bản ghi, nhưng bảng con không có chỉ mục trên cột ref của bảng cha.
Tạo parent
bảng
CREATE TABLE `parent` (
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
Tạo child
bảng
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
Điền vào bảng
INSERT INTO `parent` (id) VALUES (1), (2);
INSERT INTO `child` (id, parent_id) VALUES (1, NULL), (2, NULL);
Thử nghiệm trong hai giao dịch song song:
Giao dịch 1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 1;
Giao dịch 2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 2;
Phần chung trong cả hai trường hợp là MySQL không sử dụng các chỉ mục. Tôi tin rằng đó là lý do khóa của toàn bộ bảng.
Giải pháp của chúng tôi
Giải pháp duy nhất mà chúng ta có thể thấy bây giờ là tăng thời gian chờ khóa mặc định từ 50 giây lên 500 giây để cho phép xử lý xong luồng. Sau đó giữ ngón tay chéo.
Bất kỳ trợ giúp đánh giá cao.
day_position
bảng thường chứa bao nhiêu hàng , khi nào nó bắt đầu chạy chậm đến mức bạn phải giảm giới hạn thời gian chờ đến 500 giây? 2) Mất bao lâu để chạy khi bạn chỉ có dữ liệu mẫu?