经常看别人说 mongodb 生成几百万几千万条的数据测试。
我想知道怎么弄出来的,有没有现成的列子可以观摩的。
如果跟 Ruby 无关,则用相同的方式直接在 mongo shell 下用 javascript new 一个 obj,然后 db.collname.insert(obj);
十万数据无法读出来了。 = =。
18:00:25 [clientcursormon] mem (MB) res:1057 virt:2828 mapped:1344
18:00:58 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:3470 218ms
18:00:58 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:960 215ms
18:00:58 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:960 234ms
18:01:32 [conn55] info DFM::findAll(): extent 0:4000 was empty, skipping ahead. ns:kennx_blog_app_development.posts
18:01:32 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:3470 222ms
18:01:32 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:960 215ms
18:01:32 [conn55] query kennx_blog_app_development.posts ntoreturn:20 nscanned:100000 scanAndOrder:1 nreturned:20 reslen:960 216ms
第一次访问速度非常慢。如果是一个列表的话,一般都会 find(:all) 吧,这样好像会预先读一次数据库记录,如果有一百万条记录的话,不是会卡死。一个论坛应该没多久就有 10W 条数据了吧。微博这样的东西更加不敢想象。
不可以用 1 樓這種用法,你等於是 new 100 萬個 object 在 memory 裡,這樣會 memory bloat 搞到你的電腦 crash 的
http://stackoverflow.com/questions/5099460/mass-insert-mongoid # 一次塞 batch 而不是 new 100 萬個 object。
而且我另外的建議是拆十萬條塞一次這樣
#10 楼 @xdite 在得出这样的结论之前,你有做过实验吗?
我同意你的观点,放 100 万个对象不是最佳方式。但对于一个简单的如例子中的 mongo object 来说,在 loop 中生成 100 万个并依次插入真能把“你”的电脑弄崩?
我实际跑了一下,代码如下:(用的 ORM 是 mongoid,helper 可以无视,只是用来在每次运行前清理和创建相应的 db 和 collection 的)
require File.expand_path(File.dirname(__FILE__) + '/../helper')
require 'benchmark'
class Monkey
include Mongoid::Document
field :name
end
Benchmark.bm do |bm|
p 'create million monkeys, crash me if you can'
bm.report("1_000_000.times #create") do
1_000_000.times do |i|
Monkey.create(:id => i, :name => "##{i} monkey")
end
end
end
实际运行时间会跟机器环境有关。我的是 Mid 2010 Mac Mini,8G 内存,SSD,10.8 Mountain Lion,Ruby 1.9.3-p0,但测试用的 Mongodb 是存储在 mount 上来的一块 5400 转的普通笔记本硬盘上的。
看插入到 90 万条时的情况:
此时 MongoDB 内存使用到 300M 不到(可以跟 Safari 的使用情况比比,哈哈)。Ruby 进程没显示在上面,此时是 28M 左右且保持稳定。
之所以说同意你的观点,是因为 MongoDB 的内存是会一直增长的,这是 MongoDB 使用内存的方式引起的。Ruby 内存使用很稳定,我个人认为这说明至少从 Ruby 本身来说,在 loop 中创建百万级的对象在现在的计算机环境下根本是小 case!而且有理由相信有可能在 loop 中的对象也会被 GC 掉。
另外,MongoDB 和 Ruby 进程的 CPU 占用在这种情况下都会比较高。
反而,单纯从内存角度来讲,我倒是觉得 batch insert 的方式会比较有问题,一个 Array 中放 100 百万个 object 再去插入,这一百万个 object 在插入完成前是不能被释放掉的。你说的分 10 万一次插倒是很实际有效的方法。
花这么多力气来回你,只是想说,方法各有不同,能达到效果就能。但在说“不可以”前,先想想真的不可以吗?至少我可以!
OK,因为今天要来公司,所以到了后拿 Air 又跑了一遍,一图抵万言:
Ruby 和 MongoDB 环境是一样的,机器是去年的,内存 4G。
这次没忘把结果也记下来了:
user system total real
"create a million monkeys, crash me if you can"
1_000_000.times #create275.970000 14.640000 290.610000 (290.673265)
identity :type => Integer
要用自插入的数字的话,用华顺 (@huacnlee) 写的这个库:
我 Google 了一下,找到这篇文章,也许可以作为参考:http://www.coffeepowered.net/2009/01/23/mass-inserting-data-in-rails-without-killing-your-performance/
#20 楼 @ashchan 嗯,我跑了你的测试,内存使用没有明显增加,但 CPU 保持 50% 占用率一直到测试运行完……我还另外写了一个 batch_insert.rb,作为对照。
BTW: 为什么 benchmark 不显示内存占用??
CPU: Pentium(R) Dual-Core CPU T4200 @ 2.00GHz RAM: DDR2 2G Arch Linux + Ruby 1.9.3-p0
user system total real
"create a million monkeys, crash me if you can"
1_000_000.times #create554.660000 53.490000 608.150000 (641.897088)
user system total real
1_000_000.times #create155.340000 0.480000 155.820000 (177.756519)
关于 benchmark 为何不显示内存占用,请看它的说明:)
The Benchmark module provides methods to measure and report the time used to execute Ruby code.
雖然我覺得這是一個故意要給我難看的帖 (?)
不過很可惜我現在也找不到當時的環境去弄出來了。不知道是不是有人可以幫我找到這樣的環境再作類似的事?
我之前是在 Ruby 1.8.6 + Rails 2.3.1 + ActiveRecord(MySQL) Macbook 1.83 2G + SATA 硬碟上面跑這種 benchmark 的。不用跑到 100 萬。50 萬就爛掉了...
死因就是 memory bloat。不過我想我大概是帶著 ActiveRecord 的印象自動在回。沒看清這題是 MongoDB
mongostat 可以看到 mongo 大概支持 10000 条/s 记录的插入。就是不知道 dump 到磁盘里的数据是多快。
其实 100 万数据上次我做过一个试验。我发现你去分页 100 万数据,skip 掉 90 万,然后取 10 条。非常非常慢。加了 index 也没有用。而且在任何数据库都有这个问题。大数据量的时候分页不知道有没有其他解决方案