为什么说用 MongoDB 存储 Scraped 数据是个坏主意 已翻译 100%

oschina 投递于 2013/05/14 22:53 (共 8 段, 翻译完成于 05-16)
阅读 2190
收藏 3
1
加载中

Scrapinghub在早期就因其便利性采用了MongoDB来存储我们抽取到的数据。我们所抽取到的数据的表示形式为可序列化成JSON的记录(有可能有嵌套)。存储数据所用的schema无法事先确定,可能会随着任务的不同而不同。对于存储好的数据,我们需要支持对其进行浏览、查询和下载。采用MongoDB可以非常容易地实现这个目标(在几年前的当时同其它可选方案相比要容易),随后的一段时间内它的表现也很好。

MongoDB刚开头就是用作一个简单的数据存储,只是在几个项目中存储我们所抽取到的数据,后来它逐步发展为我们的Scrapy云平台的后端数据库。时至今日,我们目前系统架构的局限日渐暴露,因此我们不打算再接着使用MongoDB了,下定决心要换用一种不同的技术(细节会在随后的博文中给出)。我们的许多客户听说我们要抛弃MongoDB了,感到十分惊讶,我希望这篇博文能够帮我们解释清楚为什么MongoDB已再不适合我们了。

fbm
fbm
翻译于 2013/05/15 01:33
1

加锁机制

我们的网站中有大量可迅速完成的查询,其中大部分是来自网页抓取中产生的写入请求。这些查询很少会造成问题,因为它们执行起来速度很快,而且其总体数量也是完全可预测到的。然而,我们还有一些相比数量较少的、运行时间也会比较长一些的查询(比如,数据导出、过滤、批量删除、排序等等)。当有少数的这两种查询在同时运行时,就会造成锁竞争。

每个MongoDB数据库(2.2之前的服务器)都具有一个Readers-Writer锁。由于产生了锁竞争,所有的可迅速完成的查询不得不多等一些时间才能得以执行,而原本执行时间较长的查询执行起来需要的时间长了! 可迅速完成的查询由于等待的时间过长从而引发超时并会在随后再进行重试。对我们网站所发的请求(比如用户在浏览数据)处理起来所需的时间如此之长,以至于我们的Web服务器中所有的工作线程都阻塞于对MongoDB的查询。最终,我们的网站和所有的网页抓取程序就都停止运行了!

为了解决这个问题,我们:

  • 修改了MongoDB的driver,采用指数退避法对操作进行超时处理并对某些特定类型的查询进行重试 
  • 将数据同步到我们新采用的后台存储中并在新存储中运行批量查询操作
  • 建立多个单独的MongoDB数据库,在它们之间建立数据分区
  • 扩充我们的服务器
  • 推迟实现(或者禁止)需要访问大量最新数据的功能
fbm
fbm
翻译于 2013/05/15 02:05
1

存储空间利用率低

MongoDB并不自动收回被删除对象所占的磁盘空间,并且在不造成较长的停机时间的情况下想手动回收这些空间也不可行(因为有锁)。它会在插入新的对象时重新利用这些空间,当这样一来数据的碎片化非常严重。由于有锁的存在,不停机我们也不可能对碎片进行整理。

我们抽取到的数据压缩率一般都比较高,但很不幸的是,MongoDB不具有内置的压缩机制。我们在插入数据前进行压缩也毫无意义,因为每个单个的记录往往都比较小,而且我们还需要对数据进行查询呢。

总是把对象字段的名字也存储起来也会造成空间浪费,特别在某些collection中我们对它们从不进行改变的情况下更是如此。

fbm
fbm
翻译于 2013/05/15 02:15
1

数据库太多

为了便于处理我们运行的MongoDB下建了大量的数据库。每个数据库分配了最小的空间,如果数据库中的数据量比较小,就会造成空间浪费。如果磁盘缓冲区中还没有数据(例如数据库刚重启),那么MongoDB启动起来就要花比较长的时间,这是因为它要对每个数据库进行检查。

有序数据

有些数据(例如网页抓取日志)需要按照它们当初写入的顺序返回给客户端。按顺序检索出数据还需要排序在记录的数据量比较大的情况下就很不现实了。

在MongoDB里只有适用capped collection才能保持数据的顺序,但它又不适用于页面抓取数据的输出。

fbm
fbm
翻译于 2013/05/15 23:29
1

跳跃性和限制性查询运行缓慢

每个抓取作业中要写入的条目数没有限制,要写入百万个条目的作业可不罕见。要从一个抓取作业的中间部分开始读取数据,MongoDB还需要从开头处的索引遍历到指定的偏移处开始读取。对于具有大量数据的作业,后部的数据读起来非常慢。

用户可以通过我们提供的API从分页的查询结果中下载作业数据。对于大型作业(比方说,作业中有百万级的条目),速度会非常慢,有些用户会通过同时发出多个查询来解决该问题,但这无疑会造成服务器负载高起并产生锁竞争。

限制条件

MongoDB有一些奇怪的限制条件,比如在对象的字段名称中只允许这些字符。这样可不好,因为在这方面我们控制不了我们需要保存的字段名。

fbm
fbm
翻译于 2013/05/15 23:47
1

在内存中运行是不现实的

每个节点我们都有 TB 数量级的数据。 那些频繁访问的数据是很小的,当然可以将它保留在内存中。而那些不频繁访问的数据通常是通过顺序抓取得到的数据。 (原文:The infrequently accessed data is often sequentially scanned crawl data.)

MongoDB 并未给我们提供足够的控制如何去存储数据,那么那些频繁访问的数据(以及那些扫描得到的数据)就可能散布在较大的区域之中 。 扫描数据 一次 就无法 回收这些 数据 所占内存的内存空间 一旦 频繁访问的数据 不保存在 内存 MongoDB IO绑定 锁竞争 就成了一个问题
MtrS
MtrS
翻译于 2013/05/15 13:23
1

数据起初是好的,结果却坏了!

在接触MongoDB之后,我们将它应运到了许多地方,包括运行在后台为我们的Diango UI 服务。那些数据本该是整洁的,结构化的, 但是MongoDB 却使得一切变得糟糕。MongoDB本身的局限性如下:

  • 缺乏事务 - 我们常常在一个时间点需要更新一些数据,在失败的情况下(服务器宕机,遇到错误等),仅有一部分数据被提交更新。当然这将导致数据出现不一致的状态。在某些情况下,我们通过打补丁或不断更改代码来修正数据不幸的是忽视问题在MongoDB变得非常普遍提前发现问题就变的稀有次要。一种MongoDB提倡的哲学?原文:a philosophy encouraged by MongoDB
  • 故障无声错误隐藏 - 及早发现问题是好的,出现就让他“崩溃吧去”!  然而,MongoDB 却在隐藏问题(例如,写入不存在的集合)和允许一些毫无防范的编程错误(那个集合存在吗?有我所需字段的索引吗?是我期望的数据类型吗?诸如此类)。
  • 难以理解的安全模式- 开发人员往往不去理解 withoutsafe=Ture ,数据可能不会去写(如,遇到错误),或者发生写入延迟。我们遇到许多问题(如间歇测试失败时,)开发人员希望将他们回读数据,但他们可能会写withsafe = False
  • 缺乏模式和数据约束错误可以毫不引眼地在数据导入数据库时引入。
  • 缺乏连接 (Joins) - 连接是非常有用的但是在MongoDB中将被强迫保持规范化数据而不触发和事务性许多查询加载引起的数据问题
MtrS
MtrS
翻译于 2013/05/15 14:29
1

总结

MongoDB的确有它所适用的场合。许多客户告诉我们说,它们采用MongoDB存储网页抓取数据的效果很不错。在Scrapinghub也有过这么一段时间,但是,对于我们的需求来讲,它不再合适了,在这篇博文里提出的这些问题我们难以克服。

我将在近期的博文中对我们所采用的新式存储系统进行描述,如果你感兴趣的话,请关注 @scrapinghub

fbm
fbm
翻译于 2013/05/15 02:29
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(1)

zhantan
zhantan
后来他们改用HBase了
返回顶部
顶部