【问题标题】:What is an efficient way of handling millions of records and saving back to database?处理数百万条记录并保存回数据库的有效方法是什么?
【发布时间】:2018-05-02 16:50:56
【问题描述】:

我正在使用带有 python 2.7 的 Django。我有一个包含数百万行的 excel 表。我必须操作行数据并保存回数据库(postgresql)。我想有效地做到这一点。 以下是我正在考虑的方法:

1.) 将队列中的所有行(数据)排入队列(最好是 RabbitMQ),并一次获取 100 个条目。并将执行和 将其保存在数据库中。

2.) 考虑在后台使用线程,每个线程将管理 100 行并将结果保存回数据库。我不是 确定在这种情况下将打开多少个数据库连接。

您能否建议我一种有效的方法来实现这一目标。这真的很有帮助。

【问题讨论】:

  • 你不能将excel保存为.csv并使用COPY命令吗?
  • 我必须对每一行存在的数据进行操作,然后在操作后我必须将其保存到数据库中

标签: django multithreading postgresql


【解决方案1】:

您可以在 Django 请求处理程序中创建额外的线程,每个线程都有自己的数据库连接。但是问题就变成了,你的数据库插入性能会提高多少?

例如,如果您的表具有唯一约束,这可能会减慢并发写入速度。然后您可能会发现您真正的瓶颈是磁盘带宽,并且您将无法通过添加连接来实现很大程度的扩展。

因此,您可能需要编写一些快速而简单的测试代码,以在优化过度之前尝试多线程并发写入相关表/数据库。

至于如何在线程之间有效地分配数据,这取决于输入格式。

如果您正在处理 Microsoft 格式文件,例如 .xls,那么您将需要使用库来解析它。我已经成功使用了 xlrd。但这会导致您一次将所有电子表格数据都保存在内存中。您不能一次读取一行。但是假设您可以处理内存使用情况,它会使您的线程变得简单:一旦您将所有数据读入内存,启动多个写入线程,告诉每个线程它负责写入的行号范围。然后主请求线程可以加入编写线程,当它们都完成时,它可以向用户返回一个响应。但是,请记住,如果您的请求时间过长,浏览器将超时。这是一个不同的问题,为此我建议查看我前几天写的关于如何使用StreamingHttpResponse 的另一个答案:

Right way to delay file download in Django

现在,如果您的输入格式类似于 .csv 文件,您可以一次读取一条记录,处理此问题的一种可能方法是使用 python Queue 类创建一个内存队列( https://docs.python.org/2/library/queue.html)。启动您的编写器线程并让它们在队列中侦听要写入数据库的记录,然后让您的主线程从.csv 文件中一次读取一条记录并将这些记录放入队列中。

这些建议都是针对在 Django 请求中处理您的数据的。但是,如果您不想这样做,是的,您可以通过各种方式卸载处理。正如您所提到的,您当然可以使用rabbitmq,并让多个侦听器进程进行编写。这会起作用,但我不确定它是否会达到最佳效率。您需要编写所有记录,因此将它们分解,将它们运送到另一个进程,然后将它们从那里运送到另一个进程……除非这些其他进程在其他机器上运行,否则不一定有帮助。

如果您要从已写入磁盘的文件中读取数据,并且它是一种易于分割的文件格式(同样,例如 CSV),那么一种简单(且非常经典)的方法是将文件大小除以您拥有的编写器线程数。告诉每个作者您希望它处理的文件的开始和结束偏移量。每个写入器(除了从偏移量 0 开始的写入器)都可以向前搜索,直到找到记录分隔符(例如,\r\n)。然后它开始一次读取和处理一个输入记录,直到它读取到等于或超过其结束偏移量的位置。

如果您正在从请求中读取文件,并且想要在请求处理程序之外对其进行处理,您可能会发现将其作为一个大文件写入磁盘更有效,然后处理它之后如上所述。

总而言之,尝试找到处理涉及最少读写次数的数据的方法。如果可以,就地处理数据,如果不需要,不要移动它。如果它们在同一台机器上,请避免写入其他进程,因为手头的进程是磁盘密集型的。如果您确实希望能够轻松扩展到更多机器,那么当然可以尝试rabbitmq。我用它,它很好,而且速度很快。但它会增加开销,所以除非你从分解中获得一些真正的好处,否则它可能会减慢你的速度。不过,它确实使进程间通信变得超级简单。

【讨论】:

  • 在这种情况下实现取消和暂停实现的最佳方法是什么。 ?就像如果用户取消它,那么其余的线程或进程应该被取消
  • 这真的取决于你如何实现你的网页,如果它是一个没有 JavaScript 的简单表单帖子,那么用户可以在上传期间在浏览器上点击停止,但如果你想处理请求停止它,您必须向 a 发送单独的请求以终止线程..您可以在会话中存储有关信息...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 2017-03-15
  • 2012-02-21
相关资源
最近更新 更多