Các đảm bảo đã nêu của bạn áp dụng trong trường hợp đơn giản này, nhưng không nhất thiết phải áp dụng trong các truy vấn phức tạp hơn một chút. Xem phần cuối của câu trả lời để biết ví dụ.
Trường hợp đơn giản
Giả sử rằng col1 là duy nhất, có chính xác một giá trị "2" hoặc có thứ tự ổn định nên mọi UPDATE
khớp với các hàng giống nhau theo cùng một thứ tự:
Điều gì sẽ xảy ra cho truy vấn này là các chủ đề sẽ tìm thấy hàng có col =2 và tất cả đều cố gắng lấy một khóa ghi trên tuple đó. Chính xác là một trong số họ sẽ thành công. Những người khác sẽ chặn chờ giao dịch của chuỗi đầu tiên được cam kết.
Tx đầu tiên đó sẽ ghi, cam kết và trả về số hàng là 1. Cam kết sẽ giải phóng khóa.
Các tx khác sẽ lại cố gắng nắm lấy ổ khóa. Từng người một, họ sẽ thành công. Mỗi giao dịch sẽ lần lượt trải qua quy trình sau:
- Có được khóa ghi trên bộ dữ liệu đang tranh chấp.
- Kiểm tra lại
WHERE col=2
điều kiện sau khi nhận được khóa. - Việc kiểm tra lại sẽ cho thấy rằng điều kiện không còn phù hợp nữa nên
UPDATE
sẽ bỏ qua hàng đó. -
UPDATE
không có hàng nào khác nên nó sẽ báo cáo không có hàng nào được cập nhật. - Cam kết, giải phóng khóa cho lần tiếp theo cố gắng giữ nó.
Trong trường hợp đơn giản này, việc khóa cấp độ hàng và kiểm tra lại điều kiện sẽ tuần tự hóa các bản cập nhật một cách hiệu quả. Trong những trường hợp phức tạp hơn, không quá nhiều.
Bạn có thể dễ dàng chứng minh điều này. Mở nói bốn phiên psql. Trong lần đầu tiên, hãy khóa bảng bằng BEGIN; LOCK TABLE test;
. Trong phần còn lại của các phiên, hãy chạy UPDATE
giống hệt nhau s - họ sẽ chặn trên khóa cấp bảng. Bây giờ, hãy mở khóa bằng cách COMMIT
ting phiên đầu tiên của bạn. Xem chúng chạy đua. Chỉ một sẽ báo cáo số hàng là 1, những người khác sẽ báo cáo 0. Điều này dễ dàng được tự động hóa và theo tập lệnh để lặp lại và mở rộng quy mô lên nhiều kết nối / chuỗi hơn.
Để tìm hiểu thêm, hãy đọc quy tắc viết đồng thời , trang 11 của Các vấn đề về đồng thời PostgreSQL - và sau đó đọc phần còn lại của bản trình bày đó.
Và nếu col1 không phải là duy nhất?
Như Kevin đã lưu ý trong các nhận xét, if col
không phải là duy nhất, vì vậy bạn có thể so khớp nhiều hàng, sau đó thực hiện các lần thực thi khác nhau của UPDATE
có thể nhận được các thử thách khác nhau. Điều này có thể xảy ra nếu họ chọn các gói khác nhau (giả sử một gói là qua PREPARE
và EXECUTE
và một cái khác là trực tiếp, hoặc bạn đang làm rối với enable_
GUC) hoặc nếu kế hoạch mà tất cả họ sử dụng đều sử dụng một loại giá trị bằng nhau không ổn định. Nếu họ lấy các hàng theo một thứ tự khác thì tx1 sẽ khóa một bộ, tx2 sẽ khóa một bộ khác, sau đó mỗi người sẽ cố gắng lấy khóa trên các bộ đã được khóa của nhau. PostgreSQL sẽ hủy bỏ một trong số chúng với một ngoại lệ deadlock. Đây là một lý do chính đáng khác tại sao tất cả mã cơ sở dữ liệu của bạn nên luôn luôn chuẩn bị để thử lại các giao dịch.
Nếu bạn cẩn thận, hãy đảm bảo UPDATE
đồng thời s luôn nhận được các hàng giống nhau theo cùng một thứ tự mà bạn vẫn có thể dựa vào hành vi được mô tả trong phần đầu tiên của câu trả lời.
Thật thất vọng, PostgreSQL không cung cấp UPDATE ... ORDER BY
vì vậy việc đảm bảo rằng các bản cập nhật của bạn luôn chọn các hàng giống nhau theo cùng một thứ tự không đơn giản như bạn mong muốn. A SELECT ... FOR UPDATE ... ORDER BY
theo sau là UPDATE
riêng biệt thường an toàn nhất.
Các truy vấn phức tạp hơn, hệ thống xếp hàng
Nếu bạn đang thực hiện các truy vấn với nhiều giai đoạn, liên quan đến nhiều bộ giá trị hoặc các điều kiện khác với mức bình đẳng, bạn có thể nhận được kết quả đáng ngạc nhiên khác với kết quả của thực hiện nối tiếp. Đặc biệt, chạy đồng thời bất kỳ thứ gì như:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
hoặc các nỗ lực khác để xây dựng một hệ thống "hàng đợi" đơn giản sẽ * không thành công * để làm việc như bạn mong đợi. Xem tài liệu PostgreSQL về đồng thời và bản trình bày này để biết thêm thông tin.
Nếu bạn muốn một hàng đợi công việc được hỗ trợ bởi cơ sở dữ liệu, có các giải pháp đã được thử nghiệm tốt để xử lý tất cả các trường hợp góc phức tạp đáng ngạc nhiên. Một trong những cách phổ biến nhất là PgQ . Có một giấy PgCon hữu ích về chủ đề và tìm kiếm trên Google cho 'hàng đợi postgresql' có đầy đủ các kết quả hữu ích.
BTW, thay vì LOCK TABLE
bạn có thể sử dụng SELECT 1 FROM test WHERE col = 2 FOR UPDATE;
để có được một khóa ghi chỉ vào đó trên tuple. Điều đó sẽ chặn các cập nhật chống lại nó nhưng không chặn ghi vào các bộ dữ liệu khác hoặc chặn bất kỳ lần đọc nào. Điều đó cho phép bạn mô phỏng các loại vấn đề đồng thời khác nhau.