我正在尝试提高SQLAlchemy数据库查询的性能。我们正在使用psycopg2。在我们的生产系统中,我们选择使用Java,因为它速度提高了至少50%,即使不是接近100%。所以我希望Stack Overflow社区中的某个人能够提高我的表现。
我认为我的下一步将是最终修补psycopg2库,使其行为类似于JDBC驱动程序。如果是这种情况并且有人已经这样做了,那就没问题,但我希望我仍然可以通过Python进行设置或重构调整。
我有一个简单的“SELECT * FROM someLargeDataSetTable”查询运行。数据集的大小为GB。快速绩效图表如下:
Records | JDBC | SQLAlchemy[1] | SQLAlchemy[2] | Psql -------------------------------------------------------------------- 1 (4kB) | 200ms | 300ms | 250ms | 10ms 10 (8kB) | 200ms | 300ms | 250ms | 10ms 100 (88kB) | 200ms | 300ms | 250ms | 10ms 1,000 (600kB) | 300ms | 300ms | 370ms | 100ms 10,000 (6MB) | 800ms | 830ms | 730ms | 850ms 100,000 (50MB) | 4s | 5s | 4.6s | 8s 1,000,000 (510MB) | 30s | 50s | 50s | 1m32s 10,000,000 (5.1GB) | 4m44s | 7m55s | 6m39s | n/a -------------------------------------------------------------------- 5,000,000 (2.6GB) | 2m30s | 4m45s | 3m52s | 14m22s -------------------------------------------------------------------- [1] - With the processrow function [2] - Without the processrow function (direct dump)
我可以添加更多(我们的数据可以多达太字节),但我认为从数据中可以看出改变斜率。随着数据集大小的增加,JDBC的表现会更好。一些笔记......
python -u
#!/usr/bin/env python # testSqlAlchemy.py import sys try: import cdecimal sys.modules["decimal"]=cdecimal except ImportError,e: print >> sys.stderr, "Error: cdecimal didn't load properly." raise SystemExit from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker def processrow (row,delimiter="|",null="\N"): newrow = [] for x in row: if x is None: x = null newrow.append(str(x)) return delimiter.join(newrow) fetchsize = 10000 connectionString = "postgresql+psycopg2://usr:pass@server:port/db" eng = create_engine(connectionString, server_side_cursors=True) session = sessionmaker(bind=eng)() with open("test.sql","r") as queryFD: with open("/dev/null","w") as nullDev: query = session.execute(queryFD.read()) cur = query.cursor while cur.statusmessage not in ['FETCH 0','CLOSE CURSOR']: for row in query.fetchmany(fetchsize): print >> nullDev, processrow(row)
在计时之后,我还运行了一个cProfile,这是最严重罪犯的转储:
Fri Mar 4 13:49:45 2011 sqlAlchemy.prof 415757706 function calls (415756424 primitive calls) in 563.923 CPU seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 563.924 563.924 {execfile} 1 25.151 25.151 563.924 563.924 testSqlAlchemy.py:2() 1001 0.050 0.000 329.285 0.329 base.py:2679(fetchmany) 1001 5.503 0.005 314.665 0.314 base.py:2804(_fetchmany_impl) 10000003 4.328 0.000 307.843 0.000 base.py:2795(_fetchone_impl) 10011 0.309 0.000 302.743 0.030 base.py:2790(__buffer_rows) 10011 233.620 0.023 302.425 0.030 {method 'fetchmany' of 'psycopg2._psycopg.cursor' objects} 10000000 145.459 0.000 209.147 0.000 testSqlAlchemy.py:13(processrow)
Fri Mar 4 14:03:06 2011 sqlAlchemy.prof 305460312 function calls (305459030 primitive calls) in 536.368 CPU seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 536.370 536.370 {execfile} 1 29.503 29.503 536.369 536.369 testSqlAlchemy.py:2() 1001 0.066 0.000 333.806 0.333 base.py:2679(fetchmany) 1001 5.444 0.005 318.462 0.318 base.py:2804(_fetchmany_impl) 10000003 4.389 0.000 311.647 0.000 base.py:2795(_fetchone_impl) 10011 0.339 0.000 306.452 0.031 base.py:2790(__buffer_rows) 10011 235.664 0.024 306.102 0.031 {method 'fetchmany' of 'psycopg2._psycopg.cursor' objects} 10000000 32.904 0.000 172.802 0.000 base.py:2246(__repr__)
不幸的是,除非在SQLAlchemy中有一种方法指定输出的null ='userDefinedValueOrString'和delimiter ='userDefinedValueOrString',否则processrow函数需要保持不变。我们目前使用的Java已经这样做了,所以比较(与processrow)需要苹果对苹果。如果有办法用纯Python或设置调整来提高processrow或SQLAlchemy的性能,我很感兴趣。
答案 0 :(得分:3)
这不是一个开箱即用的答案,你可能需要做一些客户/数据库工作来确定究竟是什么错误
备份postgresql.conf更改
log_min_duration_statement to 0
log_destination = 'csvlog' # Valid values are combinations of
logging_collector = on # Enable capturing of stderr and csvlog
log_directory = 'pg_log' # directory where log files are written,
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
debug_print_parse = on
debug_print_rewritten = on
debug_print_plan output = on
log_min_messages = info (debug1 for all server versions prior to 8.4)
停止并重新启动数据库服务器(重新加载可能无法获取更改) 重现测试,确保服务器时间和客户端时间匹配,并记录开始时间等。
将日志文件从导入到您选择的编辑器中复制(excel或其他电子表格对于获取sql&计划等的预先操作非常有用)
现在检查服务器端的时间并注意:
是服务器上报告的sql,每种情况都相同
如果相同,你应该有相同的时间
是生成游标而不是传递sql的客户端
是一个在字符集之间进行大量转换/转换或者隐式转换其他类型(如日期或时间戳)的驱动程序。
等等
为了完整性,将包含计划数据,这可能会通知客户提交的SQL是否存在严重差异。
答案 1 :(得分:2)
下面的内容可能超出您的想法或您的环境中可接受的内容,但我会将选项放在桌面上以防万一。
SELECT
中每个test.sql
的目的地是否真的是一个简单的|
分隔结果文件?|
- 分离的结果文件(例如,共享?)如果上述所有问题的答案都是是,那么您可以将SELECT ...
语句转换为COPY ( SELECT ... ) TO E'path-to-results-file' WITH DELIMITER '|' NULL E'\\N'
。
答案 2 :(得分:1)
另一种方法是使用ODBC。这假设Python ODBC驱动程序运行良好。
PostgreSQL具有适用于Windows和Linux的ODBC驱动程序。
答案 3 :(得分:0)
作为主要使用汇编程序编写程序的人,有一件事情显而易见。你在开销上浪费了时间,而且开销是需要的。
而不是使用python,它将自己包装在与DB周围的C包装器集成的其他东西中......只需用C语言编写代码。我的意思是,需要多长时间? Postgres并不难与之接口(恰恰相反)。 C是一个简单的语言。您正在执行的操作看起来非常简单。您也可以使用嵌入在C中的SQL,这只是预编译的问题。无需翻译您的想法 - 只需将其与C一起编写并使用提供的ECPG编译器(阅读postgres手册第29章iirc)。
尽可能多地删除中间界面内容,切断中间人并与本地数据库进行交谈。在我看来,在尝试使系统更简单时,实际上使它变得比它需要的更复杂。当事情变得非常混乱时,我常常问自己一个问题:“我最害怕接触的代码是什么?” - 这通常指向我需要改变的东西。
抱歉唠叨,但也许退后一步,一些新鲜空气会有所帮助;)