考虑以下两个Python代码示例,这些示例实现相同但性能差异显着且令人惊讶。
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);
该表包含100000行和VACUUM ANALYZE TEST;已经运行了。
我在几次尝试中都得到了以下结果。
第一个代码示例:
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答案,使用光标的更新应该明显更快。
答案 0 :(得分:4)
我不认为测试是平衡的 - 您的第一个代码是从光标获取数据然后更新,而第二个代码是通过ID盲目更新而不获取数据。我假设第一个代码序列转换为FETCH命令后跟UPDATE-这样两个客户端/服务器命令转向而不是一个。
(第一个代码也是通过锁定表中的每一行开始的 - 这会将整个表拉入缓冲区缓存 - 虽然考虑到它,我怀疑这实际上会影响性能,但你没有提到它)
另外tbh我认为对于一个简单的表,ctid更新(我假设是where current of...
如何工作)和通过主键更新之间没有太大的区别 - pkey更新是一个额外的索引查找,但除非索引巨大,否则它并没有太大的退化。
为了更新这样的100,000行,我怀疑大部分时间都会产生额外的元组并将它们插入或附加到表中,而不是找到前一个元组以将其标记为已删除。