Cơ sở dữ liệu quan hệ rất tốt cho khối lượng công việc giao dịch. Nhưng mọi thứ có thể trở nên lộn xộn khi nhiều giao dịch bắt đầu cố gắng truy cập cùng một dữ liệu cùng một lúc. May mắn thay, trong nhiều cơ sở dữ liệu SQL có một giải pháp cho điều đó. SELECT FOR UPDATE
SELECT FOR UPDATE
là một lệnh SQL hữu ích trong bối cảnh khối lượng công việc giao dịch. Nó cho phép bạn “khóa” các hàng được trả về bởi một truy vấn SELECT
cho đến khi toàn bộ giao dịch mà truy vấn đó tham gia đã được cam kết. Các giao dịch khác cố gắng truy cập vào các hàng đó được đặt vào hàng chờ theo thời gian và được thực hiện theo trình tự thời gian sau khi giao dịch đầu tiên hoàn tất
Điều này rất hữu ích vì nó ngăn chặn các lần thử lại giao dịch không cần thiết và không cần thiết có thể xảy ra khi nhiều giao dịch đang cố đọc các hàng đó. Bất cứ khi nào nhiều giao dịch có khả năng hoạt động với cùng một hàng gần như cùng một lúc, thì có thể sử dụng SELECT FOR UPDATE
để tăng thông lượng và giảm độ trễ đuôi [so với những gì bạn sẽ thấy nếu không sử dụng nó]
Nói cách khác. SELECT FOR UPDATE
làm cho các giao dịch tranh chấp được xử lý trơn tru hơn [điều này thường có nghĩa là chúng được xử lý nhanh hơn và hiệu quả hơn]
Nói chung, SELECT FOR UPDATE
hữu ích cho bất kỳ khối lượng công việc giao dịch nào mà nhiều giao dịch có thể cố gắng truy cập cùng một hàng vào cùng một thời điểm
Tuy nhiên, các 'hương vị' khác nhau của SQL xử lý SELECT FOR UPDATE
hơi khác nhau và một số hoàn toàn không hỗ trợ nó. Ví dụ: MySQL hỗ trợ CHỌN ĐỂ CẬP NHẬT, nhưng SQLite thì không
Thông thường, lý do cho điều này liên quan đến cách các hệ thống khác nhau xử lý các giao dịch rộng hơn. Ví dụ: SQLite không cần SELECT FOR UPDATE
vì việc bắt đầu một giao dịch sẽ khóa toàn bộ cơ sở dữ liệu. Điều này cho phép cơ sở dữ liệu SQLite cung cấp sự cô lập giao dịch có thể tuần tự hóa, mức độ cô lập cao nhất, để đảm bảo tính nhất quán. Tuy nhiên, việc khóa toàn bộ cơ sở dữ liệu trong một giao dịch chỉ có thể ảnh hưởng đến một vài hàng có tác động tiêu cực rõ ràng đối với hiệu suất, đặc biệt là ở quy mô lớn
Tuy nhiên, lựa chọn giữa hiệu suất và tính nhất quán là không cần thiết. Ví dụ, CockroachDB cung cấp cách ly có thể tuần tự hóa, nhưng nó không phải khóa toàn bộ cơ sở dữ liệu khi bắt đầu giao dịch để thực hiện điều đó. SELECT FOR UPDATE
có thể được sử dụng để tối đa hóa hiệu suất cơ sở dữ liệu trong trường hợp các giao dịch đồng thời hoạt động trên cùng một hàng và kết quả cuối cùng [trong trường hợp của CockroachDB] vẫn là một cơ sở dữ liệu có khả năng cách ly tuần tự hóa
Các cơ sở dữ liệu SQL khác nhau xử lý cách ly giao dịch và do đó, SELECT FOR UPDATE
cũng khác nhau, vì vậy, điều quan trọng là phải làm quen với các tùy chọn và giá trị mặc định cho hệ thống bạn đang sử dụng
Hãy cùng xem cách hoạt động của SELECT FOR UPDATE
. Chúng tôi sẽ sử dụng cú pháp và các tham số của CockroachDB ở đây, nhưng cú pháp cho các cơ sở dữ liệu SQL khác hỗ trợ SELECT FOR UPDATE
sẽ tương tự
Hãy tưởng tượng chúng ta đang làm việc với một cơ sở dữ liệu bao gồm bảng sau đây
SELECT * FROM kv WHERE k = 1 FOR UPDATE NOWAIT;
4Một giao dịch hoàn chỉnh sử dụng SELECT FOR UPDATE
trên bảng đó có thể trông như thế này
BEGIN;
SELECT * FROM kv WHERE k = 1 FOR UPDATE;
UPDATE kv SET v = v + 5 WHERE k = 1;
COMMIT;
Làm việc từng dòng thông qua tuyên bố trên
- Dòng đầu tiên,
6, bắt đầu giao dịchSELECT * FROM kv WHERE k = 1 FOR UPDATE NOWAIT;
- Dòng thứ hai, câu lệnh
SELECT
, xác định các hàng sẽ bị ảnh hưởng và khóa chúng. Trong trường hợp này, đó là một hàng. hàng đầu tiên của bảng - Dòng thứ ba thực hiện cập nhật cho hàng đang đề cập. Trong trường hợp này, thêm 5 vào giá trị trong cột
8 của hàngSELECT * FROM kv WHERE k = 1 FOR UPDATE NOWAIT;
- Dòng thứ tư,
9 cam kết giao dịchSELECT * FROM kv WHERE k = 1 FOR UPDATE NOWAIT;
Nếu chúng tôi chạy giao dịch này trên bảng ví dụ của mình, kết quả sẽ là thế này
Tuy nhiên, điều quan trọng ở đây là dòng SELECT FOR UPDATE
0 [dòng 2] đã khóa hàng mà giao dịch đã cập nhật. Nếu một giao dịch khác [hãy gọi nó là Tx 2] tấn công cơ sở dữ liệu đang cố gắng hoạt động trên cùng một hàng trong khi giao dịch này [Tx 1] đang được xử lý, Tx 2 sẽ được thêm vào hàng đợi để xử lý sau khi Tx 1 xác nhận, thay vì bắt đầu thực hiện
Một lần nữa, các hệ thống cơ sở dữ liệu khác nhau cho phép các tham số và sửa đổi khác nhau của các câu lệnh SELECT
Ví dụ, phổ biến là SELECT FOR UPDATE
2, cung cấp dạng khóa hàng yếu hơn trong một số hệ thống cơ sở dữ liệu. Trong PostgreSQL, SELECT FOR UPDATE
0 khóa hoàn toàn các hàng có liên quan, trong khi SELECT FOR UPDATE
2 chỉ khóa các hàng có liên quan để cập nhật và xóa
Ngược lại, vì CockroachDB luôn đảm bảo sự cô lập có thể tuần tự hóa và không có mức khóa “yếu hơn”, nên chức năng của SELECT FOR UPDATE
2 giống hệt như SELECT FOR UPDATE
0. Cú pháp SELECT FOR UPDATE
7 chỉ được hỗ trợ để tương thích với Postgres
Một tham số phổ biến khác là SELECT FOR UPDATE
8, trả về lỗi ngay lập tức nếu giao dịch không thể khóa ngay một hàng. Trong cú pháp SQL, SELECT FOR UPDATE
8 xuất hiện ngay sau SELECT FOR UPDATE
0, như vậy
SELECT * FROM kv WHERE k = 1 FOR UPDATE NOWAIT;
SELECT FOR UPDATE
1 cũng là một tham số được hỗ trợ bởi một số cơ sở dữ liệu cho phép các giao dịch đang chờ tạm thời bỏ qua các hàng bị khóa để việc "giữ" trên các hàng đó không làm chậm quá trình xử lý các yếu tố của giao dịch ảnh hưởng đến các hàng không bị khóa. CockroachDB hiện không hỗ trợ điều này, một phần vì nó sử dụng điều khiển đồng thời đa phiên bản. Chủ đề diễn đàn này có một số thông tin tuyệt vời để đạt được các mục tiêu tương tự mà không cần sử dụng SELECT FOR UPDATE
1