2011-01-23 55 views
2

考慮以下兩個Python代碼示例,它們實現相同但具有顯着和令人驚訝的性能差異。Postgres:使用光標更新時的令人驚訝的性能

import psycopg2, time 

conn = psycopg2.connect("dbname=mydatabase user=postgres") 
cur = conn.cursor('cursor_unique_name') 
cur2 = conn.cursor() 

startTime = time.clock() 
cur.execute("SELECT * FROM test for update;") 
print ("Finished: SELECT * FROM test for update;: " + str(time.clock() - startTime)); 
for i in range (100000): 
    cur.fetchone() 
    cur2.execute("update test set num = num + 1 where current of cursor_unique_name;") 
print ("Finished: update starting commit: " + str(time.clock() - startTime)); 
conn.commit() 
print ("Finished: update : " + str(time.clock() - startTime)); 

cur2.close() 
conn.close() 

和:

import psycopg2, time 

conn = psycopg2.connect("dbname=mydatabase user=postgres") 
cur = conn.cursor('cursor_unique_name') 
cur2 = conn.cursor() 

startTime = time.clock() 
for i in range (100000): 
    cur2.execute("update test set num = num + 1 where id = " + str(i) + ";") 
print ("Finished: update starting commit: " + str(time.clock() - startTime)); 
conn.commit() 
print ("Finished: update : " + str(time.clock() - startTime)); 

cur2.close() 
conn.close() 

爲表測試​​CREATE語句是:

CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar); 

這表包含10萬行和真空分析測試;已經運行。

我在幾次嘗試中得到了以下結果。

第一個代碼示例:

Finished: SELECT * FROM test for update;: 0.00609304950429 
Finished: update starting commit: 37.3272754429 
Finished: update : 37.4449708474 

第二個代碼示例:

Finished: update starting commit: 24.574401185 
Finished committing: 24.7331461431 

這是非常令人驚訝的我,我會覺得是應該是完全相反的,這意味着使用光標的更新應該是根據this回答顯着更快。

回答

4

我不認爲測試是平衡的 - 你的第一個代碼是從光標獲取數據,然後更新,而第二個是盲目地通過ID更新而不提取數據。我假定第一個代碼序列轉換爲一個FETCH命令,然後是UPDATE-,所以這是兩個客戶端/服務器命令轉換,而不是一個。

(也是第一個代碼開始在此拉動整個表到緩衝區的cache的表 - 鎖定每一行,雖然想着它,我懷疑這實際上會影響性能,但是你沒有提到它)

另外tbh我認爲對於一個簡單的表,ctid更新(我假設它是如何工作的)和通過主鍵更新之間不會有太大的不同 - pkey更新是額外的索引查找,但除非索引是巨大的它沒有太大的退化。

爲了更新這樣的100,000行,我懷疑大部分時間都是生成額外的元組並將它們插入或附加到表中,而不是定位前面的元組來標記爲已刪除。

相關問題