导入大数据集的性能优化技巧

4
我有一个功能,允许用户导入联系人(电子邮件地址和姓名)。一些用户导入大约70,000个联系人的文件,可以是xls或csv格式。这是我现在所拥有的:
  1. 用户选择要从中导入联系人的文件(来自他们的计算机)。
  2. 我将文件保存在服务器上,并创建一个带有文件位置引用的数据库条目。
  3. 使用Amazon SQS在后台处理此数据。
  4. 第一次运行作业时,我会处理文件,仅保存具有电子邮件地址和姓名(如果找到)的行。该数据将保存到同一位置的json文件中并进行缓存。然后我将任务释放回队列。
  5. 现在联系人已准备好导入。我每次处理1000个联系人,并将每个联系人保存在数据库中的单独行中。我使用array_slice跳过JSON文件中的联系人。跳过计数保存到数据库中。
  6. 没有联系人时,删除任务并完成全部操作。
这基本上就是整个过程。我还有一个检查(数据库查询),用于检查重复项。只允许唯一的电子邮件地址。
我遇到的问题是作业似乎需要太长时间,而且我遇到了超时。这导致导入花费了太多时间。
因此,我的问题是:我能做得更好吗?
如果需要其他信息,请告诉我。我没有处理大数据和许多用户的经验。
编辑:我不需要代码。我想知道的是,问题是否是服务器问题?也许将数据库移动到自己的服务器上会起作用?还是应该采用不同的方法?
编辑2:用户可以查看导入的进度。因此,我需要计算联系人的数量,为此,我需要首先过滤掉没有电子邮件地址的行。我还要修剪它以及名称列。当我这样做时,我发现将新数据集保存到JSON文件中更容易。
编辑3:超时是在将用户保存到数据库中时发生的,而不是在初始处理和创建json文件时发生的。
编辑4:加速作业的一种方法可能是从一开始就将其保存到块中(在第一个处理中)。这样,我就不需要处理跳过计数器,也不必对大型数据集使用array_slice。现在回想起来,将其保存到JSON文件中然后缓存似乎有点愚蠢。为什么不从一开始就缓存数组呢?

1
为什么要创建JSON文件,而不是直接从CSV文件到数据库? - user557846
我不会直接告诉用户程序正在运行,而是会在后台运行并在完成后通过电子邮件通知他们,以避免超时。 - user557846
@tbleckert 你为什么不使用临时表来过滤掉没有电子邮件地址的行?然后将临时表用于真实表中?这样你还可以向用户展示进度... - Eko Junaidi Salam
在我看来,你的排队方法还不错。超时主要是由代码引起的,因此你确实需要改进代码。 - ihsan
是的,我能看出来,在一个非常大的数组上使用array_slice,在循环1000行并在每次迭代中执行两个数据库查询(总共2000次操作)可能会导致性能问题。呵呵...但现在我还不知道如何改进它。 - tbleckert
显示剩余3条评论
2个回答

2
每个任务中我会处理1000个联系人,并将每个联系人保存在数据库中的单独一行。我曾经也遇到过这样的问题,但我的问题是需要导入50000条员工出勤记录,我使用并行化解决了这个问题。你可能也注意到了这一点,所以你将每个任务队列中处理1000个联系人。真正的问题是如果我们处理得太多,就会遇到“进程超时”的问题。
因此,我的解决方案是创建更多的子进程来执行任务。如果我创建一个任务来导入1000个记录,它将花费更多时间并且速度较慢。因此,我创建了100个任务队列,每个任务导入100条记录。然后一起运行。这种方法会增加CPU负载,如果你有高规格的电脑,这不是问题。
我的建议是:
  1. 创建更多的作业队列来执行导入操作。
  2. 避免使用过多的循环。
  3. 如果可能的话,将数据存储在memcached中,因为它会加速您的处理过程。我猜您也这么认为。了解APC的相关知识。

您可以在此处阅读有关如何将数据存储在内存中的信息。希望这对您有所帮助 :)


1
是的,这听起来像是正确的方法(连同一些代码优化)。我可能还会将数据库移动到自己的服务器上,并在不同的服务器上运行队列作业。因此总共有3个服务器(Web服务器是第三个)。另外,我目前使用Redis进行缓存,Memcached更好吗? - tbleckert
是的,当然可以。你也可以在不同的服务器上运行队列作业。这将使您的导入速度更快 :) - Eko Junaidi Salam

0

你的 PHP 程序是否在等待这个任务完成?那样是行不通的,因为它会超时。你已经注意到了。

你需要以这样的方式组织你的操作,让你的 PHP 程序在 AWS SQS 上启动作业,然后告诉用户作业正在运行,并且将在一段时间后完成。设置低用户期望值(“15 分钟内完成”),然后超过它们(5 分钟),而不是相反。

然后你需要一个单独的操作来查询作业的状态,看看它是否完成。你可以通过安排作业在完成时更新表中的一行来实现这一点。


超时是在后台发生的。用户将这些联系人导入到特定的联系人列表中。当这种情况发生时,我会将此列表的状态更改为“正在导入”。因此,用户永远不会遇到超时问题。发生的情况是,当作业完成后,我将其释放回队列。因此,当它下次运行时,它会继续进行。我这样做是为了防止超时。但有时仍然会发生超时。如果我将其更改为每个作业仅导入100个联系人,则可以正常工作。 - tbleckert
同时,每当完成一个任务时,我会更新联系人列表中导入的联系人数量。因此,用户会看到类似于“还有15,000个联系人待导入”的信息。 - tbleckert

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