12.4. 應用層的數據完整性檢查

因為不管哪種隔離級別,對 PostgreSQL 的讀動作不會鎖定數據, 一個事務讀取的數據可能被另一個事務覆蓋。換句話說,如果一條 SELECT 返回了一行, 這並不意味著在返回該行時該行還存在(也就是說在語句完成或事務開始後的某時) 該行可能已經被一個在此事務開始之後提交的事務更新或者刪除。 即使該行"現在"仍然有效,那它也可能在當前事務提交或者回滾之前被改變或者刪除。

另外一個認識它的方法是每個事務都看到一個數據庫內容的快照, 而並行執行的事物很可能看到不同的快照。 因此不管怎樣,整個"現在"的概念都是值得懷疑的。 不過如果客戶端應用相互隔離,那麼這就不是個大問題, 但是如果客戶端之間在數據庫外部相互之間通過通道通訊,那就可能有嚴重的歧義。

要保證一行的實際存在和避免其被並行更新,我們必須使用 SELECT FOR UPDATE 或者或者合適的 LOCK TABLE 語句。 (SELECT FOR UPDATE 只是對其它的並行更新鎖住返回的行,而 LOCK TABLE 保護整個表。) 當從其他環境向 PostgreSQL 裡用可串行化模式移植應用時一定要把這些問題考慮進去。 (在版本 6.5 前,PostgreSQL 使用讀動作鎖,因而當從以前的 PostgreSQL 版本向6.5(或更高版本)升級時也要考慮這些問題。)

MVCC 環境下,全局有效性檢查需要一些額外的考慮。 比如,一個銀行應用可能會希望檢查一個表重的所有扣款總和等于另外一個表中的加款總和, 同時兩個表還會被活躍地更新。在讀已提交模式下比較兩個連續的 SELECT sum(...) 命令的結果是不可靠的, 因為第二個查詢很可能會包含第一個沒計算的事務提交的結果。 在一個可串行化的事務裡進行兩個求和則給出在可串行化事務開始之前提交的所有事務產生的精確的結果 --- 但我們還是會合理地置疑在結果提交的時候,它們是否還相關。 如果可串行化事務本身在試圖做一致性檢查之前進行了某些變更, 那麼檢查的有用性就更加值得討論了,因為現在它包含了一些,但不是全部,事務開始後的變化。 在這種情況下,一個仔細的人會希望鎖住所有需要檢查的表, 這樣才能獲得一個無可置疑的當前現狀的圖象。 一個 SHARE 模式(或者更高級)的鎖保證在被鎖定表中除了當前事務之外,沒有未提交的更新。

還要注意如果我們依賴明確鎖定來避免並發更新,那麼我們應該使用讀已提交模式, 或者是在可串行化模式裡在執行命令之前小心地獲取鎖。 在可串行化事務裡的明確鎖定保證了不會有其它正在運行的修改該表的事務存在, 但是如果事務看到的快照提前獲取了鎖,那麼它可能提前把一些現在已經提交的改變放到表中。 一個可串行化事務的快照實際上是在它的第一個查詢或者數據修改命令開始的時候凍結的(SELECTINSERTUPDATE,或 DELETE), 因此我們可能在快照凍結之前獲取明確的鎖。