使用PostgreSQL进行Python多线程处理

4

我有一个非常基本的应用程序,通过套接字连接一次下载一个记录从服务器上获取数据,并将每个记录写入postgresql数据库。这只是我自己使用的。

downloader = DataDownloader.Downloader()
con = psycopg2.connect(datebase='db', user='username', password='pswrd')

downloader.get_data(con)
con.close()

在上面的代码中,我将psycopg2连接传递给一个类的实例,然后该类执行所有的下载和数据库更新操作。
问题在于,由于在get_data()中,该类每次只下载一条记录,因此该过程非常缓慢。我已经开始改变这种情况以包含线程:
downloader = DataDownloader.Downloader()

records_to_download = 'abc'
thread1 = threading.Thread(target=downloader.get_data, args=(records_to_download))
thread1.start()

records_to_download = 'xyz'
thread2 = threading.Thread(target=downloader.get_data, args=(records_to_download))
thread2.start()

records_to_download = 'ghj'
thread3 = threading.Thread(target=downloader.get_data, args=(records_to_download))
thread3.start()

我所做的只是再次创建了一个单一的downloader实例,然后创建了3个线程,每个线程通过传递指定要下载的记录的参数来运行get_data
我没有创建psycopg2连接并将其传递给get_data,而是在get_data中创建了连接,以便每个线程都有自己的连接。
我的问题是,这样是否足够并且正确地确保所有线程同时与数据库“友好”交互?每个线程仍在使用相同的用户名/密码访问数据库。我已确保每个线程将通过传递records_to_download参数来更新不同的记录。
我听说过连接池,但对于像这样的单用户、多线程设置,这是否过度?(我从未使用过连接池,我的数据库知识非常基础,...因此如果这不是必需的,我会避免使用它)。
最后,我可以在被迫使用更为强大的方法(如连接池)之前使用多少线程?或者连接池是否从一开始就是必需的?
1个回答

6
这样做足够并且正确,可以确保所有线程同时与数据库“友好”吗?
的确,这是实现并行性最简单的方法。
(请注意,PostgreSQL实际上支持通过单个连接进行并行处理,但据我所知,它仅在C驱动程序中方便地提供。还有executemany和使用单个INSERT语句插入多行的技巧(参见此问题)。)
现在你已经通过多个连接实现了高效的并行处理,你应该检查服务器端的瓶颈。特别是,以超耐久的方式将事务提交到磁盘可能会使更新变慢,因为它们正在等待操作系统和存储设备报告有效载荷的成功到达。
为此,您可能需要通过在用于批量更新的每个连接中发出SET synchronous_commit TO off来放松耐久性。(参见synchronous_committhis question)。
或者,您可以将更新分组到大型事务中,延迟持久性到磁盘问题直到事务提交。
引用块:
我听说过连接池,但对于这种单用户、多线程设置,这是否过度了?
连接池主要用于减轻建立和拆除连接的成本。在某些设置中,它还可以减少数据库需要维护的连接数。
考虑以下用例:1)用户访问服务器页面;2)服务器建立数据库连接;3)服务器通过查询数据库检查用户是否存在;4)服务器拆除连接;5)服务器向用户发送页面。
在这种重复的任务中,通常会使用某种类型的连接池来优化第二和第四个步骤。
另一个用例:1)用户访问服务器页面;2)服务器进程建立并缓存数据库连接;3)服务器通过查询数据库检查用户是否存在;4)服务器将页面发送给用户。
这里每个服务器进程都保留其自己的数据库连接,以避免每次请求时建立新连接。因此,如果有200个正在运行的PHP进程,则有200个数据库连接。这需要从数据库服务器中消耗RAM以维护连接。可以使用连接池来实现类似的性能,而无需维护200个连接。
在您的情况下,我看不到使用连接池的必要性。同时拥有自己的连接允许您进一步调整它们的synchronous_commitwork_mem等设置,但当使用连接池时不建议这样做,因为通过将调整后的连接留在池中,您可能会以不可控的方式影响其他池用户的持久性和性能。
最后,对于这种设置,我可以使用多少线程,才需要使用更强大的方法,例如连接池?或者从一开始就需要连接池呢?
多线程并不是万能的解决方案。您只需要足够的线程和连接来对抗驱动程序与数据库通信的串行性所带来的瓶颈(实际上,如果您使用C驱动程序的异步流水线版本,则可能根本不需要线程),并达到由数据库服务器配置和硬件所强加的性能平台。将比所需线程更多的线程投入问题中只会使系统变得混乱,使其工作速度更慢(参见Reasons for Performance Reduction Past the "Knee")。
测试和测量以确定您的用例的最佳线程和连接数量。
性能是您的限制,过多地投入线程会影响性能。
至于连接池,您只需要在特定的用例中使用它。

感谢你提供详细的回答,非常有帮助。 - darkpool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接