在使用 Psycopg2 Cursor 向 PostgreSQL 数据库发起查询时,有两种方式:一种是在 server 端执行查询(server-side cursor),另一种是在 client 端执行查询(client-side cursor)。虽然两种方式都可以使用 Psycopg2 完成,并且在某些场景下二者的速度并没有很大的差距,但是在某些情况下,server-side cursor 的性能却明显要比 client-side cursor 高,尤其是在大批量数据查询的场景下。
下面是使用 server-side cursor 的代码示例:
import psycopg2
from psycopg2.extras import RealDictCursor
conn = psycopg2.connect(
user="username",
password="password",
host="localhost",
port=5432,
database="database_name"
)
with conn.cursor(cursor_factory=RealDictCursor) as cursor:
cursor.execute("SELECT * FROM my_table")
# fetch rows one by one
while True:
row = cursor.fetchone()
if not row:
break
print(row)
conn.close()
上面这段代码中,我们使用 RealDictCursor
作为 cursor 的工厂函数,并将其传入 cursor()
方法中。这是因为 RealDictCursor
可以将查询结果按照 dict 的形式返回,易于数据处理。
需要注意的是,在使用 server-side cursor 时,必须要开启自动提交模式,这可以通过 autocommit=True
来实现。如果没有开启自动提交模式,在 fetch 完数据后会导致事务一直处于打开状态,无法关闭。
conn.autocommit = True
Server端与Client端的 Psycopg2 Cursor 性能差异问题〔
使用 server-side cursor 是一个解决方案,但实际上,在使用 Psycopg2 Cursor 进行查询时,还有一些其他的优化技巧,例如:
-
尽量使用批量查询,而非一次一次地查询,可以显著提高查询效率。
-
使用 readonly
事务模式。
conn.set_session(readonly=True, autocommit=True)
- 使用 psycopg2 的
execute_values()
方法进行批量插入。
from psycopg2.extras import execute_values
import random
values = [(i, random.randint(10,20)) for i in range(1000000)]
with conn.cursor() as cursor:
cursor.execute("""
CREATE TABLE IF NOT EXISTS test (id SERIAL PRIMARY KEY, num INTEGER);
""")
execute_values(cursor, "INSERT INTO test (id, num) VALUES %s", values, page_size=10000)
使用 execute_values()
方法插入数据比使用 execute()
方法一个一个插入数据要快很多。
综上所述,在使用 Psycopg2 Cursor 进行查询时,建议尽可能使用 server-side cursor,并且尝试采用上述的优化技巧,可以显著提高查询效率。
使用 server-side cursor 是一个解决方案,但实际上,在使用 Psycopg2 Cursor 进行查询时,还有一些其他的优化技巧,例如:
-
尽量使用批量查询,而非一次一次地查询,可以显著提高查询效率。
-
使用 readonly
事务模式。
conn.set_session(readonly=True, autocommit=True)
- 使用 psycopg2 的
execute_values()
方法进行批量插入。
from psycopg2.extras import execute_values
import random
values = [(i, random.randint(10,20)) for i in range(1000000)]
with conn.cursor() as cursor:
cursor.execute("""
CREATE TABLE IF NOT EXISTS test (id SERIAL PRIMARY KEY, num INTEGER);
""")
execute_values(cursor, "INSERT INTO test (id, num) VALUES %s", values, page_size=10000)
使用 execute_values()
方法插入数据比使用 execute()
方法一个一个插入数据要快很多。
综上所述,在使用 Psycopg2 Cursor 进行查询时,建议尽可能使用 server-side cursor,并且尝试采用上述的优化技巧,可以显著提高查询效率。