ได้รับ“ หมดเวลารอการล็อค ลองเริ่มต้นธุรกรรมใหม่ "แม้ว่าฉันจะไม่ได้ใช้ธุรกรรมก็ตาม


267

ฉันใช้UPDATEคำสั่งMySQL ต่อไปนี้:

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

ฉันไม่ได้ใช้ธุรกรรมดังนั้นทำไมฉันถึงได้รับข้อผิดพลาดนี้? ฉันลองรีสตาร์ทเซิร์ฟเวอร์ MySQL แล้วก็ไม่ได้ผล

ตารางมี 406,733 แถว

คำตอบ:


211

คุณกำลังใช้ธุรกรรม autocommit ไม่ได้ปิดการใช้งานการทำธุรกรรมมันเพียงทำให้พวกเขากระทำโดยอัตโนมัติในตอนท้ายของคำสั่ง

สิ่งที่เกิดขึ้นคือเธรดอื่น ๆ กำลังถือล็อกเร็กคอร์ดในบางเรคคอร์ด (คุณกำลังอัปเดตทุกเรคคอร์ดในตาราง!) นานเกินไปและเธรดของคุณหมดเวลาแล้ว

คุณสามารถดูรายละเอียดเพิ่มเติมของกิจกรรมได้โดยการออก

SHOW ENGINE INNODB STATUS

หลังจากเหตุการณ์ (ในตัวsqlแก้ไข) ทำสิ่งนี้ให้ดีบนเครื่องทดสอบที่เงียบ


1
มีวิธีบันทึกผลลัพธ์ไปยังไฟล์หรือไม่? ฉันลอง SHOW ENGINE INNODB สถานภาพ \ G> innodb_stat.txt แต่ไม่ทำงาน
yantaq

14
จากบรรทัดคำสั่ง: mysql [ใส่ข้อมูลประจำตัว] -e "SHOW ENGINE INNODB สถานภาพ \ G"> innodb_stat.txt
VenerableAgents

หากมีเธรด mysql จำนวนมาก (หรือกระบวนการ) ไม่ว่างเช่นบางข้อความค้นหาต้องใช้เวลานานดังนั้นคุณต้องรอprocessให้ข้อความว่าง ถ้าเป็นเช่นนั้นคุณอาจได้รับข้อผิดพลาดนี้ ฉันถูกไหม?
zhuguowei

6
การเรียกใช้การปรับปรุงหลายรายการ (2+) แบบสอบถามในแถวเดียวกันระหว่างการทำธุรกรรมเดียวจะทำให้เกิดข้อผิดพลาดนี้
Okneloper

สำหรับผู้ที่ใช้ Python MySQL Connector ใช้connection.commit()เพื่อคอมมิทINSERTหรือUPDATEคุณเพิ่งยิง
AER

334

วิธีการบังคับปลดล็อคสำหรับตารางที่ถูกล็อคใน MySQL:

การทำลายล็อคเช่นนี้อาจทำให้ atomicityในฐานข้อมูลไม่ถูกบังคับใช้กับคำสั่ง sql ที่ทำให้เกิดการล็อค

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

1) ป้อน MySQL

mysql -u your_user -p

2) ลองดูรายการตารางที่ถูกล็อค

mysql> show open tables where in_use>0;

3) เรามาดูรายการของกระบวนการปัจจุบันหนึ่งในนั้นคือการล็อคตารางของคุณ

mysql> show processlist;

4) ฆ่าหนึ่งในกระบวนการเหล่านี้

mysql> kill <put_process_id_here>;

14
นี่เป็นสิ่งที่อันตรายและแฮ็ค ทางออกที่เหมาะสมคือแก้ไขใบสมัครของคุณ
Zenexer

71
ไร้สาระสิ่งนี้ช่วยให้คุณสามารถยกเลิกการ messup แล้วแก้ไขแอปพลิเคชัน ถ้าฉันจะให้คะแนน 100 คะแนนสำหรับปัญหานี้ซึ่งฉันต้องแก้ไขตอนนี้ฉันจะทำ
Lizardx

7
ฉันเห็นด้วยกับ Lizardx นี่เป็นวิธีแก้ปัญหาที่มีประโยชน์มากในสถานการณ์ที่ฉันไม่มีสิทธิ์โทร SHOW ENGINE INNODB สถานะ
Travis Schneeberger

8
การฆ่าเคียวรีที่ใช้เวลานานเป็นวิธีที่อันตรายหรือไม่? การเรียกไคลเอ็นต์จะได้รับข้อผิดพลาด
Xeoncross

7
@EricLeschinski ฉันได้รับประเด็นของคุณ แต่ฉันต้องถามว่าทำไมในโลกที่คุณจะใช้ฐานข้อมูลเลอะเทอะอย่างMySQLบนระบบLife-Critical ?
Xeoncross

96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

ทริกเกอร์การล็อคอีกครั้ง คุณมีเวลา 100 วินาทีในการออกSHOW ENGINE INNODB STATUS\Gฐานข้อมูลและดูว่าธุรกรรมใดที่ล็อคคุณอยู่


8
คำตอบนี้ไม่ได้อธิบายว่าทำไมผู้ถามถึงได้รับข้อผิดพลาด คุณช่วยอธิบายเพิ่มเติมได้ไหมว่าทำไมนอกจากให้คำตอบ?
เลื่อน

5
+1 แม้ว่าสิ่งนี้จะไม่ตอบคำถามโดยตรง แต่สำหรับฉันมันเป็นการอ้างอิงที่ดีในการแก้ปัญหานี้
Jossef Harush

1
@ArtB dev.mysql.com/doc/innodb/1.1/en/…ในสาระสำคัญ OP ได้รับข้อผิดพลาดเนื่องจากมีการเรียกล็อกบนโต๊ะและเวลาที่ผ่านไปก่อนที่จะสิ้นสุดธุรกรรมเกินlock_wait_timeoutค่า
fyrye

70

ดูว่าฐานข้อมูลของคุณได้รับการปรับแต่งหรือไม่ โดยเฉพาะการแยกธุรกรรม ไม่ใช่ความคิดที่ดีที่จะเพิ่มตัวแปร innodb_lock_wait_timeout

ตรวจสอบระดับการแยกธุรกรรมฐานข้อมูลของคุณใน mysql cli:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

คุณสามารถได้รับการปรับปรุงการเปลี่ยนระดับการแยกใช้ oracle เช่น READ COMMITTED แทน REPEATABLE READ (ค่าปริยายของ InnoDB)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

ลองใช้ SELECT FOR UPDATE เฉพาะในกรณีที่จำเป็นเท่านั้น


1
นี่เป็นทางออกที่ดีสำหรับปัญหาการล็อค
redolent

3
ใช้งานได้ดีสำหรับฉันรุ่น my.cnf คือ [mysqld] ทรานแซกชันการแยก = คอมมิชชันที่อ่านแล้ว
เบอนัวต์กัวร์

เพียงบันทึก: MySQL 8 ได้เปลี่ยนชื่อตัวแปรtx_isolation transaction_isolation
xonya

ต้องใส่ใจให้ได้ว่าคุณกำลังทำอะไรเมื่อคุณเปลี่ยนจากการอ่านอย่างเดียวเป็น REIT COMMITTED คุณอาจจบลงด้วยข้อมูลสกปรกที่คุณอาจต้องการหลีกเลี่ยง Wikipedia Isoloation
huggie

27

วิธีแก้ปัญหาที่แนะนำไม่เหมาะกับฉัน

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

SHOW PROCESSLIST;

เมื่อคุณพบกระบวนการบล็อกให้ค้นหาidและเรียกใช้:

KILL {id};

เรียกใช้แบบสอบถามเริ่มต้นของคุณอีกครั้ง


ฉันตั้งใจฆ่ากระบวนการทั้งหมดที่แสดงรายการด้วย SHOW PROCESSLIST; ตอนนี้ฉันได้รับข้อผิดพลาด 500 ครั้งใน phpmyadmin นั่นคือข้อผิดพลาด 500 ข้อที่เกี่ยวข้องกับการฆ่ากระบวนการเหล่านี้หรือไม่ ถ้าใช่ฉันจะเริ่มต้นใหม่ได้อย่างไร
Dashrath

ฉันเห็นสิ่งที่คุณอธิบาย แต่การฆ่ากระบวนการไม่ทำงาน คำสั่งของกระบวนการ 'ถูกฆ่า' แต่มันยังคงอยู่ในรายการกระบวนการ
Torsten

12

100% กับสิ่งที่ MarkR พูด การทำสำเนาอัตโนมัติทำให้แต่ละคำสั่งเป็นการทำรายการคำสั่งเดียว

SHOW ENGINE INNODB STATUSควรให้เบาะแสแก่คุณเกี่ยวกับเหตุผลการหยุดชะงัก ลองดูบันทึกการสืบค้นที่ช้าของคุณด้วยเช่นกันเพื่อดูว่ามีอะไรอื่นในการสืบค้นตารางและลองลบสิ่งที่กำลังทำอยู่เต็มตาราง การล็อกระดับแถวใช้งานได้ดี แต่ไม่ใช่เมื่อคุณพยายามล็อคแถวทั้งหมด!


5

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


3
บางทีinnodb_lock_wait_timeoutในmy.cnf
oblig

1
ฉันตั้งค่าใน my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> ค่าเริ่มต้นคือ 50 สำหรับ mysql 5.5 หลังจากการเปลี่ยนแปลงนี้ฉันไม่สามารถเห็นปัญหานี้ในการทดสอบหน่วยของฉัน! สิ่งนี้เกิดขึ้นหลังจากเปลี่ยนจาก proxool เป็น tomcat jdbc pool อาจเป็นเพราะเวลาในการทำธุรกรรมกับ Tomcat Pool เพิ่มขึ้น!
แชมป์

3

จำนวนแถวไม่มาก ... สร้างดัชนีใน account_import_id หากไม่ใช่คีย์หลัก

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);

OMG ... นี่เพิ่งช่วยฉัน ฉันขันฐานการผลิตโดยการวางดัชนีและทำให้มันคงที่ ขอบคุณ.
Nick Gotch

2

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

รายละเอียด:

ฉันได้ออกแบบสอบถามลบเพื่อลบประมาณ 900,000 จากประมาณ 1 ล้านแถว

ฉันทำสิ่งนี้โดยไม่ได้ตั้งใจ (ลบเพียง 10% ของแถว): DELETE FROM table WHERE MOD(id,10) = 0

แทนสิ่งนี้ (ลบ 90% ของแถว): DELETE FROM table WHERE MOD(id,10) != 0

ฉันต้องการลบ 90% ของแถวไม่ใช่ 10% ดังนั้นฉันจึงฆ่ากระบวนการในบรรทัดคำสั่ง MySQL โดยรู้ว่ามันจะย้อนกลับทุกแถวที่ลบไปแล้ว

จากนั้นฉันก็รันคำสั่งที่ถูกต้องทันทีและได้รับlock timeout exceededข้อผิดพลาดหลังจากนั้น ฉันตระหนักว่าการล็อกอาจเป็นrollbackของคิวรี่ที่ถูกฆ่าซึ่งยังคงเกิดขึ้นในพื้นหลัง ดังนั้นฉันรอไม่กี่วินาทีและเรียกใช้แบบสอบถามอีกครั้ง


1

ตรวจสอบให้แน่ใจว่าตารางฐานข้อมูลกำลังใช้เอ็นจิ้นการจัดเก็บข้อมูลของ InnoDB และระดับการแยกธุรกรรม

คุณสามารถตรวจสอบได้โดยเลือก @@ GLOBAL.tx_isolation, @@ tx_isolation; บนคอนโซล mysql

หากไม่ได้ตั้งค่าเป็น READ-COMMITTED คุณจะต้องตั้งค่า ตรวจสอบให้แน่ใจก่อนที่จะตั้งค่าว่าคุณมีสิทธิ์พิเศษใน mysql

คุณสามารถรับความช่วยเหลือได้จากhttp://dev.mysql.com/doc/refman/5.0/en/set-transaction.html http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html

ด้วยการตั้งค่านี้ฉันคิดว่าปัญหาของคุณจะได้รับการแก้ไข


คุณอาจต้องการตรวจสอบว่าคุณไม่ได้พยายามอัปเดตสิ่งนี้ในสองกระบวนการพร้อมกัน ผู้ใช้ (@tala) พบข้อความแสดงข้อผิดพลาดที่คล้ายกันในบริบทนี้อาจตรวจสอบอีกครั้งว่า ...


1

ฉันมาจาก Google และฉันแค่ต้องการเพิ่มโซลูชันที่เหมาะกับฉัน ปัญหาของฉันคือฉันพยายามลบระเบียนของตารางขนาดใหญ่ที่มี FK จำนวนมากในน้ำตกดังนั้นฉันจึงได้รับข้อผิดพลาดเดียวกับ OP

ฉันปิดการใช้งานautocommitแล้วมันก็ทำงานได้เพิ่มCOMMITในตอนท้ายของประโยค SQL เท่าที่ฉันเข้าใจสิ่งนี้ปล่อยบัฟเฟอร์ทีละนิดแทนที่จะรอที่ส่วนท้ายของคำสั่ง

เพื่อรักษาตัวอย่างของ OP สิ่งนี้น่าจะใช้ได้ดี:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

อย่าลืมเปิดใช้งานautocommitอีกครั้งหากคุณต้องการออกจากการตั้งค่า MySQL เหมือนเดิม

mysql> set autocommit=1;


0

มาสายไปงานปาร์ตี้ (ตามปกติ) แต่ปัญหาของฉันคือข้อเท็จจริงที่ว่าฉันเขียน SQL ที่ไม่ดี (เป็นสามเณร) และกระบวนการต่าง ๆ มีการล็อกเร็กคอร์ด <- ไม่แน่ใจว่า verbiage ที่เหมาะสม ฉันต้องลงเอยด้วยการSHOW PROCESSLISTฆ่าแล้วใช้รหัสKILL <id>


0

สิ่งนี้เกิดขึ้นกับฉันเมื่อฉันใช้ภาษาสร้าง php; ในระหว่างการทำธุรกรรม จากนั้นธุรกรรมนี้ "ค้าง" และคุณต้องฆ่ากระบวนการ mysql (อธิบายไว้ข้างต้นด้วยรายการกระบวนการ;)


0

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

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

นี่อาจไม่ใช่ความคิดที่ดีสำหรับการใช้งานปกติ

ดูข้อมูลเพิ่มเติมได้ที่: คู่มืออ้างอิง MySQL 8.0


0

ฉันวิ่งเข้าไปในนี้มีการเชื่อมต่อ 2 หลักคำสอน DBAL ซึ่งเป็นหนึ่งในนั้นไม่ใช่การทำธุรกรรม (สำหรับบันทึกที่สำคัญ) พวกเขามีจุดประสงค์ที่จะทำงานแบบขนานไม่ได้ขึ้นอยู่กับแต่ละอื่น ๆ

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

การทดสอบการรวมเข้าด้วยกันถูกห่อเป็นธุรกรรมสำหรับการย้อนกลับข้อมูลหลังจากการทดสอบอย่างมาก

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

โซลูชันของฉันคือปิดการทำธุรกรรมการตัดคำในการทดสอบเหล่านั้นและรีเซ็ตข้อมูล db ด้วยวิธีอื่น


-4

มีข้อผิดพลาดเดียวกันนี้แม้ว่าฉันจะอัปเดตเพียงหนึ่งตารางที่มีหนึ่งรายการ แต่หลังจากรีสตาร์ท mysql แล้วก็จะได้รับการแก้ไข

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