测试环境:
型号名称: iMac
处理器名称: Intel Core 2 Duo
处理器速度: 3.06 GHz
处理器数量: 1
总核心数: 2
L2 高速缓存: 3 MB
内存: 4 GB
总线速度: 1.07 GHz
测试方案:
用js脚本创建向一个collection里插入一百万个文档,测量mongo占用内存,硬盘数据文件大小(优化数据库设计),插入时间,在一百万条基础上做find操作。
内存:
起动mongod之后,可用内存是2.65G. 创建完1百万条记录后,可用内存为1.34G. 说明在创建过程中审请过约1.3G内存,用完了其它程序可接着使用。
索引内存:300M,容易记的是100万个文档,3个索引字段(加系统_id),300M。
> db.process.totalIndexSize()
302638016
#导入100万个文档
db.serverStatus().mem
{
"bits" : 64,
"resident" : 530,
"virtual" : 6386,
"supported" : true,
"mapped" : 3952
}
#未做查询
"mem" : {
"bits" : 64,
"resident" : 4,
"virtual" : 2399,
"supported" : true,
"mapped" : 0
},
#做完查询
"mem" : {
"bits" : 64,
"resident" : 20,
"virtual" : 3375,
"supported" : true,
"mapped" : 976
},
从以上测试来看,在生产环境下,最好预留1-2G的内存给mongod!
时间:
创建完100万个文档,时间, 3分20秒 左右(多次测试)
baicaomatoiMac:mongo baicao$ time mongo process_task.js
MongoDB shell version: 1.6.2
connecting to: test
real 3m22.432s
user 3m14.673s
sys 0m6.514s
find查询:
击中索引,基本只需要0ms(测不到), 最坏的情况也是在26ms(第一次查询),mongo在查询之后就有cach,后面基本就是0ms了。
> db.process.find({"index.task_name":"ReturnMoney","index.user_id":100,"index.status":1}).explain()
{
"cursor" : "BtreeCursor index.task_name_1_index.user_id_1_index.status_1",
"nscanned" : 50,
"nscannedObjects" : 50,
"n" : 50,
"millis" : 0,
"indexBounds" : {
"index.task_name" : [
[
"ReturnMoney",
"ReturnMoney"
]
],
"index.user_id" : [
[
100,
100
]
],
"index.status" : [
[
1,
1
]
]
}
}
不是完全击中索引,加上process_id(非索引字段)这个查询条件,速度也是非常快,基本只要1ms, 最差在8ms(第一次)。即先用索引击中100或200条记录,再在这百条记录里查找process_id,也是非常快。
这给我们一个启示,只对最关键的字段做索引。有些字段可以让mongo去遍历的。 如果都做成索引,那索引太大,需要的内存就要非常大!
> db.process.find({"index.task_name":"Precontract","index.user_id":100,"index.status":1, process_id:0}).explain()
{
"cursor" : "BtreeCursor index.task_name_1_index.user_id_1_index.status_1",
"nscanned" : 100,
"nscannedObjects" : 100,
"n" : 100,
"millis" : 1,
"indexBounds" : {
"index.task_name" : [
[
"Precontract",
"Precontract"
]
],
"index.user_id" : [
[
100,
100
]
],
"index.status" : [
[
1,
1
]
]
}
}
如果没有索引,一百万条记录,对4个字段查找(遍历),要1000ms左右。 速度还是非常快,但离可用性还是有距离。
缩小数据库文件大小
MongoDB查询能力强,性能好,但代价是内存开销大,数据库文件大。每个文档都有字段定义!
用第一个脚本创建:process_task.js(见附件)创建,100万条文档需要2G-3G大小的数据库文件。
文件大小: 128 + 256 + 512 + 1024 + 1024 * 2 = 3968 M, 约4G, 最后的2G可能没怎么用,那最小也在2G左右
-rw------- 1 root admin 134217728 4 1 10:28 genius_t_1.1
-rw------- 1 root admin 268435456 4 1 10:28 genius_t_1.2
-rw------- 1 root admin 536870912 4 1 10:28 genius_t_1.3
-rw------- 1 root admin 1073741824 4 1 10:28 genius_t_1.4
-rw------- 1 root admin 2146435072 4 1 10:27 genius_t_1.5
用mongo自带命令查看
genius_t_1: 663M, 每个文档平均大小是544!
> db.printCollectionStats()
process
{
"ns" : "genius_t_1.process",
"count" : 1000000,
"size" : 544000000,
"avgObjSize" : 544,
"storageSize" : 663950336,
"numExtents" : 20,
"nindexes" : 2,
"lastExtentSize" : 117793792,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 302638016,
"indexSizes" : {
"_id_" : 41394176,
"index.task_name_1_index.user_id_1_index.status_1" : 261243840
},
"ok" : 1
}
用process_task_short.js创建,把process_id 简写为pid, user_id, 简写为uid, task_name, 简写为name
genius_t_2: 616M, 每个文档平均大小是508!
process
{
"ns" : "genius_t_2.process",
"count" : 1000000,
"size" : 508000000,
"avgObjSize" : 508,
"storageSize" : 616527872,
"numExtents" : 20,
"nindexes" : 2,
"lastExtentSize" : 109380352,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 302638016,
"indexSizes" : {
"_id_" : 41394176,
"index.name_1_index.uid_1_index.state_1" : 261243840
},
"ok" : 1
}
用process_task_short_short.js创建,把手机号用整数方式来存,再一步缩短字段名。
genius_t_3 : 499M, 每个文档平均大小是460, 比最初的544,下降了15%的空间 。数据库文件上也没有出来2G的那个文件了。所以,这种优化还是非常值的,就是可读性略有下降。但只对约定的字段进行简缩,并没有对任务名进行缩简。
process
{
"ns" : "genius_t_3.process",
"count" : 1000000,
"size" : 460000136,
"avgObjSize" : 460.000136,
"storageSize" : 499348736,
"numExtents" : 19,
"nindexes" : 2,
"lastExtentSize" : 89748992,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 284779456,
"indexSizes" : {
"_id_" : 41394176,
"index.name_1_index.uid_1_index.mob_1" : 243385280
},
"ok" : 1
}
总结:
1. mongoDB占用内存挺大的,虚拟内存都是1G以上,随着查询,会用更多的内存(索引)被进驻,生产环境最好留1-2G给mongoDB
2. mongoDB的性能非常好,在有索引的情况下,0ms。没有必要所有的字段都做成索引,先用索引确定几百个文档,对非索引字段进行遍历,也只需要1ms。
3. mongoDB的数据库文件也非常大,100万条文档也有在1G以上。所以数据库设计时,在保证可读性性的前提下,尽量缩短字段名 。像常用的字段(约定俗成)长度最好在5个字符以下。参考测试附件。
附件:
测试脚本: