Vượt quá thời gian chờ đợi của Khóa Lock; hãy thử khởi động lại giao dịch ngay cả khi tôi không sử dụng giao dịch


267

Tôi đang chạy UPDATEcâu lệnh MySQL sau :

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

Tôi không sử dụng giao dịch, vậy tại sao tôi lại gặp phải lỗi này? Tôi thậm chí đã thử khởi động lại máy chủ MySQL của mình và nó không giúp được gì.

Bảng có 406.733 hàng.

Câu trả lời:


211

Bạn đang sử dụng một giao dịch; autocommit không vô hiệu hóa các giao dịch, nó chỉ làm cho chúng tự động cam kết vào cuối tuyên bố.

Điều gì đang xảy ra là, một số luồng khác đang giữ khóa bản ghi trên một số bản ghi (bạn đang cập nhật mọi bản ghi trong bảng!) Quá lâu và chủ đề của bạn đang bị hết thời gian.

Bạn có thể xem thêm chi tiết của sự kiện bằng cách phát hành

SHOW ENGINE INNODB STATUS

sau sự kiện (trong sqlbiên tập). Lý tưởng nhất là làm điều này trên một máy thử nghiệm yên tĩnh.


1
Có cách nào để lưu đầu ra vào một tập tin không? Tôi đã thử SHOW Engine INNODB STATUS \ G> innodb_stat.txt nhưng không hoạt động.
yantaq

14
Từ dòng lệnh: mysql [chèn thông tin đăng nhập] -e "SHOW Engine INNODB STATUS \ G"> innodb_stat.txt
VenerableAgents

nếu nhiều luồng mysql (hoặc quá trình) đang bận, ví dụ: một số truy vấn cần thời gian rất dài, vì vậy bạn phải chờ một số processđể không hoạt động. Nếu vậy bạn có thể nhận được lỗi này. Tôi có đúng không
zhuguowei

6
Chạy nhiều (2+) CẬP NHẬT truy vấn trên cùng một hàng trong một giao dịch cũng sẽ gây ra lỗi này.
Okneloper

Đối với những người sử dụng Python MySQL Connector sử dụng connection.commit()để cam kết INSERThoặc UPDATEbạn vừa bắn qua.
AER

334

CÁCH KIẾM UNLOCK cho các bảng bị khóa trong MySQL:

Việc phá khóa như thế này có thể khiến tính nguyên tử trong cơ sở dữ liệu không được thực thi trên các câu lệnh sql gây ra khóa.

Đây là hackish, và giải pháp thích hợp là sửa lỗi ứng dụng của bạn gây ra các khóa. Tuy nhiên, khi đô la trên đường, một cú đá nhanh sẽ khiến mọi thứ di chuyển trở lại.

1) Nhập MySQL

mysql -u your_user -p

2) Hãy xem danh sách các bảng bị khóa

mysql> show open tables where in_use>0;

3) Hãy xem danh sách các quy trình hiện tại, một trong số chúng đang khóa (các) bảng của bạn

mysql> show processlist;

4) Giết một trong những quá trình này

mysql> kill <put_process_id_here>;

14
Điều này là nguy hiểm và hackish. Một giải pháp thích hợp là sửa ứng dụng của bạn.
Zenexer

71
Vô nghĩa, điều này cho phép bạn hoàn tác một mớ hỗn độn và sau đó sửa ứng dụng. Nếu tôi có thể cho anh chàng này 100 phiếu bầu cho vấn đề này mà tôi phải sửa NGAY thì tôi sẽ làm.
Lizardx

7
Tôi đồng ý với Lizardx. Đây là một giải pháp rất hữu ích trong trường hợp tôi không có đặc quyền gọi SHOW Engine INNODB STATUS
Travis Schneeberger

8
Làm thế nào là giết một truy vấn dài hạn theo cách này nguy hiểm ? Các khách hàng gọi sẽ chỉ nhận được một lỗi.
Xeoncross

7
@EricLeschinski Tôi hiểu ý của bạn, nhưng tôi phải hỏi tại sao trên thế giới bạn sẽ sử dụng một cơ sở dữ liệu cẩu thả như MySQL trên một hệ thống quan trọng trong cuộc sống ?
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   |
+--------------------------+-------+

Bây giờ kích hoạt khóa một lần nữa. Bạn có 100 giây thời gian để phát hành SHOW ENGINE INNODB STATUS\Gcơ sở dữ liệu và xem giao dịch nào khác đang khóa giao dịch của bạn.


8
Câu trả lời này không giải thích tại sao người hỏi lại nhận được lỗi của họ. Bạn có thể giải thích tại sao ngoài việc chỉ đưa ra câu trả lời?
Sled

5
+1 mặc dù điều này không trả lời trực tiếp câu hỏi, nhưng đối với tôi, đây là một tài liệu tham khảo tốt để khắc phục vấn đề này.
Jossef Harush

1
@ArtB dev.mysql.com/doc/innodb/1.1/en/NH Về bản chất, OP đang nhận được lỗi vì một khóa được gọi trên bàn và thời gian trôi qua trước khi kết thúc giao dịch vượt quá lock_wait_timeoutgiá trị
fyrye

70

Hãy xem nếu cơ sở dữ liệu của bạn được điều chỉnh tốt. Đặc biệt là cách ly giao dịch. Không nên tăng biến innodb_lock_wait_timeout.

Kiểm tra mức cô lập giao dịch cơ sở dữ liệu của bạn trong mysi 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)

Bạn có thể nhận được các cải tiến thay đổi mức độ cô lập, sử dụng lời tiên tri như READ CAMITED thay vì REPEATABLE READ (Mặc định của 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> 

Ngoài ra, hãy thử sử dụng CHỌN CẬP NHẬT chỉ trong trường hợp cần thiết.


1
Đây là một giải pháp tuyệt vời cho các vấn đề khóa.
hương thơm

3
Hoạt động rất tốt đối với tôi, phiên bản my.cnf là [mysqld] giao dịch cách ly = ĐỌC-CAM KẾT
Benoit Gauthier

Chỉ cần lưu ý: MySQL 8 đã đổi tên thành tx_isolationbiến transaction_isolation.
xonya

Phải quan tâm để biết những gì bạn đang gặp phải khi bạn thay đổi từ cách ly đọc sang đọc CAM KẾT. Bạn có thể kết thúc với dữ liệu bẩn mà bạn có thể muốn tránh. Wikipedia Isoloation
huggie

27

Không có giải pháp đề xuất nào làm việc cho tôi nhưng điều này đã làm.

Một cái gì đó đang chặn việc thực hiện truy vấn. Rất có thể là một truy vấn khác đang cập nhật, chèn hoặc xóa từ một trong các bảng trong truy vấn của bạn. Bạn phải tìm ra đó là gì:

SHOW PROCESSLIST;

Khi bạn xác định vị trí quá trình chặn, hãy tìm quá trình chặn id và chạy:

KILL {id};

Chạy lại truy vấn ban đầu của bạn.


Tôi vô tình giết chết tất cả các quy trình được liệt kê với SHOW PROCESSLIST; Bây giờ tôi đang nhận được 500 lỗi trong phpmyadmin. Là 500 lỗi liên quan đến việc giết chết các quá trình này? Nếu có, làm thế nào tôi có thể khởi động lại nó.
Dashrath

Tôi có thể thấy những gì bạn mô tả, tuy nhiên việc giết quá trình không hoạt động. Lệnh 'process "bị" giết ", nhưng nó vẫn nằm trong danh sách process.
Torsten

12

100% với những gì MarkR nói. autocommit làm cho mỗi câu lệnh trở thành một giao dịch câu lệnh.

SHOW ENGINE INNODB STATUSsẽ cung cấp cho bạn một số manh mối về lý do bế tắc. Có một cái nhìn tốt về nhật ký truy vấn chậm của bạn để xem những gì khác đang truy vấn bảng và cố gắng loại bỏ bất cứ điều gì đang làm một bảng quét đầy đủ. Khóa cấp hàng hoạt động tốt nhưng không phải khi bạn đang cố khóa tất cả các hàng!


5

Bạn có thể cập nhật bất kỳ bản ghi nào khác trong bảng này không, hay bảng này được sử dụng nhiều? Những gì tôi đang nghĩ là trong khi nó đang cố gắng để có được một khóa mà nó cần để cập nhật hồ sơ này, thời gian chờ đã được thiết lập đã hết thời gian. Bạn có thể tăng thời gian có thể giúp đỡ.


3
có thể innodb_lock_wait_timeouttrongmy.cnf
bắt buộc

1
Tôi đã đặt trong my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> Mặc định là 50 cho mysql 5.5. Sau thay đổi này, tôi không thể thấy vấn đề này trong các bài kiểm tra đơn vị của mình! Điều này xảy ra sau khi chuyển từ proxool sang pool jdbc tomcat. Có lẽ là do thời gian giao dịch nhiều hơn với hồ bơi tomcat?!
Champ

3

Số lượng hàng không lớn ... Tạo một chỉ mục trên account_import_id nếu đó không phải là khóa chính.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);

OMG ... điều này chỉ cứu tôi. Tôi thực sự làm hỏng một DB sản xuất bằng cách bỏ một chỉ mục và điều này đã sửa nó. Cảm ơn bạn.
Nick Gotch

2

Nếu bạn vừa giết một truy vấn lớn, sẽ mất thời gian để rollback . Nếu bạn đưa ra một truy vấn khác trước khi truy vấn bị giết hoàn tất, bạn có thể gặp lỗi hết thời gian khóa. Đó là những gì đã xảy ra với tôi. Giải pháp chỉ là chờ một chút.

Chi tiết:

Tôi đã đưa ra một truy vấn XÓA để xóa khoảng 900.000 trong số khoảng 1 triệu hàng.

Tôi đã chạy lỗi này (chỉ xóa 10% số hàng): DELETE FROM table WHERE MOD(id,10) = 0

Thay vì điều này (loại bỏ 90% các hàng): DELETE FROM table WHERE MOD(id,10) != 0

Tôi muốn xóa 90% các hàng, không phải 10%. Vì vậy, tôi đã giết quá trình trong dòng lệnh MySQL, biết rằng nó sẽ khôi phục lại tất cả các hàng mà nó đã xóa cho đến nay.

Sau đó, tôi chạy lệnh chính xác ngay lập tức và gặp lock timeout exceededlỗi ngay sau đó. Tôi nhận ra rằng khóa thực sự có thể là rollbacktruy vấn bị giết vẫn đang diễn ra trong nền. Vì vậy, tôi đã đợi một vài giây và chạy lại truy vấn.


1

Đảm bảo các bảng cơ sở dữ liệu đang sử dụng công cụ lưu trữ InnoDB và mức cô lập giao dịch READ-CAM KẾT.

Bạn có thể kiểm tra nó bằng cách CHỌN @@ GLOBAL.tx_isolation, @@ tx_isolation; trên bảng điều khiển mysql.

Nếu nó không được đặt thành READ-CAM KẾT thì bạn phải đặt nó. Hãy chắc chắn rằng trước khi thiết lập rằng bạn có các đặc quyền SUPER trong mysql.

Bạn có thể nhận trợ giúp từ http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html .

Bằng cách thiết lập điều này, tôi nghĩ rằng vấn đề của bạn sẽ được giải quyết.


Bạn cũng có thể muốn kiểm tra xem bạn không cố cập nhật điều này trong hai quy trình cùng một lúc. Người dùng (@tala) đã gặp phải thông báo lỗi tương tự trong ngữ cảnh này, có thể kiểm tra kỹ xem ...


1

Tôi đến từ Google và tôi chỉ muốn thêm giải pháp hiệu quả cho mình. Vấn đề của tôi là tôi đã cố gắng xóa các bản ghi của một bảng lớn có nhiều FK theo tầng để tôi gặp lỗi tương tự như OP.

Tôi đã vô hiệu hóa autocommitvà sau đó nó hoạt động chỉ cần thêmCOMMIT vào cuối câu SQL. Theo như tôi hiểu thì điều này giải phóng bộ đệm từng chút một thay vì chờ ở cuối lệnh.

Để theo ví dụ về OP, điều này nên hoạt động:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Đừng quên kích hoạt autocommitlại nếu bạn muốn rời khỏi cấu hình MySQL như trước.

mysql> set autocommit=1;


0

Tuy nhiên, đến bữa tiệc muộn (như thường lệ), vấn đề của tôi là thực tế là tôi đã viết một số SQL xấu (là người mới) và một số quy trình đã khóa (các) bản ghi <- không chắc chắn có thông báo phù hợp. Cuối cùng tôi đã phải: SHOW PROCESSLISTvà sau đó giết ID bằng cách sử dụngKILL <id>


0

Điều này đã xảy ra với tôi khi tôi đang sử dụng ngôn ngữ php xây dựng lối thoát; ở giữa giao dịch. Sau đó, giao dịch này bị "treo" và bạn cần phải giết tiến trình mysql (được mô tả ở trên với danh sách quy trình;)


0

Trong trường hợp của tôi, tôi đã chạy một truy vấn bất thường để sửa dữ liệu. Nếu bạn khóa các bảng trong truy vấn của mình, thì bạn sẽ không phải đối phó với thời gian chờ Khóa:

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

Đây có lẽ không phải là một ý tưởng tốt cho sử dụng bình thường.

Để biết thêm thông tin, xem: Hướng dẫn tham khảo MySQL 8.0


0

Tôi bắt gặp điều này khi có 2 kết nối DBAL Doctrine, một trong những kết nối không giao dịch (đối với các nhật ký quan trọng), chúng được dự định chạy song song không phụ thuộc vào nhau.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Các thử nghiệm tích hợp của tôi đã được gói gọn trong các giao dịch để phục hồi dữ liệu sau khi thử nghiệm.

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

Giải pháp của tôi là vô hiệu hóa giao dịch gói trong các thử nghiệm đó và đặt lại dữ liệu db theo cách khác.


-4

Có lỗi tương tự, mặc dù tôi chỉ cập nhật một bảng với một mục, nhưng sau khi khởi động lại mysql, nó đã được giải quyết.

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.