2017-02-15 15 views
12

Tôi có một mục và Jobs bảng:Khả năng tương thích và tuần tự của Postgres. Tôi có cần mức cách ly SERIALIZABLE không?

Items

  • id = PK
  • job_id = Jobs FK
  • status = IN_PROGRESS | COMPLETE

Jobs

  • id = PK

Items bắt đầu ra như IN_PROGRESS, nhưng công việc được thực hiện trên chúng, và đưa ra một công nhân để cập nhật. Tôi có một quá trình cập nhật đang cập nhật các mục khi họ đến, với một trạng thái mới. Cách tiếp cận mà tôi đã thực hiện cho đến nay là (trong mã giả):

def work(item: Item) = { 
    insideTransaction { 
    updateItemWithNewStatus(item) 
    jobs, items = getParentJobAndAllItems(item) 
    newJobStatus = computeParentJobStatus(jobs, items) 
    // do some stuff depending on newJobStatus 
    } 
} 

Điều đó có hợp lý không? Tôi muốn điều này làm việc trong một môi trường đồng thời. Vấn đề tôi có ngay bây giờ, là COMPLETE được đưa ra nhiều lần cho một công việc, khi tôi chỉ muốn làm logic trên COMPLETE, một lần.

Nếu tôi thay đổi cấp độ giao dịch thành SERIALIZABLE, tôi nhận được thông báo "L ERI: không thể tuần tự hóa truy cập do đọc/ghi phụ thuộc giữa các giao dịch" như mô tả.

Vì vậy, câu hỏi của tôi là:

  • Tôi có cần SERIALIZABLE?
  • Tôi có thể lấy đi bằng SELECT FOR UPDATE và ở đâu không?
  • Ai đó có thể giải thích cho tôi điều gì đang xảy ra và tại sao?

Chỉnh sửa: Tôi đã mở lại câu hỏi này vì tôi không hài lòng với giải thích câu trả lời trước đó. Có ai có thể giải thích điều này cho tôi không? Cụ thể, tôi muốn một số truy vấn mẫu cho mã giả đó.

Trả lời

2

Nếu bạn muốn công việc có thể chạy đồng thời, không SERIALIZABLE cũng không SELECT FOR UPDATE sẽ hoạt động trực tiếp.

Nếu bạn khóa hàng bằng cách sử dụng SELECT FOR UPDATE, thì một quy trình khác sẽ chỉ chặn khi thực thi SELECT FOR UPDATE cho đến khi quá trình đầu tiên cam kết giao dịch.

Nếu bạn làm SERIALIZABLE, cả hai quy trình có thể chạy đồng thời (xử lý cùng một hàng) nhưng ít nhất một quá trình được dự kiến ​​sẽ không thành công khi thời gian thực hiện COMMIT do cơ sở dữ liệu sẽ phát hiện xung đột. Ngoài ra SERIALIZABLE có thể thất bại nếu nó xung đột với bất kỳ truy vấn nào khác đang diễn ra trong cơ sở dữ liệu cùng một lúc ảnh hưởng đến các hàng có liên quan. Lý do thực sự để sử dụng SERIALIZABLE là chính xác nếu bạn đang cố gắng bảo vệ chống lại các cập nhật cơ sở dữ liệu đồng thời được thực hiện bởi các công việc khác, trái ngược với việc ngăn chặn cùng một công việc thực hiện hai lần.

Lưu ý có các thủ thuật để thực hiện SELECT FOR UPDATE bỏ qua các hàng bị khóa. Nếu bạn làm điều đó thì bạn có thể có đồng thời thực tế. Xem Select unlocked row in Postgresql.

Cách tiếp cận khác tôi thấy thường xuyên hơn là thay đổi cột "trạng thái" của bạn để có trạng thái tạm thời thứ 3 được sử dụng trong khi công việc đang được xử lý. Thông thường, một người sẽ có các trạng thái như 'ĐANG ĐANG', 'KHÔNG GIAN', 'HOÀN THÀNH'. Khi quá trình của bạn tìm kiếm công việc cần làm, nó tìm thấy công việc 'ĐANG ĐANG', ngay lập tức di chuyển nó sang 'IN_PROGRESS' và cam kết giao dịch, sau đó tiếp tục với công việc và cuối cùng chuyển nó đến 'COMPLETE'. Điểm bất lợi là nếu quá trình chết trong khi xử lý một công việc, nó sẽ bị bỏ lại trong 'IN_PROGRESS' vô thời hạn.

4

Bạn có thể sử dụng SELECT FOR UPDATE trên itemsjobs và làm việc trên các hàng bị ảnh hưởng trong cả hai bảng trong một giao dịch. Điều đó là đủ để thực thi tính toàn vẹn của toàn bộ hoạt động mà không cần chi phí SERIALIZABLE hoặc khóa bàn.

tôi sẽ đề nghị bạn tạo một hàm được gọi sau khi một chèn hoặc cập nhật được thực hiện trên bảng items, đi qua các PK của mục:

CREATE FUNCTION process_item(item integer) RETURNS void AS $$ 
DECLARE 
    item items%ROWTYPE; 
    job jobs%ROWTYPE; 
BEGIN -- Implicitly starting a transaction 
    SELECT * INTO job FROM jobs 
    WHERE id = (SELECT job_id FROM items WHERE id = item) 
    FOR UPDATE; -- Lock the row for other users 

    FOR item IN SELECT * FROM items FOR UPDATE LOOP  -- Rows locked 
     -- Work on items individually 

     UPDATE items 
     SET status = 'COMPLETED' 
     WHERE id = item.id; 
    END LOOP; 

    -- Do any work on the job itself 
END; -- Implicitly close the transaction, releasing the locks 
$$ LANGUAGE plpgsql; 

Nếu một số quy trình khác đã được thực hiện công việc hoặc bất kỳ mục liên quan nào của nó, thì việc thực thi sẽ dừng lại cho đến khi khóa khác được giải phóng. Điều này khác với SERIALIZABLE sẽ hoạt động cho đến khi không thành công và sau đó bạn phải thực hiện lại tất cả quá trình xử lý trong lần thử thứ hai.

+0

Cảm ơn bạn, đây là một câu trả lời tuyệt vời. Vì vậy, SELECT CHO CẬP NHẬT trên một tham gia khóa tất cả mọi thứ tôi cần? –

+0

Trừ khi bạn chỉ rõ một cách rõ ràng trên bảng nào để áp dụng khóa, tất cả các bảng trong truy vấn sẽ khóa tất cả các hàng bị ảnh hưởng. – Patrick

+0

Ah. Đuợc. Cảm ơn –

Các vấn đề liên quan