4

我使用 MongoDB 作为临时日志存储。该集合每小时接收约 400,000 行新行。每行包含一个 UNIX 时间戳和一个 JSON 字符串。

我想定期将集合的内容复制到 S3 上的文件中,每小时创建一个包含约 400,000 行的文件(例如,today_10_11.log 包含上午 10 点到 11 点之间收到的所有行)。我需要在集合接收插入时进行此复制。

我的问题:在每小时插入 400,000 次的时间戳列上建立索引与查询一个小时的行所需的额外时间相比,对性能有何影响。

有问题的应用程序使用在 Heroku 上运行的 Ruby 编写并使用 MongoHQ 插件。

4

4 回答 4

4

我有一个像你这样的应用程序,目前它有 1.5 亿条日志记录。以每小时 400k 的速度,这个数据库会很快变大。每小时插入 400k 次并在时间戳上建立索引比进行未索引查询更有价值。我可以在一小时内插入数以千万计的带有索引时间戳的记录,但是如果我对时间戳进行未索引查询,则在 4 个服务器分片(cpu 绑定)上需要几分钟。索引查询立即出现。所以一定要索引它,索引的写入开销并不高,每小时 40 万条记录对于 mongo 来说并不多。

您必须注意的一件事是内存大小。如果每小时记录 400k 条记录,那么您每天要记录 1000 万条记录。每天将消耗大约 350MB 的内存来将该索引保存在内存中。因此,如果这种情况持续一段时间,您的索引可能会快速大于内存。

此外,如果您在一段时间后使用 remove 截断记录,我发现删除会为磁盘创建大量 IO,并且它是磁盘绑定的。

于 2011-02-09T02:09:30.763 回答
4

Mongo 默认索引 _id 字段,并且 ObjectId 已经以时间戳开头,所以基本上,Mongo 已经为您按插入时间索引您的集合。因此,如果您使用 Mongo 默认值,则不需要索引第二个时间戳字段(甚至添加一个)。

在 ruby​​ 中获取对象 id 的创建时间:

ruby-1.9.2-p136 :001 > id = BSON::ObjectId.new
 => BSON::ObjectId('4d5205ed0de0696c7b000001') 
ruby-1.9.2-p136 :002 > id.generation_time
 => 2011-02-09 03:11:41 UTC 

要为给定时间生成对象 ID:

ruby-1.9.2-p136 :003 > past_id = BSON::ObjectId.from_time(1.week.ago)
 => BSON::ObjectId('4d48cb970000000000000000') 

因此,例如,如果您想加载过去一周插入的所有文档,您只需搜索大于 past_id 且小于 id 的 _id。因此,通过 Ruby 驱动程序:

collection.find({:_id => {:$gt => past_id, :$lt => id}}).to_a
 => #... a big array of hashes.

当然,您也可以为时间戳添加一个单独的字段并将其编入索引,但是当 Mongo 已经使用其默认 _id 字段为您完成了必要的工作时,没有必要对性能造成影响。

有关对象 ID 的更多信息。

于 2011-02-09T03:17:21.723 回答
1

当然,在每次写入时,您都需要更新索引数据。如果您要对数据进行大型查询,您肯定需要索引。

考虑将时间戳存储在 _id 字段而不是 MongoDB ObjectId 中。只要您存储唯一的时间戳,您就可以在这里。_id 不必是 ObjectID,但在 _id 上有一个自动索引。这可能是您最好的选择,因为您不会增加额外的索引负担。

于 2011-02-09T00:31:18.777 回答
1

我只是使用一个封顶的集合,没有索引,有空间,比如 600k 行,以允许雪泥。每小时一次,将集合转储到文本文件,然后使用 grep 过滤掉不是来自目标日期的行。这不会让您利用 DB 的优点,但这意味着您不必担心集合索引、刷新或任何废话。它的性能关键位是保持集合免费插入,因此如果您可以在数据库上下文之外执行“硬”位(按日期过滤),那么您不应该有任何明显的性能影响。400-600k 行文本对于 grep 来说是微不足道的,而且可能不会超过一两秒。

如果您不介意在每个日志中添加一些内容,则可以转储并 gzip 集合。您将在每个转储中获得一些较旧的数据,但除非您在转储之间插入超过 600k 行,否则您应该有一系列连续的 600k 行日志快照。

于 2011-02-09T00:40:29.440 回答