2011-07-14 10 views
9

Máy chủ MySQL phiên bản 5.1.41 với plugin InnoDB được bật. Tôi có ba bảng sau đây cho hóa đơn: hóa đơn, hóa đơn và thành phần hóa đơn. Hóa đơn bảng có khóa chính là invoice_id. Cả invoice_components và invoice_expenses được liên kết với hóa đơn bảng với invoice_id là một ngoại lệ không phải là duy nhất (mỗi hóa đơn có thể có nhiều thành phần và nhiều hơn một chi phí). Cả hai bảng đều có chỉ số BTREE cho khóa ngoại này.InnoDB SELECT ... FOR UPDATE statement khóa tất cả các hàng trong một bảng

tôi có các giao dịch sau:

giao dịch 1

START TRANSACTION; 
SELECT * FROM invoices WHERE invoice_id = 18 FOR UPDATE; 
SELECT * FROM invoice_components WHERE invoice = 18 FOR UPDATE; 
SELECT * FROM invoice_expenses WHERE invoice = 18 FOR UPDATE; 

Mọi thứ hoạt động ok cho giao dịch đầu tiên và các hàng được lựa chọn và khóa.

giao dịch 2

START TRANSACTION; 
SELECT * FROM invoices WHERE invoice_id = 19 FOR UPDATE; 
SELECT * FROM invoice_components WHERE invoice = 19 FOR UPDATE; 
SELECT * FROM invoice_expenses WHERE invoice = 19 FOR UPDATE; 

Giao dịch thứ hai trả ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction cho truy vấn thứ ba.

Điều tương tự cũng xảy ra khi tôi cố gắng CHỌN ... CẬP NHẬT hóa đơn khác cũng như các thành phần và chi phí của chúng. Dường như giao dịch đầu tiên đã khóa tất cả các hàng trong bảng invoice_expenses. Có ý kiến ​​giải thích tại sao điều này lại xảy ra không?

thông tin bổ sung

Giao dịch 2 bắt đầu sau khi truy vấn thứ ba thực hiện giao dịch 1. Không có người dùng khác, kết nối, giao dịch trên máy chủ.

Sự cố xảy ra ở mức cô lập REPEATABLE READ mặc định. Nó được cố định bằng cách thay đổi sang mức READ COMMITTED. Đây là một giải pháp nhưng nó vẫn không giải thích tại sao vấn đề xảy ra với invoice_expenses và không phải với invoice_components.

+4

bạn có chắc chắn tất cả hồ sơ đã bị khóa không? InnoDB có thể khóa hồ sơ khu phố (khi bạn khóa 18-17 và 19), nhưng không nên khóa hồ sơ mà là xa awat trong btree - vì vậy hãy thử điều này với khoảng cách lớn hơn trong các id? Ngoài ra, mức cô lập của bạn là gì? –

+0

@Darhazer. Cảm ơn. Thay đổi mức cô lập thành READ COMMITTED đã giải quyết được vấn đề. Bạn có thể giải thích những gì đang xảy ra không? Khoảng cách MySQL có khóa bảng invoice_expenses của tôi không? Tại sao điều tương tự lại không xảy ra với invoice_components? Dù bằng cách nào, nếu bạn đăng một câu trả lời tôi sẽ chấp nhận nó. –

+0

Tôi không có câu trả lời tại sao điều này xảy ra trong READEATABLE READ, vì vậy tôi muốn đặt một tiền thưởng vào câu hỏi, hơn là đăng câu trả lời chưa hoàn chỉnh. Đó có phải là những truy vấn duy nhất chạy trong các giao dịch không? Giao dịch thứ hai có bắt đầu sau khi thực hiện truy vấn thứ ba trong giao dịch đầu tiên hay không, hoặc chúng là đồng thời? Tôi sẽ thêm tiền thưởng sau 2 ngày được yêu cầu bởi SO –

Trả lời

-2

Bạn đang sử dụng 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 ở cuối các câu lệnh không có rõ ràng start transaction trên chúng.

Điều gì đang xảy ra, một số chủ đề 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à chuỗi của bạn đang hết thời gian chờ.

Bạn có thể xem thêm chi tiết về sự kiện bằng cách phát hành "SHOW ENGINE INNODB STATUS" sau sự kiện. 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

Xin lỗi, Rush, bạn đã không đọc chi tiết câu hỏi của tôi hoặc không hiểu nó rất rõ. Tôi đang giữ giao dịch đầu tiên mở nhằm mục đích kiểm tra sự tương tranh. Vấn đề là nó đang khóa các hàng mà nó không được chọn trong bảng invoice_expenses. Đây là trên một máy chủ phát triển vì vậy không có người dùng khác, kết nối hoặc giao dịch. Đã sử dụng SHOW ENGINE INNODB STATUS. –

9

tôi nghi ngờ nó đã làm với khóa khoảng cáchkhóa tiếp theo-key và sự khác biệt trong hành vi của REPEATABLE READ:

Các trích đoạn từ tài liệu MySQL: SET TRANSACTION syntax

Đối với các lần đọc khóa (SELECT với lệnh FOR UPDATE hoặc LOCK IN SHARE MODE), các câu lệnh UPDATE và DELETE, khóa phụ thuộc vào việc câu lệnh sử dụng một chỉ mục duy nhất có điều kiện tìm kiếm duy nhất hoặc điều kiện tìm kiếm loại phạm vi. Đối với một chỉ mục duy nhất có điều kiện tìm kiếm duy nhất, InnoDB chỉ khóa bản ghi chỉ mục được tìm thấy, không phải khoảng trống trước nó.Đối với các điều kiện tìm kiếm khác, InnoDB khóa phạm vi chỉ mục được quét, sử dụng các khóa khoảng cách khoảng cách hoặc khóa tiếp theo (khoảng cách cộng với ghi chỉ mục) để chặn các lần chèn khác vào các khoảng trống trong phạm vi phủ sóng.

ĐỌC CAM KẾT:

Lưu ý: Trong MySQL 5.1, nếu ĐỌC CAM KẾT mức cô lập được sử dụng hoặc biến hệ thống innodb_locks_unsafe_for_binlog được kích hoạt, không có khoảng cách InnoDB khóa trừ kiểm tra ràng buộc khóa ngoài và kiểm tra khóa trùng lặp. Ngoài ra, khóa bản ghi cho các hàng không khớp được phát hành sau khi MySQL đã đánh giá điều kiện WHERE.

Có lẽ OP có thể cho chúng tôi biết trạng thái của biến số innodb_locks_unsafe_for_binlog system và nếu cùng một khóa xảy ra khi cài đặt của biến này bị thay đổi.

Ngoài ra, nếu cùng khóa sẽ xảy ra với id không tuần tự, như 1820, hoặc 1899

+0

Innodb_locks_unsafe_for_binlog của tôi bị TẮT. Tôi có kết quả tương tự khi khóa hàng cho id 18 và cố gắng chọn cho các hàng cập nhật cho id 21. Không có id 20. –

+0

@ Miloš: Và nếu bạn đặt 'innodb_locks_unsafe_for_binlog' thành ON? Liệu nó vẫn xảy ra? –

+0

Chúng tôi không thể nói được nếu điều này gây ra vấn đề, nhưng câu trả lời của bạn là hữu ích cho tôi, và là người duy nhất cung cấp một số thông tin chi tiết, vì vậy tôi sẽ cấp cho bạn tiền thưởng. Tôi hy vọng OP sẽ tiếp tục thử nghiệm và chúng tôi sẽ tìm ra nguyên nhân gốc rễ. –

0

"Đối với chỉ số ghi lại cuộc gặp gỡ tìm kiếm, SELECT ... FROM ... CHO khối CẬP NHẬT phiên khác từ làm CHỌN ... TỪ ... KHÓA CHẾ ĐỘ CHIA SẺ hoặc đọc trong các mức cách ly giao dịch nhất định. Các lần đọc nhất quán sẽ bỏ qua bất kỳ khóa nào được đặt trên các bản ghi tồn tại trong chế độ xem "

được áp dụng với chọn để cập nhật để các phiên khác không thể đọc bản ghi bị khóa?