Không có lỗi, và tôi không nghĩ rằng bạn đang hiểu sai bất cứ điều gì; bạn chỉ còn thiếu một vài mảnh ghép của câu đố.
Khóa ngoại được triển khai nội bộ bằng cách sử dụng khóa cấp hàng; bắt đầu từ Postgres 8.1 và lên đến 9.2, bất cứ khi nào bạn cập nhật bảng tham chiếu (apples
trong trường hợp này), một truy vấn được kích hoạt SELECT FOR SHARE
trên bảng được tham chiếu (trees
). Vì vậy, SELECT FOR UPDATE
trong giao dịch đầu tiên chặn SELECT FOR SHARE
về tính toàn vẹn tham chiếu cho giao dịch thứ hai. Đây là nguyên nhân gây ra khối trong lệnh thứ hai.
Bây giờ tôi nghe thấy bạn hét lên, "Chờ đã! Tại sao nó lại chặn lệnh thứ hai chứ không phải lệnh đầu tiên? Thực sự thì lời giải thích rất đơn giản - đó chỉ là vì có một tính năng tối ưu hóa đơn giản bỏ qua SELECT FOR SHARE
nội bộ khi khóa không được sửa đổi. Tuy nhiên, điều này đơn giản ở chỗ nếu bạn cập nhật một tuple lần thứ hai, tính năng tối ưu hóa này sẽ không kích hoạt vì khó theo dõi các giá trị ban đầu hơn. Do đó bị tắc nghẽn.
Bạn cũng có thể thắc mắc tại sao tôi lại nói con số này lên đến 9.2 --- còn 9.3 thì sao? Sự khác biệt chính là trong 9.3 nó sử dụng SELECT FOR KEY SHARE
, là một cấp độ khóa mới, nhẹ hơn; nó cho phép đồng thời tốt hơn. Nếu bạn thử ví dụ của mình trong 9.3 và cũng thay đổi SELECT FOR UPDATE
thành SELECT FOR NO KEY UPDATE
(là chế độ nhẹ hơn SELECT FOR UPDATE
điều đó nói rằng bạn có thể sẽ cập nhật bộ tuple, nhưng bạn hứa sẽ không sửa đổi khóa chính và hứa không xóa nó), bạn sẽ thấy nó không chặn. (Ngoài ra, bạn có thể thử CẬP NHẬT trên hàng được tham chiếu và nếu bạn không sửa đổi khóa chính, thì khóa đó cũng sẽ không bị chặn.)
Nội dung 9.3 này được giới thiệu bởi một bản vá của bạn thực sự là http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 và tôi nghĩ đó là một vụ hack khá hay (Thông báo cam kết có thêm một số chi tiết, nếu bạn quan tâm đến loại nội dung đó). Nhưng hãy cẩn thận, không sử dụng các phiên bản trước 9.3.4 vì bản vá đó rất phức tạp nên một số lỗi nghiêm trọng không được chú ý và chúng tôi chỉ sửa gần đây.