考慮以下代碼:
import psycopg2
conn = psycopg2.connect(**credentials)
cur = conn.cursor()
cur.execute('select * from some_table') # Imagine some_table to be a very big table
while True:
rows = cur.fetchmany(1000)
if not rows:
break
do_some_processing(rows)
cur.close()
conn.commit()
問題1:如果并發事務some_table在回圈運行時插入新行,如果事務隔離級別設定為“已提交讀”,是否會獲取新行?
問題2:如果一個并發事務some_table在回圈運行時更新了一些行,如果事務隔離級別設定為“已提交讀”,是否會獲取更新的行?
根據 Postgres 檔案:
Read Committed 是 PostgreSQL 中的默認隔離級別。當事務使用此隔離級別時,SELECT 查詢(沒有 FOR UPDATE/SHARE 子句)只會看到在查詢開始之前提交的資料;它永遠不會看到未提交的資料或并發事務在查詢執行期間提交的更改。實際上,SELECT 查詢會在查詢開始運行的那一刻看到資料庫的快照。但是,SELECT 確實會看到在其自己的事務中執行的先前更新的影響,即使它們尚未提交。另請注意,如果其他事務在第一個 SELECT 開始后和第二個 SELECT 開始之前提交更改,則兩個連續的 SELECT 命令可以看到不同的資料,即使它們在單個事務中。
在上面的代碼SELECT中,事務中只有 1 個查詢,這意味著沒有“連續的 SELECT 命令”,所以我的假設是游標不會看到任何新的插入/更新。這是正確的嗎?如果是,那么游標如何始終“記住”資料庫的舊狀態?如果回圈運行幾個小時/天怎么辦?這種情況會導致一些與 MVCC 相關的磁盤膨脹或類似的事情嗎?
uj5u.com熱心網友回復:
只要游標打開,舊版本的行就會被保留。是的,這意味著將游標保持打開數天將面臨膨脹的風險,因為無法洗掉在打開游標時有效的當前過時的元組。
您可能會爭辯說,這僅需要應用于定義了游標的表,因為稍后無法將新表添加到查詢中(在讀提交模式下)。但是這種推理不適用于具有動態 SQL 的函式,因為它可以隨時引入新表。無論如何,它會使會計變得更加復雜,所以它沒有完成。因此,在游標關閉之前,在整個資料庫范圍內都不能洗掉游標時代的元組。
uj5u.com熱心網友回復:
無論游標持續多久,您的游標都會看到 SELECT 陳述句開始時出現的任何記錄。資料庫服務器非常擅長將多個“代”表分開。例如,如果您使用 BEGIN TRANSACTION,那么在出現 COMMIT TRANSACTION 之前,除了您之外沒有其他人會看到您所做的更改。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/385348.html
標籤:Python PostgreSQL的 psycopg2 事务隔离
