Trong câu trả lời liên quan mà bạn đang đề cập đến:
- CẬP NHẬT Postgres ... LIMIT 1
Mục tiêu là khóa một hàng tại một thời điểm. Điều này hoạt động tốt khi có hoặc không có khóa tư vấn, bởi vì không có cơ hội cho khóa - miễn là bạn không cố gắng khóa nhiều hàng hơn trong cùng một giao dịch.
Ví dụ của bạn khác ở chỗ bạn muốn khóa 3000 hàng cùng một lúc . Có là khả năng xảy ra bế tắc, ngoại trừ trường hợp tất cả các thao tác ghi đồng thời khóa các hàng theo cùng một thứ tự nhất quán. Theo tài liệu:
Cách bảo vệ tốt nhất chống lại các deadlock nói chung là tránh chúng bằng cách chắc chắn rằng tất cả các ứng dụng sử dụng cơ sở dữ liệu đều có khóa trên nhiều đối tượng theo một thứ tự nhất quán.
Triển khai điều đó bằng ORDER BY trong truy vấn con của bạn.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
Điều này là an toàn và đáng tin cậy, miễn là tất cả các giao dịch có được các khóa theo cùng một thứ tự và các cập nhật đồng thời của các cột thứ tự không được mong đợi. (Đọc hộp "THẬN TRỌNG" màu vàng ở cuối chương này trong sách hướng dẫn.) Vì vậy, điều này sẽ an toàn trong trường hợp của bạn, vì bạn sẽ không cập nhật id
cột.
Hiệu quả, chỉ một ứng dụng khách tại một thời điểm có thể thao tác các hàng theo cách này. Các giao dịch đồng thời sẽ cố gắng khóa các hàng giống nhau (bị khóa) và đợi giao dịch đầu tiên kết thúc.
Khóa tư vấn rất hữu ích nếu bạn có nhiều hoặc rất dài các giao dịch đồng thời (có vẻ như bạn không làm vậy). Chỉ với một số ít, nhìn chung sẽ rẻ hơn nếu chỉ sử dụng truy vấn trên và để các giao dịch đồng thời chờ đến lượt.
Tất cả trong một CẬP NHẬT
Có vẻ như quyền truy cập đồng thời không phải là vấn đề đối với thiết lập của bạn. Đồng thời là một vấn đề do giải pháp hiện tại của bạn tạo ra.
Thay vào đó, hãy thực hiện tất cả trong một UPDATE
duy nhất . Chỉ định các lô n
số (trong ví dụ là 3000) cho từng UUID và cập nhật tất cả cùng một lúc. Sẽ nhanh nhất.
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
Những điểm chính
-
Cắt bớt phép chia số nguyên. Bạn nhận được 1 cho 3000 hàng đầu tiên, 2 cho 3000 hàng tiếp theo. vv
-
Tôi chọn hàng tùy ý, bạn có thể áp dụng
ORDER BY
trong cửa sổ chorow_number()
để gán các hàng nhất định. -
Nếu bạn không có bảng UUID để gửi đi (
uuid_tbl
), sử dụngVALUES
biểu hiện để cung cấp cho họ. Ví dụ. -
Bạn nhận được hàng loạt 3000 hàng. Lô cuối cùng sẽ thiếu 3000 nếu bạn không tìm thấy bội số 3000 để chỉ định.