将Mongo中的数据转换为MySQL(110M个文档,60G)- 有什么提示和建议?

10
我被委托将数据从MongoDB数据库移植到MySQL数据库(因为有很强的理由需要这样做)。MongoDB集合包含大约1.1亿个文档,大小为60 GB,并且具有重要属性的索引。我们已经尝试使用具有7.5 GB RAM / 8 GB页面文件的大型Amazon EC2 Win2008服务器实例和C#控制台应用程序将MongoDB数据转换为本地MySQL数据库。我们每次从MongoDB内存中取出1K个文档,进行必要的处理,然后进行批处理写入MySQL db,每次写入500个。但是,每处理250万个文档就会导致服务器崩溃,Mongo响应非常缓慢,卡住我们的应用程序。我们已经尝试通过定期杀死mongod进程重新启动来解决这个问题,但我们认为我们肯定做错了什么。我们是否应该将Mongo服务器迁移到基于Linux的大型实例上,并将MySQL迁移到Amazon RDS上,并改用PHP重写转换应用程序?还有哪些其他事情可以尝试或提示?更新后,我们增加了Mongo Read计数器,从1,000个记录增加到10,000个记录,删除了MySQL目标数据库中的所有索引,并将Windows页面大小从4 GB增加到8 GB。处理的记录数已经达到了11M,但速度已降至每秒370条记录,下一步是将Mongo和MySQL服务器隔离到单独的服务器,并将它们全部放置在相同的Amazon可用性区域中以最小化延迟。我们更改了代码以使用Mongo游标并使其自动递增,而不是手动执行.skip().limit()。这大大加快了转换过程,但应用程序开始消耗太多内存,并且需要在每处理200万个记录后重新启动。
var docs = db[collectionName].Find(query);
docs.SetBatchSize(numOfResultsToFetchAtATime);
foreach (var d in docs) {
  // do processing
}
所以这个代码会一次获取'numOfResultsToFetchAtATime'条记录-然后在循环中自动进行并获取下一组记录。Mongo使用游标来处理这个进程,因此速度更快。 但是,我们仍然无法成功移植它。当成功移植时,将发布我的回复和代码。 -- 更新03:成功 -- 最终,我们采用了@scarpacci的建议,使用mongoexport。请记住,必须将mongodb放在linux盒子上而不是windows盒子上。 我们首先尝试从Windows本地MongoDB进行mongoexport,无论我们尝试什么,它都会在一个大集合(13Gigs +)的不同位置失败。 最后,我将数据库还原到Linux盒子上,mongoexport就像魔术般地工作了。 没有Json -> MySQL转换器-所以我们必须做到这一点。经过一些微调,我们能够使用我们以前的应用程序读取文件并直接写入MySQL。它很快且相对无错误。 我们遇到了一些大文件的问题,但将13GB文件分成500兆长的文件有所帮助,我们能够成功地将所有数据迁移到MySQL。 感谢大家花费时间帮助我们。希望这个解释能帮助将来的某个人。

2
我认为将应用程序和MySQL移动到另一台服务器可能会有所帮助。Mongo喜欢独处,因此它可以消耗所有可用的RAM。如果在迁移中没有使用索引,您可以考虑删除它们。或者,您可以尝试配置MySQL具有非常低的最大RAM,并确保C#应用程序不会增加其内存使用量。 - Eve Freeman
你为什么要转换到MySql?只是好奇...想知道使用MongoDB的原因/问题...谢谢--S - scarpacci
3
我很久没有使用过MongoDB,使用MySQL也已经有好几年了。不过,我认为问题出在你的控制台应用程序上。之前我曾做过类似的C#控制台应用程序,查询记录数量比较小(数千条而非数百万条)。我注意到每次获取数据时控制台应用程序的内存都会增长。当时我并不在意,因为任务完成后内存就会得到释放。但你可能需要重新编写控制台应用程序,并务必确保在执行任务时清理其内存使用情况。 - Gup3rSuR4c
@saurabhj 我有什么遗漏吗?从Mongo回到RDBMS? 那么为什么MongoDB如此受欢迎?它被误用了还是对于特定的场景来说,MySQL更好?水平扩展呢? - NeverEndingQueue
@NeverEndingQueue 基本上我们使用了错误的Mongo数据库。需要进行分析并且一些人需要直接在DB中运行SQL查询。以前的团队在Mongo中实现解决方案时没有意识到DB/项目会变得如此庞大。因此,这对于我们来说只是一个糟糕的用例。我们最终将数据逻辑地拆分成不同的表格 - 这使得可以使用SQL快速查询。希望这能有所帮助。 - saurabhj
显示剩余3条评论
5个回答

3
我曾经使用.NET迁移数据到SQLServer时遇到了问题 - 尽管我尽可能保持轻量级,但速度仍然不够快。最终,我写了一个快速的C++ OLEDB应用程序,速度显著提高。我仍在努力弄清楚我的.NET应用程序出了什么问题,但问题可能出在.NET上。我不会重新用PHP编写转换,而是选择性能选项并使用C++(从网上获取教程,这并不难,对于一个一次性应用程序来说)。
所以,首先要看的就是这个 - 还有你的C#应用程序是否存在内存泄漏错误,这会逐渐使系统变得缓慢。
我觉得你停止MongoDB应用程序而不是其他任何东西很有趣。是什么让你认为它是MongoDB正在崩溃,而不是其他系统?如果是内存使用情况,则将其拆分到单独的盒子中可能会有所改善;如果内存逐渐增长,则读取更少的块 - Mongo应该可以很好地读取数据,因此如果它不能,那么很可能是您做了某些事情使其保持内存,无论是在配置还是在应用程序中。
启动perfmon并查看Mongo、您的应用程序和MySQL实例的内存、交换、磁盘IO和CPU使用情况。

是的,我有一种感觉,如果应用程序或mysql被杀死,它可能会继续运行一段时间。了解不同进程的内存配置文件的更多信息会很有帮助。 - Eve Freeman
1
我同意,使用托管代码执行此类迁移可能会(很可能)消耗比实际所需更多的资源。C++绝对是更好的选择。 - Gup3rSuR4c
感谢您的见解。我之前在同一配置的Linux系统上编写了一些PHP代码 - Mongo表现出了类似的行为。当我获取数据时,最初的30万条记录左右,每秒处理约1000条记录,然后开始呈指数级减速。在Windows上,我选择重新启动Mongo,因为mongod进程从启动时使用350兆字节的RAM增长到6.5吉字节,在6小时内。 (.Net应用程序目前正在使用400兆字节)。将在尝试新东西时发布更多更新... 将尝试并尝试C++选项。 - saurabhj
@saurabhj:6.5 GB的RAM,应该够用了 :) 现在,我知道MongoDB在许多领域中都被使用,如果它在几千次读取后停止工作,那么肯定会有人注意到...所以我现在怀疑是你的应用程序,在Mongo中是否保留了应该释放的内容?也许你应该将这个问题发送给10gen,他们的支持非常出色。 - gbjbaanb
如果MongoDB没有竞争资源,它会这样做,这是由于内存映射文件的特性。问题在于(根据我的经验),当它不需要竞争资源时,突然需要竞争时 - 这是操作系统释放文件缓存以便其他进程使用的工作。你的MongoDB索引有多大? - Eve Freeman

3

我曾经迁移过一个大型数据库(不是60GB,但足够大以显示问题),最终编写了一个小应用程序来完成任务。

这样,我从一个数据库中读取数据并写入另一个数据库,采用某种批处理模式(我遇到了类似的数据库崩溃等问题)。

我的做法是为每个部分生成较小的事务,并在解决一个工作项后关闭它们。

我们在两个数据库中都有表格,没有文档,但问题是相同的。

总之:

  • 有一个协调迁移的应用程序,但本身没有任何数据库连接
  • 从协调应用程序产生多个实例来移动数据,完成工作项后关闭(在关闭之前有一种报告成功的方法)。这样你可以拥有多个读者/写者并且可以尝试计数,我一次只有大约10个并发读者/写者实例。如果你的文档足够小,则可以产生更多。但它们将非常快速地关闭。

注意:在写入目标数据库时不要设置索引,这将给您带来终极性能提升。当您将所有数据放入其中时再设置索引。


3

你没有必要直接将数据写入MySQL - 将任务分成两个独立的阶段,先运行MongoDB再运行MySQL,这样它们就不会争夺资源了 - 看起来MySQL进程正在占用RAM或IO资源导致MongoDB受到限制。

第一阶段:从MongoDB获取数据,处理并将其保存为文本文件(作为SQL)。停止MongoDB,启动MySQL。

第二阶段:使用在第一阶段生成的文件运行常规数据库导入。


2
最好的方法是使用mongoexport从Mongo中导出数据,然后使用某种方式将其加载到MySQL中,并编写一个程序将JSON文件转换为SQL插入语句文件。 - Asya Kamsky
是的。将Mongo转换为json文本文件,然后再转换为MySQL是我最后一个也是最终的选择,我相信这种方法会奏效。但我认为这需要很长时间。如果其他方法都不行,我肯定会采用这种方法。感谢您验证我的思路。 :) - saurabhj
1
这就是你的误解所在。这样会更快。由于MySQL和Mongo竞争资源(特别是IO),实际性能将大幅下降。如果同时运行两者,不要期望它们各自以约50%的速度运行。5-10%更为现实。通过先在MySQL上运行操作,然后在Mongo上运行,您可能会使其快2-3倍。我已经吃过亏了。 - c2h5oh

2
重新启动MongoDB可以解决性能问题,而且在出现问题之前可以处理的记录数量是一致的,这表明可能存在资源泄漏。建议确认是否关闭了所有内容。确保MySQL没有配置过多内存使用,或者最好将其移至另一台机器上。

MySQL目前运行6小时,表现非常不错,只占用450兆内存。相比之下,Mongo却占用了6.78吉内存。我猜在Windows上运行Mongo可能不是一个好主意,所以下一步可能是将其运行在一个独立的Linux机器上。感谢您的反馈! - saurabhj
在重新启动MongoDB之前,您可以查看内存和交换情况。内存使用量增加和磁盘交换是资源泄漏的症状。 - Joshua Martell

1

您是否考虑过使用mongoexport,然后执行某种批量插入到MySQL中?不确定MySQL是否支持此功能,但我们在SQL Server中经常执行类似操作。这可能会使转储/插入更容易分解和优化。只是一个想法...


感谢您的评论。我的最后选择是将数据导出为JSON格式,然后通过读取JSON文件尝试将其推入MySQL数据库中。但是,导出60GB的文本数据并处理这么多文件会带来自己的后勤挑战。因此,我已将此作为最后的资源。 - saurabhj
这最终对我们起作用了 - 我们尝试过的其他方法要么太有bug,要么花费了很多时间。我们最终进行了mongoexport并编写了一个应用程序来读取文件并更新MySQL,这确实起作用了 - 而且非常快! - saurabhj

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