คำนำ
แอปพลิเคชันของเราทำงานหลายเธรดที่ดำเนินการDELETE
ค้นหาแบบขนาน แบบสอบถามมีผลต่อข้อมูลที่แยกได้เช่นไม่ควรมีความเป็นไปได้ที่DELETE
เกิดขึ้นพร้อมกันในแถวเดียวกันจากกระทู้แยก อย่างไรก็ตามตามเอกสาร MySQL ใช้ล็อคที่เรียกว่า 'คีย์ต่อไป' สำหรับDELETE
งบซึ่งล็อคทั้งคีย์การจับคู่และช่องว่างบางส่วน สิ่งนี้นำไปสู่การล็อกที่ตายแล้วและทางออกเดียวที่เราพบคือใช้READ COMMITTED
ระดับการแยก
ปัญหา
ปัญหาเกิดขึ้นเมื่อรันDELETE
คำสั่งที่ซับซ้อนด้วยJOIN
s ของตารางขนาดใหญ่ ในบางกรณีเรามีตารางที่มีคำเตือนที่มีเพียงสองแถว แต่แบบสอบถามจำเป็นต้องวางคำเตือนทั้งหมดที่อยู่ในเอนทิตี้บางแห่งจากINNER JOIN
ตาราง ed แยกสองตาราง แบบสอบถามมีดังนี้:
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
เมื่อตาราง day_position มีขนาดใหญ่พอ (ในกรณีทดสอบของฉันมีแถว 1448) ธุรกรรมใด ๆ แม้ด้วยREAD COMMITTED
โหมดแยกบล็อกตารางทั้งหมด proc_warnings
ปัญหานี้จะมีการทำซ้ำในข้อมูลตัวอย่างนี้ - http://yadi.sk/d/QDuwBtpW1BxB9ทั้งใน MySQL 5.1 (ตรวจสอบใน 5.1.59) และ MySQL 5.5 (ตรวจสอบใน MySQL 5.5.24)
แก้ไข: ข้อมูลตัวอย่างที่เชื่อมโยงยังมีสคีมาและดัชนีสำหรับตารางคิวรีทำซ้ำที่นี่เพื่อความสะดวก:
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`)
);
แบบสอบถามต่อการทำธุรกรรมมีดังนี้:
ธุรกรรม 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;
ธุรกรรม 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;
หนึ่งในนั้นล้มเหลวเสมอกับข้อผิดพลาด 'เกินเวลารอการล็อคเกิน ... ' information_schema.innodb_trx
มีจำนวนแถวต่อไปนี้:
| 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' |
ดังที่ฉันเห็นทั้งสองข้อความค้นหาต้องการการX
ล็อกแบบเอกสิทธิ์เฉพาะบุคคลในแถวที่มีคีย์หลัก = 53 อย่างไรก็ตามทั้งสองแบบสอบถามไม่ต้องลบแถวออกจากproc_warnings
ตาราง ฉันไม่เข้าใจว่าทำไมดัชนีจึงถูกล็อค ยิ่งไปกว่านั้นดัชนีจะไม่ถูกล็อกเมื่อproc_warnings
ตารางว่างเปล่าหรือday_position
ตารางมีจำนวนแถวน้อยลง (เช่นหนึ่งร้อยแถว)
การตรวจสอบเพิ่มเติมคือการเรียกใช้แบบสอบถามที่EXPLAIN
คล้ายกัน SELECT
มันแสดงให้เห็นว่าเครื่องมือเพิ่มประสิทธิภาพการสืบค้นไม่ได้ใช้ดัชนีเพื่อสืบค้นproc_warnings
ตารางและนั่นเป็นเหตุผลเดียวที่ฉันสามารถจินตนาการได้ว่าเหตุใดจึงปิดกั้นดัชนีคีย์หลักทั้งหมด
กรณีที่ง่าย
ปัญหายังสามารถทำซ้ำได้ในกรณีที่ง่ายกว่าเมื่อมีเพียงสองตารางที่มีระเบียนไม่กี่ตาราง แต่ตารางลูกไม่ได้มีดัชนีในคอลัมน์อ้างอิงตารางหลัก
สร้างparent
ตาราง
CREATE TABLE `parent` (
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
สร้างchild
ตาราง
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
เติมตาราง
INSERT INTO `parent` (id) VALUES (1), (2);
INSERT INTO `child` (id, parent_id) VALUES (1, NULL), (2, NULL);
ทดสอบในการทำธุรกรรมสองขนาน:
ธุรกรรม 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;
ธุรกรรม 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;
ส่วนที่พบบ่อยในทั้งสองกรณีคือ MySQL ไม่ได้ใช้ดัชนี ฉันเชื่อว่าเป็นสาเหตุของการล็อคของตารางทั้งหมด
ทางออกของเรา
ทางออกเดียวที่เราเห็นได้ในตอนนี้คือเพิ่มการหมดเวลารอการล็อกเริ่มต้นจาก 50 วินาทีเป็น 500 วินาทีเพื่อให้เธรดหมดสิ้น จากนั้นให้นิ้วไขว้กัน
ความช่วยเหลือใด ๆ ที่ชื่นชม
day_position
ตารางมีจำนวนแถวเท่าไหร่เมื่อเริ่มทำงานช้าจนคุณต้องชน จำกัด การหมดเวลา 500 วินาที 2) ใช้เวลานานแค่ไหนเมื่อคุณมีเพียงข้อมูลตัวอย่าง?