วิธีการแก้จุดบกพร่องล็อคเกินเวลารอล็อคบน MySQL?


269

ในบันทึกข้อผิดพลาดการผลิตของฉันฉันเห็นเป็นครั้งคราว:

SQLSTATE [HY000]: ข้อผิดพลาดทั่วไป: เกินกำหนดเวลารอการล็อค 1205; ลองรีสตาร์ทธุรกรรม

ฉันรู้ว่าแบบสอบถามใดพยายามเข้าถึงฐานข้อมูลในขณะนั้น แต่มีวิธีค้นหาว่าแบบสอบถามใดมีการล็อกในช่วงเวลาที่แม่นยำหรือไม่


1
ฉันขอแนะนำให้ทุกคนให้คำตอบด้วยการยิงของ
Eirik

คำตอบ:


261

สิ่งที่ให้ไปนี้เป็นคำที่การทำธุรกรรม จะเห็นได้ชัดโดยคำสั่งที่แบบสอบถามพยายามเปลี่ยนอย่างน้อยหนึ่งแถวในตาราง InnoDB หนึ่งตารางขึ้นไป

เมื่อคุณทราบแบบสอบถามตารางทั้งหมดที่ถูกเข้าถึงจะเป็นตัวเลือกในการเป็นผู้ร้าย

จากตรงนั้นคุณควรจะสามารถวิ่งได้ SHOW ENGINE INNODB STATUS\G

คุณควรจะเห็นตารางที่ได้รับผลกระทบ

คุณได้รับข้อมูลการล็อคและ Mutex เพิ่มเติมทุกชนิด

นี่คือตัวอย่างจากลูกค้าของฉัน:

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
110514 19:44:14 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 9014315, signal count 7805377
Mutex spin waits 0, rounds 11487096053, OS waits 7756855
RW-shared spins 722142, OS waits 211221; RW-excl spins 787046, OS waits 39353
------------------------
LATEST FOREIGN KEY ERROR
------------------------
110507 21:41:35 Transaction:
TRANSACTION 0 606162814, ACTIVE 0 sec, process no 29956, OS thread id 1223895360 updating or deleting, thread declared inside InnoDB 499
mysql tables in use 1, locked 1
14 lock struct(s), heap size 3024, 8 row lock(s), undo log entries 1
MySQL thread id 3686635, query id 124164167 10.64.89.145 viget updating
DELETE FROM file WHERE file_id in ('6dbafa39-7f00-0001-51f2-412a450be5cc' )
Foreign key constraint fails for table `backoffice`.`attachment`:
,
  CONSTRAINT `attachment_ibfk_2` FOREIGN KEY (`file_id`) REFERENCES `file` (`file_id`)
Trying to delete or update in parent table, in index `PRIMARY` tuple:
DATA TUPLE: 17 fields;
 0: len 36; hex 36646261666133392d376630302d303030312d353166322d343132613435306265356363; asc 6dbafa39-7f00-0001-51f2-412a450be5cc;; 1: len 6; hex 000024214f7e; asc   $!O~;; 2: len 7; hex 000000400217bc; asc    @   ;; 3: len 2; hex 03e9; asc   ;; 4: len 2; hex 03e8; asc   ;; 5: len 36; hex 65666635323863622d376630302d303030312d336632662d353239626433653361333032; asc eff528cb-7f00-0001-3f2f-529bd3e3a302;; 6: len 40; hex 36646234376337652d376630302d303030312d353166322d3431326132346664656366352e6d7033; asc 6db47c7e-7f00-0001-51f2-412a24fdecf5.mp3;; 7: len 21; hex 416e67656c73204e6f7720436f6e666572656e6365; asc Angels Now Conference;; 8: len 34; hex 416e67656c73204e6f7720436f6e666572656e6365204a756c7920392c2032303131; asc Angels Now Conference July 9, 2011;; 9: len 1; hex 80; asc  ;; 10: len 8; hex 8000124a5262bdf4; asc    JRb  ;; 11: len 8; hex 8000124a57669dc3; asc    JWf  ;; 12: SQL NULL; 13: len 5; hex 8000012200; asc    " ;; 14: len 1; hex 80; asc  ;; 15: len 2; hex 83e8; asc   ;; 16: len 4; hex 8000000a; asc     ;;

But in child table `backoffice`.`attachment`, in index `PRIMARY`, there is a record:
PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 30; hex 36646261666133392d376630302d303030312d353166322d343132613435; asc 6dbafa39-7f00-0001-51f2-412a45;...(truncated); 1: len 30; hex 38666164663561652d376630302d303030312d326436612d636164326361; asc 8fadf5ae-7f00-0001-2d6a-cad2ca;...(truncated); 2: len 6; hex 00002297b3ff; asc   "   ;; 3: len 7; hex 80000040070110; asc    @   ;; 4: len 2; hex 0000; asc   ;; 5: len 30; hex 416e67656c73204e6f7720436f6e666572656e636520446f63756d656e74; asc Angels Now Conference Document;;

------------
TRANSACTIONS
------------
Trx id counter 0 620783814
Purge done for trx's n:o < 0 620783800 undo n:o < 0 0
History list length 35
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1192212800
MySQL thread id 5341758, query id 189708501 127.0.0.1 lwdba
show innodb status
---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1223895360
MySQL thread id 5341667, query id 189706152 10.64.89.145 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1227888960
MySQL thread id 5341556, query id 189699857 172.16.135.63 lwdba
---TRANSACTION 0 620781112, not started, process no 29956, OS thread id 1222297920
MySQL thread id 5341511, query id 189696265 10.64.89.143 viget
---TRANSACTION 0 620783736, not started, process no 29956, OS thread id 1229752640
MySQL thread id 5339005, query id 189707998 10.64.89.144 viget
---TRANSACTION 0 620783785, not started, process no 29956, OS thread id 1198602560
MySQL thread id 5337583, query id 189708349 10.64.89.145 viget
---TRANSACTION 0 620783469, not started, process no 29956, OS thread id 1224161600
MySQL thread id 5333500, query id 189708478 10.64.89.144 viget
---TRANSACTION 0 620781240, not started, process no 29956, OS thread id 1198336320
MySQL thread id 5324256, query id 189708493 10.64.89.145 viget
---TRANSACTION 0 617458223, not started, process no 29956, OS thread id 1195141440
MySQL thread id 736, query id 175038790 Has read all relay log; waiting for the slave I/O thread to update it
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
519878 OS file reads, 18962880 OS file writes, 13349046 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 6.25 writes/s, 4.50 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1190, seg size 1192,
174800 inserts, 174800 merged recs, 54439 merges
Hash table size 35401603, node heap has 35160 buffer(s)
0.50 hash searches/s, 11.75 non-hash searches/s
---
LOG
---
Log sequence number 28 1235093534
Log flushed up to   28 1235093534
Last checkpoint at  28 1235091275
0 pending log writes, 0 pending chkp writes
12262564 log i/o's done, 3.25 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 18909316674; in additional pool allocated 1048576
Dictionary memory allocated 2019632
Buffer pool size   1048576
Free buffers       175763
Database pages     837653
Modified db pages  6
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 770138, created 108485, written 7795318
0.00 reads/s, 0.00 creates/s, 4.25 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 29956, id 1185823040, state: sleeping
Number of rows inserted 6453767, updated 4602534, deleted 3638793, read 388349505551
0.25 inserts/s, 1.25 updates/s, 0.00 deletes/s, 2.75 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set, 1 warning (0.00 sec)

คุณควรพิจารณาเพิ่มค่าการหมดเวลาการรอการล็อคสำหรับ InnoDB โดยการตั้งค่าinnodb_lock_wait_timeoutค่าเริ่มต้นคือ 50 วินาที

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set (0.01 sec)

คุณสามารถกำหนดเป็นค่าที่สูงขึ้นได้/etc/my.cnfอย่างถาวรด้วยบรรทัดนี้

[mysqld]
innodb_lock_wait_timeout=120

และเริ่ม mysql ใหม่ หากคุณไม่สามารถรีสตาร์ท mysql ได้ในเวลานี้ให้รันสิ่งนี้:

SET GLOBAL innodb_lock_wait_timeout = 120; 

นอกจากนี้คุณยังสามารถตั้งค่าสำหรับช่วงเวลาของเซสชั่นของคุณ

SET innodb_lock_wait_timeout = 120; 

ตามด้วยคำค้นหาของคุณ


5
สำหรับ InnoDB ในinnodb_lock_wait_timeoutตัวตัวแปรสามารถตั้งค่าได้เฉพาะเมื่อเริ่มต้นเซิร์ฟเวอร์ สำหรับปลั๊กอิน InnoDB สามารถตั้งค่าเมื่อเริ่มต้นหรือเปลี่ยนแปลงในขณะทำงานและมีทั้งค่าส่วนกลางและค่าเซสชัน
Timo Huovinen

1
สวัสดี @rolandomysqldba คุณช่วยกรุณาแนะนำฉันในโพสต์นี้ของฉันได้ที่: stackoverflow.com/questions/18267565/…
Manish Sapkal

2
ฉันได้รับข้อผิดพลาดนี้เมื่อพยายามเรียกใช้SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
คิวรี

1
@Pacerier ทุกครั้งที่ mysqld ถูกรีสตาร์ทคุณต้องทำงานSET GLOBAL innodb_lock_wait_timeout = 120;อีกครั้ง หาก/etc/my.cnfมีตัวเลือกinnodb_lock_wait_timeoutถูกตั้งค่าสำหรับคุณ ไม่ใช่ทุกคนที่มีสิทธิ์พิเศษในการเปลี่ยนแปลงทั่วโลกสำหรับคนอื่น ๆ ( dev.mysql.com/doc/refman/5.6/en/ … )
RolandoMySQLDBA

3
@IulianOnofrei อักขระ \ G เป็นคุณสมบัติพิเศษของบรรทัดคำสั่ง MySQL และเปลี่ยนวิธีแสดงผลลัพธ์ สำหรับลูกค้า MySQL อื่น ๆ ให้ใช้เครื่องหมายอัฒภาคปกติแทน
thenickdude

83

ตามที่มีคนพูดถึงหนึ่งในหลาย ๆ หัวข้อดังนั้นเกี่ยวกับปัญหานี้: บางครั้งกระบวนการที่ล็อคตารางจะปรากฏขึ้นขณะหลับในรายการกระบวนการ! ฉันฉีกผมออกจนกว่าจะฆ่ากระทู้ที่กำลังเปิดอยู่ในฐานข้อมูลที่เป็นปัญหา (ไม่มีการใช้งานในเวลานั้น) ในที่สุดก็ปลดล็อคตารางและปล่อยให้แบบสอบถามแบบใช้เรียกใช้

ผู้วิจารณ์กล่าวว่าคล้ายกับ "บางครั้งเธรดของ MySQL จะล็อกตารางจากนั้นหลับขณะที่รอสิ่งที่ไม่เกี่ยวข้องกับ MySQL ที่จะเกิดขึ้น"

หลังจากตรวจสอบshow engine innodb statusบันทึกอีกครั้ง (เมื่อฉันติดตามลูกค้าที่รับผิดชอบการล็อค) ฉันสังเกตเห็นว่ากระทู้ที่ค้างอยู่ในคำถามนั้นอยู่ในรายการด้านล่างสุดของรายการธุรกรรมใต้แบบสอบถามที่ใช้งานซึ่งกำลังจะเกิดข้อผิดพลาด ออกเพราะล็อคแช่แข็ง:

------------------
---TRANSACTION 2744943820, ACTIVE 1154 sec(!!)
2 lock struct(s), heap size 376, 2 row lock(s), undo log entries 1
MySQL thread id 276558, OS thread handle 0x7f93762e7710, query id 59264109 [ip] [database] cleaning up
Trx read view will not see trx with id >= 2744943821, sees < 2744943821

(ไม่แน่ใจว่าข้อความ "มุมมองการอ่าน Trx" เกี่ยวข้องกับการล็อกน้ำแข็ง แต่ไม่เหมือนกับธุรกรรมอื่น ๆ ที่ใช้งานอยู่อันนี้ไม่ปรากฏขึ้นพร้อมกับแบบสอบถามที่ออกและแทนที่จะอ้างว่าการทำธุรกรรม "ล้าง" แต่มีหลาย ล็อคแถว)

คุณธรรมของเรื่องราวคือธุรกรรมสามารถใช้งานได้แม้ว่าเธรดกำลังหลับอยู่


2
ฉันไม่สามารถพูดได้ว่าคุณช่วยชีวิตฉันไว้ แต่คุณตั้งใจอย่างสงบ อ่านคำตอบของคุณฉันพบเธรดที่น่าขนลุกที่ทำงานเป็นเวลา 3260 วินาทีและไม่ปรากฏขึ้นทุกที่ หลังจากที่ฆ่ามันปัญหาทั้งหมดของฉันได้รับการแก้ไข!
kommradHomer

นี่เป็นปัญหาของฉัน ธุรกรรมการนอนหลับที่มีเวลา 20,000 วินาทีที่ป้องกันไม่ให้งานล่าช้าในแอป Rails ทำงานได้อย่างถูกต้อง ขอบคุณ @Eirik
bigtex777

ความคิดใดที่ว่าทำไมธุรกรรมการนอนหลับไม่ได้ถูกกำจัด เช่นมีการหมดเวลาที่คุณสามารถกำหนดให้การทำธุรกรรมต้องเสร็จสิ้นภายในหรือไม่
patrickdavey

1
คำสั่งอื่น ๆ ที่อาจจะเป็นประโยชน์ในการทำธุรกรรมสำหรับล็อคการค้นหาของคุณ: จะแสดงรายการครบถ้วนสมบูรณ์ของกระบวนการในขณะนี้กำลังดำเนินการซึ่งเป็นสิ่งที่ดีเพราะมันเป็นฉบับย่อของshow processlist; show engine innodb status\gนอกจากนี้หากฐานข้อมูลของคุณอยู่ในอินสแตนซ์ Amazon RDS คุณสามารถใช้CALL mysql.rds_kill(<thread_id>);เพื่อฆ่าเธรด มีสิทธิ์สูงกว่าที่ฉันคิดเพราะอนุญาตให้ฉันฆ่ากระบวนการมากกว่าธรรมดาkill <thread_id>;- โปรดทราบว่าสิ่งเหล่านี้ควรเรียกใช้ภายใน MySQL CLI
Nick

1
ทุกคนมีแหล่งที่มาสำหรับสิ่งนี้ - อาจเป็นหน้าเอกสารที่ระบุล็อคถูกวางไว้ก่อนระยะ COMMIT หรือไม่ ฉันไม่พบอะไรเลยแม้จะพบปัญหาที่แน่นอนนี้และมันก็ถูกลบล้างโดยการฆ่าด้ายนอนที่กำลังล็อคอยู่
Erin Schoonover

42

เนื่องจากความนิยมของ MySQL จึงไม่น่าแปลกใจที่การรอคอยการล็อคเกินเวลา ลองรีสตาร์ทข้อยกเว้นการทำธุรกรรมได้รับความสนใจอย่างมากใน SO

ยิ่งคุณมีความขัดแย้งมากเท่าไหร่โอกาสในการหยุดชะงักมากขึ้นซึ่งเอ็นจิน DB จะแก้ไขได้ด้วยการหมดเวลาของธุรกรรมที่หยุดชะงัก นอกจากนี้ธุรกรรมที่ใช้เวลานานซึ่งมีการแก้ไข (เช่นUPDATEหรือDELETE) รายการจำนวนมาก (ซึ่งใช้การล็อกเพื่อหลีกเลี่ยงความผิดปกติในการเขียนสกปรกตามที่อธิบายไว้ในหนังสือJava Persistence ประสิทธิภาพสูง ) มีแนวโน้มที่จะสร้างความขัดแย้งกับธุรกรรมอื่น ๆ

แม้ว่า InnoDB MVCC คุณยังสามารถขอล็อคอย่างชัดเจนโดยใช้FOR UPDATEประโยค แต่แตกต่างจากดีบีเอสที่นิยมอื่น ๆ (Oracle, MSSQL, PostgreSQL, DB2), MySQL ใช้REPEATABLE_READเป็นระดับแยกเป็นค่าเริ่มต้น

ตอนนี้ล็อคที่คุณได้รับ (ไม่ว่าจะโดยการปรับเปลี่ยนแถวหรือใช้การล็อคอย่างชัดเจน) จะถูกระงับในช่วงระยะเวลาของการทำธุรกรรมที่กำลังทำงานอยู่ หากคุณต้องการคำอธิบายที่ดีของความแตกต่างระหว่างREPEATABLE_READและREAD COMMITTEDในเรื่องที่เกี่ยวกับการล็อคโปรดอ่านบทความ Percona นี้

ใน REPEATABLE READ ทุกล็อคที่ได้รับระหว่างการทำธุรกรรมจะถูกเก็บไว้ตลอดระยะเวลาของการทำธุรกรรม

ใน READ COMMITTED การล็อกที่ไม่ตรงกับการสแกนจะถูกนำออกใช้หลังจากที่ STATEMENT เสร็จสิ้น

...

ซึ่งหมายความว่าในธุรกรรม READ COMMITTED มีอิสระที่จะอัปเดตแถวที่พวกเขาจะไม่สามารถอัปเดต (ใน REPEATABLE READ) เมื่อคำสั่ง UPDATE เสร็จสมบูรณ์

ดังนั้น: ระดับการแยกที่ จำกัด มากขึ้น ( REPEATABLE_READ, SERIALIZABLE) ยิ่งมีโอกาสในการหยุดชะงักมากขึ้น นี่ไม่ใช่ปัญหา "ต่อ se" มันเป็นการแลกเปลี่ยน

คุณสามารถได้รับผลลัพธ์ที่ดีมากREAD_COMMITEDเนื่องจากคุณต้องการการป้องกันการอัพเดทระดับแอปพลิเคชันที่หายไปเมื่อใช้ธุรกรรมแบบลอจิคัลที่ครอบคลุมมากกว่าคำขอ HTTP หลายรายการ มองโลกในแง่ล็อคเป้าหมายวิธีการสูญเสียการปรับปรุงที่อาจเกิดขึ้นได้ถ้าคุณใช้SERIALIZABLEระดับการแยกREAD_COMMITEDในขณะที่ลดการต่อสู้ล็อคโดยช่วยให้คุณสามารถใช้งาน


4
การล็อกรอหมดเวลาไม่แตกต่างจากการหยุดชะงักใช่ไหม เช่นถ้าหนึ่งเธรดมีการล็อคเป็นเวลา 60 วินาทีสำหรับเหตุผลที่ถูกต้องการรอการล็อคอาจเกิดขึ้นได้ มันไม่เป็นความจริงที่ว่าถ้ามีการหยุดชะงักของ MySQL จริง ๆ จะตรวจจับและฆ่าการทำธุรกรรมทันทีและสิ่งนี้ไม่เกี่ยวข้องกับการหมดเวลาล็อครอ
ColinM

1
คุณพูดถูก ฐานข้อมูลจะตรวจพบการล็อคตายหลังจากหมดเวลาและฆ่าหนึ่งกระบวนการรอดังนั้นธุรกรรมหนึ่งชนะในขณะที่อีกกระบวนการหนึ่งล้มเหลว แต่ยิ่งคุณถือล็อคนานเท่าไร แม้ว่าคุณจะไม่ได้พบเจอกับการล็อคที่ไม่ได้ผลคุณยังคงเพิ่มส่วนต่อเนื่องของพฤติกรรมรันไทม์ของแอปพลิเคชันของคุณ
Vlad Mihalcea

19

สำหรับบันทึกนั้นข้อยกเว้นการหมดเวลาการล็อคจะเกิดขึ้นเช่นกันเมื่อมีการหยุดชะงักและ MySQL ไม่สามารถตรวจพบได้ดังนั้นจึงหมดเวลา อีกเหตุผลหนึ่งอาจเป็นข้อความค้นหาที่ใช้เวลานานมากซึ่งง่ายต่อการแก้ไข / ซ่อมแซมอย่างไรก็ตามฉันจะไม่อธิบายกรณีนี้ที่นี่

โดยปกติแล้ว MySQL สามารถจัดการกับการหยุดชะงักหากพวกเขาสร้าง "ถูกต้อง" ภายในสองธุรกรรม จากนั้น MySQL ก็เพียงฆ่า / ย้อนกลับธุรกรรมหนึ่งที่มีการล็อคน้อยลง (มีความสำคัญน้อยกว่าเพราะจะส่งผลต่อแถวน้อยลง) และปล่อยให้อีกอันหนึ่งทำเสร็จ

ตอนนี้สมมติว่ามีสองกระบวนการ A และ B และ 3 ธุรกรรม:

Process A Transaction 1: Locks X
Process B Transaction 2: Locks Y
Process A Transaction 3: Needs Y => Waits for Y
Process B Transaction 2: Needs X => Waits for X
Process A Transaction 1: Waits for Transaction 3 to finish

(see the last two paragraph below to specify the terms in more detail)

=> deadlock 

นี่เป็นการตั้งค่าที่โชคร้ายมากเนื่องจาก MySQL ไม่สามารถเห็นว่ามีการหยุดชะงัก (ขยายภายใน 3 ธุรกรรม) ดังนั้นสิ่งที่ MySQL ทำคือ ... ไม่มีอะไร! รอเพียงเพราะไม่รู้ว่าจะทำอย่างไร มันจะรอจนกว่าการล็อคครั้งแรกที่ได้รับจะเกินเวลาหมดเวลา (ประมวลผลธุรกรรม 1: ล็อค X) จากนั้นสิ่งนี้จะปลดล็อค Lock X ซึ่งปลดล็อกธุรกรรม 2 เป็นต้น

ศิลปะคือการค้นหาสิ่งที่ (แบบสอบถามใด) ทำให้เกิดการล็อกแรก (Lock X) คุณจะสามารถดูได้อย่างง่ายดาย ( show engine innodb status) ว่าธุรกรรม 3 รอธุรกรรม 2 แต่คุณจะไม่เห็นว่าธุรกรรม 2 กำลังรอธุรกรรม (ธุรกรรม 1) MySQL จะไม่พิมพ์การล็อกหรือการสืบค้นใด ๆ ที่เกี่ยวข้องกับการทำธุรกรรม 1. คำใบ้เพียงอย่างเดียวคือที่ด้านล่างสุดของรายการธุรกรรม (ของshow engine innodb statusผลงานพิมพ์) คุณจะเห็นการทำธุรกรรม 1 เห็นได้ชัดว่าไม่ได้ทำอะไรเลย เสร็จสิ้น).

เทคนิคสำหรับวิธีค้นหาแบบสอบถาม SQL ที่ทำให้การล็อก (Lock X) ได้รับอนุญาตสำหรับธุรกรรมที่กำหนดซึ่งกำลังรออยู่อธิบายไว้ที่นี่ Tracking MySQL query history in long running transactions

หากคุณสงสัยว่ากระบวนการและธุรกรรมเป็นอย่างไรในตัวอย่าง กระบวนการนี้เป็นกระบวนการ PHP รายการเป็นการทำธุรกรรมตามที่กำหนดโดยInnoDB-TRX ตาราง ในกรณีของฉันฉันมีสองกระบวนการ PHP ในแต่ละฉันเริ่มทำธุรกรรมด้วยตนเอง ส่วนที่น่าสนใจคือแม้ว่าฉันจะเริ่มทำธุรกรรมหนึ่งครั้ง แต่ MySQL ใช้การทำธุรกรรมสองอย่างแยกต่างหากในความเป็นจริง

MySQL จัดการธุรกรรมของตัวเองภายในและตัดสินใจ (ในกรณีของฉัน) เพื่อใช้สองธุรกรรมเพื่อจัดการการร้องขอ SQL ทั้งหมดที่มาจากกระบวนการ PHP (กระบวนการ A) ข้อความที่ว่า Transaction 1 กำลังรอให้ Transaction 3 เสร็จสิ้นเป็นสิ่ง MySQL ภายใน MySQL "รู้" ว่าธุรกรรม 1 และธุรกรรม 3 ได้รับการยกตัวอย่างจริงโดยเป็นส่วนหนึ่งของคำขอ "ธุรกรรม" หนึ่งรายการ (จากกระบวนการ A) ขณะนี้ "ธุรกรรม" ถูกปิดกั้นเนื่องจากธุรกรรม 3 (ส่วนย่อยของ "ธุรกรรม") ถูกปิดกั้น เนื่องจาก "ทรานแซคชัน" ไม่สามารถทำธุรกรรม 1 ให้เสร็จสิ้น (เช่นกันส่วนย่อยของ "ทรานแซคชัน") จึงถูกทำเครื่องหมายว่าไม่เสร็จเช่นกัน นี่คือสิ่งที่ฉันหมายถึงโดย "ธุรกรรม 1 รอให้ธุรกรรม 3 จบ"


14

ปัญหาใหญ่ที่เกิดจากข้อยกเว้นนี้คือมันมักจะไม่สามารถทำซ้ำได้ในสภาพแวดล้อมการทดสอบและเราไม่ได้อยู่รอบ ๆ เพื่อเรียกใช้สถานะเครื่องยนต์ของ Innodb เมื่อมันเกิดขึ้นกับผลิตภัณฑ์ ดังนั้นในโครงการใดโครงการหนึ่งฉันใส่โค้ดด้านล่างลงใน catch block สำหรับข้อยกเว้นนี้ ที่ช่วยให้ฉันรับสถานะเครื่องยนต์เมื่อเกิดข้อยกเว้น นั่นช่วยได้มาก

Statement st = con.createStatement();
ResultSet rs =  st.executeQuery("SHOW ENGINE INNODB STATUS");
while(rs.next()){
    log.info(rs.getString(1));
    log.info(rs.getString(2));
    log.info(rs.getString(3));
}

11

ดูที่หน้า man ของpt-deadlock-loggerโปรแกรมอรรถประโยชน์ :

brew install percona-toolkit
pt-deadlock-logger --ask-pass server_name

มันดึงข้อมูลจากที่engine innodb statusกล่าวมาข้างต้นและยังสามารถใช้ในการสร้างdaemonที่ทำงานทุก 30 วินาที


3
เครื่องมือนี้เป็นส่วนหนึ่งของชุดเครื่องมือ Percona
Brad Mace

หมดเวลาการรอของล็อคไม่เหมือนกับการหยุดชะงักโดยเฉพาะ innodb ไม่แสดงข้อมูลใด ๆ เกี่ยวกับพวกเขาเพราะพวกเขาไม่ได้ตรวจพบการหยุดชะงักดังนั้นฉันจึงไม่คิดว่า pt-deadlock-logger เป็นความช่วยเหลือใด ๆ
Jay Paroline


11

การคาดการณ์จากคำตอบของ Rolando ด้านบนเป็นสิ่งที่บล็อกการสืบค้นของคุณ:

---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget

หากคุณต้องการดำเนินการค้นหาของคุณและไม่สามารถรอให้คนอื่นทำงานให้ฆ่าพวกเขาออกโดยใช้รหัสด้าย MySQL:

kill 5341773 <replace with your thread id>

(จากภายใน mysql ไม่ใช่เชลล์แน่นอน)

คุณต้องค้นหา ID ของเธรดจาก:

show engine innodb status\G

คำสั่งและหาว่าอันไหนคืออันที่กำลังบล็อคฐานข้อมูล


1
คุณรู้ได้5341773อย่างไร ฉันไม่เห็นสิ่งที่แตกต่างจากที่อื่น
Wodin

ไม่อาจเป็นไปได้ว่าไม่ใช่ threadID นั่นเป็นตัวอย่าง คุณต้องค้นหา ID ของเธรดจากคำสั่ง "show engine innodb status \ G" และหาว่าอันไหนเป็นอันที่บล็อกฐานข้อมูล
Ellert van Koperen

1
ขอบคุณ ดังนั้นจึงไม่มีวิธีที่จะบอกได้ว่าอันใดที่ปราศจากเช่นฆ่าพวกมันทีละคน?
Wodin

ในรายการธุรกรรมที่คุณสามารถดูได้ว่ารายการใดกำลังทำงานอยู่และนานเท่าใด ดังนั้นไม่จำเป็นต้องฆ่าพวกเขาทีละคนรายการนั้นมักจะทำให้คุณมีความคิดที่ดีว่าเกิดอะไรขึ้น
Ellert van Koperen

10

ต่อไปนี้เป็นสิ่งที่ฉันต้องทำเพื่อค้นหาว่า "ข้อความค้นหาอื่น ๆ " ทำให้เกิดปัญหาการหมดเวลาล็อค ในรหัสแอปพลิเคชันเราจะติดตามการเรียกฐานข้อมูลที่รอดำเนินการทั้งหมดในเธรดแยกต่างหากสำหรับงานนี้โดยเฉพาะ หากการเรียก DB ใช้เวลานานกว่า N-วินาที (สำหรับเราคือ 30 วินาที) เราจะบันทึก:

-- Pending InnoDB transactions
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started; 

-- Optionally, log what transaction holds what locks
SELECT * FROM information_schema.innodb_locks;

จากด้านบนเราสามารถระบุข้อความค้นหาที่เกิดขึ้นพร้อมกันที่ล็อคแถวทำให้เกิดการหยุดชะงัก ในกรณีของฉันพวกเขาเป็นงบเช่นINSERT ... SELECTที่ไม่เหมือนกับ SELECT แบบธรรมดาที่ล็อคแถวที่อยู่ข้างใต้ จากนั้นคุณสามารถจัดระเบียบรหัสใหม่หรือใช้การแยกธุรกรรมที่แตกต่างกันเช่นอ่านปราศจากข้อผูกมัด

โชคดี!


9

คุณสามารถใช้ได้:

show full processlist

ซึ่งจะแสดงรายการการเชื่อมต่อทั้งหมดใน MySQL และสถานะปัจจุบันของการเชื่อมต่อรวมถึงแบบสอบถามที่กำลังดำเนินการ นอกจากนี้ยังมีชุดตัวเลือกที่สั้นกว่าshow processlist;ซึ่งแสดงคิวรีที่ถูกตัดทอนรวมถึงสถิติการเชื่อมต่อ



-2

เปิดใช้งาน MySQL general.log (ใช้ดิสก์มาก) และใช้mysql_analyse_general_log.plเพื่อแยกธุรกรรมที่ใช้เวลานานเช่น:

--min-duration = ค่า innodb_lock_wait_timeout ของคุณ

ปิดใช้งาน general.log หลังจากนั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.