我使用MongoDB作为临时日志存储。该系列每小时收到约400,000个新行。每行包含一个UNIX时间戳和一个JSON字符串。
我想定期将集合的内容复制到S3上的文件中,为每小时创建一个包含~400,000行的文件(例如,today_10_11.log包含在上午10点到11点之间收到的所有行)。我需要在集合接收插入时执行此复制。
我的问题:在400,000小时插入的时间戳列上有一个索引对查询一小时行数所花费的额外时间有什么影响。
有问题的应用程序使用在Heroku上运行的Ruby编写并使用MongoHQ插件。
答案 0 :(得分:4)
我有一个类似你的应用程序,目前它有1.5亿条日志记录。每小时400k,这个数据库将快速增长。在时间戳上建立索引的400k插入比使用无索引查询更有价值。使用索引时间戳在一小时内插入数千万条记录没有问题,但如果我在时间戳上执行未索引的查询,则需要在4服务器分片(cpu bound)上花费几分钟。索引查询立即出现。所以绝对索引它,索引的写入开销不是那么高,一小时40万条记录对于mongo来说并不多。
你需要注意的一件事是内存大小。每小时400k记录,你每天要做1000万。这将每天消耗大约350MB的内存以将该索引保留在内存中。因此,如果这种情况持续一段时间,您的索引可能会比内存快得多。
另外,如果你在使用删除一段时间后截断记录,我发现remove会创建大量IO到磁盘并且它是磁盘绑定的。
答案 1 :(得分: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的_ids。所以,通过Ruby驱动程序:
collection.find({:_id => {:$gt => past_id, :$lt => id}}).to_a
=> #... a big array of hashes.
当然,您也可以为时间戳添加一个单独的字段并对其进行索引,但是当Mongo已经使用其默认的_id字段为您完成必要的工作时,没有任何意义。
答案 2 :(得分:1)
当然,在每次写入时,您都需要更新索引数据。如果你要对数据进行大量查询,你肯定会想要一个索引。
考虑将时间戳存储在_id字段而不是MongoDB ObjectId中。只要您存储唯一的时间戳,您就可以在这里找到它。 _id不必是ObjectID,但在_id上有自动索引。这可能是您最好的选择,因为您不会增加额外的索引负担。
答案 3 :(得分:1)
我只是使用一个没有索引的带顶盖的集合,有空间,比如600k行,以允许雪泥。每小时一次,将集合转储到文本文件,然后使用grep过滤掉不是目标日期的行。这不会让你利用数据库的优点,但这意味着你不必担心收集索引,刷新或任何废话。它的性能关键部分是为插入保留集合,因此如果您可以在数据库上下文之外执行“硬”位(按日期过滤),则不应对性能产生任何明显的影响。对于grep来说,400-600k行的文本是微不足道的,并且可能不会超过一两秒。
如果你不介意每个日志中的一些搪瓷,你可以转储和gzip集合。您将在每个转储中获得一些旧数据,但除非您在转储之间插入超过600k的行,否则您应该连续一系列每行600k行的日志快照。