Bất kỳ cách nào để chọn mà không gây ra khóa trong MySQL?


126

Truy vấn:

SELECT COUNT(online.account_id) cnt from online;

Nhưng bảng trực tuyến cũng được sửa đổi bởi một sự kiện, vì vậy thường xuyên tôi có thể thấy khóa bằng cách chạy show processlist.

Có ngữ pháp nào trong MySQL có thể khiến câu lệnh chọn không gây ra khóa không?

Và tôi đã quên đề cập ở trên rằng đó là trên cơ sở dữ liệu nô lệ MySQL.

Sau khi tôi thêm vào my.cnf:transaction-isolation = READ-UNCOMMITTED nô lệ sẽ gặp lỗi:

Lỗi 'Không thể đăng nhập nhị phân. Thông báo: Cấp độ giao dịch 'READ-UNCOMMITTED' trong InnoDB không an toàn cho chế độ binlog 'STATMENT' 'khi truy vấn

Vì vậy, có một cách tương thích để làm điều này?


3
Đối với những người khác gặp phải câu hỏi này và đang gặp khó khăn với các khóa trên bảng của họ: Cách myQuery sử dụng khóa bên trong phụ thuộc vào công cụ lưu trữ. Đọc câu trả lời của @zombat bên dưới.
Simon Forsberg

Câu trả lời:


169

Tìm thấy một bài viết có tiêu đề "MYSQL VỚI NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

trong MS SQL Server, bạn sẽ làm như sau:

SELECT * FROM TABLE_NAME WITH (nolock)

và tương đương MYSQL là

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

BIÊN TẬP

Michael Mior gợi ý như sau (từ các bình luận)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;

54
Chỉ cần một lưu ý cho độc giả tương lai mà bạn có thể muốn loại bỏ SESSIONvà do đó, mức giao dịch chỉ áp dụng cho giao dịch tiếp theo. Sau đó, chỉ cần thay thế câu lệnh thứ ba ở trên bằng COMMIT. Đây sẽ là một noop trong trường hợp này, nhưng có tác dụng phụ là kết thúc giao dịch và đặt lại về mức cô lập mặc định.
Michael Mior

3
Chỉ cần một lưu ý, liên kết đó đã chết ... :(
longda

5
Xin lỗi, nhưng tôi phải đánh giá thấp câu trả lời này vì đã không đề cập đến những khác biệt rất quan trọng giữa InnoDB và MyISAM ở đây. Như đã nêu bởi @omg ở trên, điều này sẽ hoạt động cho InnoDB nhưng không hoạt động cho các bảng MyISAM.
Simon Forsberg

4
@Craig Chắc chắn là MyISAM không phát hành khóa READ trong các truy vấn CHỌN - có các khóa và đối lập với InnoDB, các khóa đó là khóa bảng, chặn tất cả các khóa WRITE được yêu cầu tất cả các truy vấn tiếp theo trong quá trình thực thi. Câu hỏi ban đầu dường như là về InnoDB và các mức cô lập cũng không tồn tại đối với MyISAM - tài liệu choSET TRANSACTION trạng thái câu lệnh : "Câu lệnh này đặt mức cô lập giao dịch, được sử dụng cho các hoạt động trên các bảng InnoDB."
syirecton-dj

1
Điểm thủng lưới. :-) Tôi đã thực sự cố gắng đề cập đến hành vi khóa của MyISAM vs InnoDB. Những giải pháp cấp dựa cô lập không áp dụng cho MyISAM, mà không phải là giao dịch, vì vậy nó sử dụng một khóa bảng đơn giản. MyISAM CẬP NHẬT và XÓA phải chờ khóa bảng để xóa, do đó, bất kỳ lệnh CHỌN tiếp theo nào xếp sau yêu cầu ghi, bị chặn cho đến khi quá trình ghi kết thúc. MyISAM không có "đọc bẩn" và không có cách nào cho phép hầu hết các bài viết xảy ra đồng thời với các lần đọc, do đó không có ý kiến ​​gì về bất kỳ bình luận nào ở đây "không giải quyết MyISAM." Tôi nghĩ đó là những gì tôi đã nhận được. :-)
Craig

24

Nếu bảng là InnoDB, hãy xem http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html - nó sử dụng tính năng đọc nhất quán (chế độ không khóa) cho CHỌN "làm được không chỉ định FOR UPDATE hoặc LOCK IN SHARE MODE nếu tùy chọn innodb_locks_unsafe_for_binlog được đặt và mức cô lập của giao dịch không được đặt thành SERIALIZABLE. Do đó, không có khóa nào được đặt trên các hàng được đọc từ bảng đã chọn ".


16

Sử dụng

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Tài liệu phiên bản 5.0 đang ở đây .

Phiên bản 5.1 Tài liệu đang ở đây .


2
Cảm ơn bạn, tôi nghĩ rằng nó gần, nhưng tuyên bố này sẽ ảnh hưởng trong bao lâu? Tôi sẽ sử dụng câu lệnh này trong một chương trình PHP và nên được thiết lập lại tốt nhất CẤP PHÂN TÍCH GIAO DỊCH tự động sau khi truy vấn kết thúc
omg

14

Bạn có thể muốn đọc trang này của hướng dẫn sử dụng MySQL. Làm thế nào một bảng bị khóa phụ thuộc vào loại bảng.

MyISAM sử dụng khóa bảng để đạt được tốc độ đọc rất cao, nhưng nếu bạn có câu lệnh CẬP NHẬT đang chờ, thì các CHỌN trong tương lai sẽ xếp hàng phía sau CẬP NHẬT.

Các bảng InnoDB sử dụng khóa cấp hàng và bạn sẽ không khóa toàn bộ bảng phía sau CẬP NHẬT. Có các loại sự cố khóa khác liên quan đến InnoDB, nhưng bạn có thể thấy nó phù hợp với nhu cầu của bạn.


1
"THIẾT LẬP CẤP PHÂN TÍCH GIAO DỊCH ĐỌC HIỂU" có hoạt động cho các bảng MyISAM không?
omg

5
Bảng MyISAM không hỗ trợ giao dịch dưới mọi hình thức. Một truy vấn giao dịch sẽ chạy trên bảng MyISAM, vì vậy truy vấn bạn đề cập ở trên sẽ thực thi, nhưng nó không có hiệu lực.
zombat

1
Vậy thì tôi có thể làm gì để tránh CHỌN xếp hàng trong trường hợp MyISAM?
omg

6
Tôi có thể làm gì để tránh CHỌN xếp hàng trong trường hợp MyISAM? Chuyển sang innodb. MyISAM sử dụng khóa cấp bảng cho mọi truy vấn. Đó là lỗ hổng lớn.
Nông dân Frank

2

Tùy thuộc vào loại bảng của bạn, khóa sẽ thực hiện khác nhau, nhưng số lượng CHỌN cũng sẽ như vậy. Đối với các bảng MyISAM, bảng CHỌN đơn giản (*) TỪ bảng không nên khóa bảng vì nó truy cập dữ liệu meta để kéo số lượng bản ghi. Innodb sẽ mất nhiều thời gian hơn vì phải lấy bảng trong ảnh chụp nhanh để đếm các bản ghi, nhưng nó không gây ra khóa.

Ít nhất bạn nên đặt conc conc_insert thành 1 (mặc định). Sau đó, nếu không có "khoảng trống" nào trong tệp dữ liệu để bảng cần điền, các phần chèn sẽ được thêm vào tệp và CHỌN và CHỌN có thể xảy ra đồng thời với các bảng MyISAM. Lưu ý rằng việc xóa một bản ghi sẽ tạo ra một "khoảng trống" trong tệp dữ liệu sẽ cố gắng lấp đầy các bản cập nhật và chèn trong tương lai.

Nếu bạn hiếm khi xóa các bản ghi, thì bạn có thể đặt concallel_insert bằng 2 và các phần chèn sẽ luôn được thêm vào cuối tệp dữ liệu. Sau đó, chọn và chèn có thể xảy ra đồng thời, nhưng tệp dữ liệu của bạn sẽ không bao giờ nhỏ hơn, bất kể bạn xóa bao nhiêu bản ghi (ngoại trừ tất cả các bản ghi).

Điểm mấu chốt, nếu bạn có nhiều cập nhật, chèn và chọn trên một bảng, bạn nên đặt nó là InnoDB. Bạn có thể tự do trộn các loại bảng trong một hệ thống.


0

Từ tài liệu tham khảo này :

Nếu bạn có được khóa bảng rõ ràng với LOCK TABLES, bạn có thể yêu cầu khóa ĐỌC ĐỊA PHƯƠNG thay vì khóa ĐỌC để cho phép các phiên khác thực hiện chèn đồng thời trong khi bạn khóa bảng.


0

CHỌN thường không thực hiện bất kỳ khóa nào mà bạn quan tâm trên các bảng InnoDB. Mức cô lập giao dịch mặc định có nghĩa là chọn không khóa công cụ.

Tất nhiên sự tranh chấp vẫn xảy ra.


1
Tôi biết bài này đã cũ, nhưng câu trả lời này quá chung chung và đôi khi chỉ đúng. Xem dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html . Khóa chắc chắn được để đọc, tùy thuộc vào mức độ cô lập. Cụ thể, trong trường hợp này, người đăng đang xử lý các cơ sở dữ liệu được nhân rộng và tuyên bố rõ ràng rằng anh ta có thể sử dụng show processlistđể thực sự nhìn thấy các khóa. Vì vậy, nó an toàn để giả định rằng trên thực tế có khóa được thực hiện.
Craig

1
Câu trả lời luôn luôn đúng. Tất nhiên, có một số khóa - một số mutexes bên trong innodb được sử dụng (ví dụ mutex pool pool innodb chẳng hạn). Hầu hết người dùng không quan tâm hoặc chú ý đến các khóa này và họ thường chỉ tranh chấp trong các hoạt động DDL (chẳng hạn như nếu bạn có nhóm bộ đệm 16G và thực hiện "thả bảng" trong một luồng khác). Nhưng nó không có bất kỳ khóa hàng theo mặc định. Ý tôi là thế Câu trả lời khá mơ hồ.
MarkR

1
Luôn luôn luôn? Điều gì xảy ra nếu mức cô lập giao dịch được đặt thành tuần tự hóa hoặc câu lệnh chọn sử dụng LOCK IN SHARE MODE và autocommit bị vô hiệu hóa? Tôi biết nhiều máy chủ cơ sở dữ liệu (hầu hết / tất cả?) Hiện tại sử dụng cách ly ảnh chụp nhanh thay vì xê-ri hóa thực sự, nhưng thỉnh thoảng vẫn có những biện minh cho việc buộc đọc tuần tự? Nhưng có vẻ như bạn đã nói rằng trong tất cả các trường hợp từ xa bình thường, các điều kiện mặc định trong MySQL không gây ra các khóa đọc ảnh hưởng đến các luồng khác, vì vậy đừng lo lắng về vấn đề bạn không gặp phải? Tôi đã cố gắng hoàn tác downvote của mình, BTW. Xin lỗi ...
Craig

3
Tôi nói "không bình thường". Ý tôi là nếu bạn thực hiện một lựa chọn bình thường (không có FOR UPDATE hoặc LOCK IN SHARE MODE) và sử dụng mức cô lập giao dịch mặc định. Có một số trường hợp hợp lệ để thay đổi mức cô lập, nhưng tôi chỉ thực hiện trên cơ sở mỗi phiên không bao giờ là mặc định.
MarkR

0

một cách khác để kích hoạt tính năng đọc bẩn trong mysql là thêm gợi ý: LOCK IN SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 

"Nếu autocommit được đặt thành 1, các mệnh đề LOCK IN SHARE và FOR UPDATE không có hiệu lực." ... và autocommit = 1 là mặc định
Martin Zvarík
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.