通过PHP优化MySQL InnoDB插入

时间:2015-06-12 13:38:36

标签: php mysql optimization query-optimization innodb

我有一个Cronjob脚本,用PHP编写,具有以下要求:

  • 第1步(数据库服务器1):从多个表中获取一些数据(我们这里有大量数据)
  • 第2步(应用程序服务器):执行一些计算
  • 步骤3(数据库服务器2):计算完成后,将该数据插入另一个数据库(MySQL)/表(InnoDB)中以进行报告。该表包含97列,实际上是不同的速率,无法进一步规范化。这是不同的物理数据库服务器,只有一个数据库。

脚本在开发过程中运行良好但在生产过程中,步骤1返回了大约5000万条记录。结果,很明显,脚本运行大约4天然后失败。 (粗略估计,以当前的速度,完成需要大约171天)

请注意,我们使用预备语句,第1步是一次获取1000条记录中的数据。

到目前为止我们做了什么

优化步骤1:插入&中的多个值。删除所有索引

一些测试显示插入(上面的步骤3)占用了最长时间(超过95%的时间)。为了优化,在一些谷歌搜索之后,我们从表中删除了所有索引,而不是一个插入查询/行,我们没有一个插入查询/ 100行。这使我们的插入速度提高了一些,但是根据粗略估计,运行cron一次需要90天,我们需要每月运行一次,因为每个月都有新的数据。

优化步骤2,而不是写入DB,写入csv文件,然后使用linux命令在mysql中导入。

此步骤似乎无效。在CSV文件中写入30000行需要16分钟,我们仍然需要在MySQL中导入该CSV文件。我们为所有写操作都有单个文件处理程序。

当前状态

看来我现在还无能为力。一些关键要求:

  • 脚本需要插入大约50,000,000条记录(会随着时间的推移而增加)
  • 每条记录有97列,我们可以至少跳过85列。
  • 根据输入,我们可以将脚本分成三个不同的cron,在三个不同的服务器上运行,但必须在一个数据库服务器(主服务器)上进行插入,因此不确定它是否有用。

然而:

  • 我们愿意更改数据库/存储引擎(包括NoSQL)
  • 在生产中,我们可以有多个数据库服务器,但插入只能在master上完成。所有读取操作都可以定向到奴隶,这是最小的和偶尔的(只是为了生成报告)

问题

我不需要任何描述性答案,但有人可以简单地提出可能的解决方案。我只需要一些优化提示,我会留下R& D.

我们对一切开放,更改数据库/存储引擎,服务器优化/多个服务器(数据库和应用程序),更改编程语言或满足上述要求的最佳配置。

最终期望,cron必须在最多24小时内完成。

在优化步骤2中编辑

为了进一步理解为什么生成csv花费时间,我已经创建了我的代码的副本,只有必要的代码。该代码出现在git https://github.com/kapilsharma/xz

实验的输出文件是https://github.com/kapilsharma/xz/blob/master/csv/output500000_batch5000.txt

如果你检查上面的文件,我一次插入500000条记录并从数据库中获取5000条记录,使循环运行100次。第一次循环所用的时间是0.25982284545898秒,但是在第100次循环中是3.9140808582306。我假设它是因为系统资源和/或csv文件的文件大小。在这种情况下,它变得更多的编程问题然后DB优化。不过,有人可以建议为什么在下一个循环中花费更多时间吗?

如果需要,除了生成csv文件和sql文件以创建虚拟DB之外,整个代码都会被提交,因为这些文件非常大。但是,可以使用代码轻松生成它们。

3 个答案:

答案 0 :(得分:2)

我在CakePHP上有一个邮件程序cron作业,它只在600行获取并向注册用户发送电子邮件失败。它甚至不能在批处理操作中执行任务。我们最终选择mandrill,从那时起一切顺利。

我建议(考虑在生产中触摸遗留系统是一个坏主意):

  • 考虑在golang或node.js中安排一个mirco解决方案 performance benchmarks,涉及数据库交互 - 你对这些中的任何一个都没问题。有这个微解决方案执行 cron的工作。 (获取+计算)
  • 来自NoSQL的报道将是 挑战,所以你应该尝试使用像这样的可用服务 Google Big Query。让cron工作存储数据谷歌大 查询,你甚至可以获得巨大的性能提升 生成报告。

  • 将每行插入到原始数据库服务器1中,设置一种消息传递机制,每次插入时执行cron作业操作(触发器类型)并将其存储到报告服务器中。您可以使用的可能服务包括:Google PubSubPusher。我认为每插入时间消耗量会相当少。 (您还可以使用异步服务设置来完成存储到报告数据库中的任务。)

希望这有帮助。

答案 1 :(得分:1)

使用OFFSETLIMIT遍历表格是O(N * N),这比你想要或预期的要慢得多。

相反,走过桌子“记住你离开的地方”。最好使用PRIMARY KEY。由于id看起来像AUTO_INCREMENT没有间隙,因此代码很简单。 My blog讨论了(以及更复杂的分块技术)。

速度不会快100(500K / 5K),但速度会明显加快。

答案 2 :(得分:0)

这是一个非常广泛的问题。我首先要弄清楚“插入”语句的瓶颈是什么。运行代码,并使用操作系统提供的任何内容来查看机器正在执行的操作。

如果瓶颈是CPU,您需要找到最慢的部分并加快速度。不太可能,给出了您的示例代码,但可能。

如果瓶颈是I / O或内存,那么你几乎肯定需要更好的硬件或基本的重新设计。

重新设计它的显而易见的方法是找到一种方法来处理50M记录中的增量。例如,如果您可以在记录更改时写入审计表,则您的cron作业可以查看该审计表并挑选自上次批处理运行以来修改过的任何数据。