在PostgreSQL查询上使用多重处理的Python减少了运行时间

时间:2019-05-31 19:07:59

标签: python sql postgresql multiprocessing

我在Python中有以下代码,它将连续执行5个查询。每个查询的平均运行时间约为181.1秒(约3分钟),所有5个查询的总运行时间为905.4秒(约15分钟)。最终,在将数据加载到DataFrames中之后,我将执行ETL工作(主要是查找错误,数据质量问题和不一致),但是在此之前,我想尝试利用多处理来减少运行时。我对Python中的多重处理不熟悉,因此我一直在阅读有关不同方法的信息(队列与池等)。我很好奇哪种方法最适合此工作流程,我将如何实现呢?理想情况下,此代码的多过程翻译版本或到达那里的指南会很棒。

谢谢。

编辑:如果不清楚,我想同时运行所有5个查询。可能存在问题的是将每个DataFrame同时添加到列表中,因此,如果需要,我愿意放弃。

import pandas as pd
import psycopg2
import time
import os

host = os.environ["DBHOST"]
user = os.environ["DBUSER"]
pass = os.environ["DBPWD"]

db_conn = psycopg2.connect("host='{}' port={} dbname='{}' user={} password={}".format(host, 
                                                                                           port#, 
                                                                                           "db_name", 
                                                                                           user, 
                                                                                           pass))
query_load = [("SELECT column_name_1, COUNT(*) "
            "FROM schema.table "
            "GROUP BY column_name_1 "
            "ORDER BY column_name_1 ASC"),

             ("SELECT column_name_2, COUNT(*) "
            "FROM schema.table "
            "GROUP BY column_name_2 "
            "ORDER BY column_name_2 ASC"),

             ("SELECT column_name_3, COUNT(*) "
            "FROM schema.table "
            "GROUP BY column_name_3 "
            "ORDER BY column_name_3 ASC"),

             ("SELECT column_name_4, COUNT(*) "
            "FROM schema.table "
            "GROUP BY column_name_4 "
            "ORDER BY column_name_4 ASC"),

            ("SELECT column_name_5, COUNT(*) "
            "FROM schema.table "
            "GROUP BY column_name_5 "
            "ORDER BY column_name_5 ASC")]
start_time = time.time()
data_load = []
for queries in query_load:
    data_load.append(pd.read_sql(queries, db_conn))
elapsed_time = time.time() - start_time
print ("Job finished in {} seconds".format(elapsed_time))

2 个答案:

答案 0 :(得分:1)

由于您已经有了查询集合,因此我们可以组织一个函数一次获取一个查询,但是通过使用Pool.map,它们可以同时运行:

from multiprocessing import Pool
import pandas as pd
import time

# define query_load
# define db_conn

def read_sql(query):
    return pd.read_sql(query, db_conn)

if __name__ == '__main__':
    start_time = time.time()
    with Pool(5) as p:
        data_load = p.map(read_sql, query_load)
    elapsed_time = time.time() - start_time
    print ("Job finished in {} seconds".format(elapsed_time))
    # carry on re-processing data_load

现在,我假设db_conn将允许并发请求。

还请注意,p.map会为您整理结果并将其加载到list中。

答案 1 :(得分:0)

(作为对已接受答案的评论更好,但我对此没有足够的声誉)

来自 psycopg2 文档:https://www.psycopg.org/docs/usage.html#thread-safety

<块引用>

libpq 连接不应该被 fork 进程使用,所以当使用诸如 multiprocessing 的模块或诸如 FastCGI 的 fork web 部署方法时,请确保在 fork 之后创建连接。

因此将连接对象传递给池是不安全的,相反,每个进程都应该创建一个新连接。