首先这类NoSQL数据库的设计宗旨基本是“读多写少”。
而我们的需求是:有一大批数据(5亿条记录,每条100B左右)。需要灌入MongoDB。
考虑到数据规模,事先已经做好了Sharding,10台机器。
1、并发12进程,持续灌入数据。
一开始速度很快,基本10W/s,到了1亿6千万左右,开始变成龟速。经过仔细检查,mongos中报大量如下错误:
1
|
Wed Jun 6 16:56:41 [conn33] warning: splitChunk failed - cmd: { splitChunk: "trec.doc", keyPattern: { id: 1.0 }, min: { id: MinKey }, max: { id: "00029610933" }, from: "172.22.0.16:27018", splitKeys: [ { id: "00000000000" } ], shardId: "trec.doc-id_MinKey", configdb: "172.22.0.11:27019" } result: { who: { _id: "trec.doc", process: "node5:27018:1338970017:616299555", state: 2, ts: ObjectId('4fcf1a425ac42b3930c16116'), when: new Date(1338972738056), who: "node5:27018:1338970017:616299555:conn14:1486636348", why: "migrate-{ id: "01032438609" }" }, errmsg: "the collection's metadata lock is taken", ok: 0.0 }
|
其实很很容易理解,灌入速度太暴力,导致很多后台进程如分片、迁移Shard的任务一直拿不到锁。
最后导致Shard1上面有130个Chunk,其他上面就是3~5个。
杀掉进程后,Mongo开始逐渐均衡,但考虑到时间会很长,遂删除数据。
2、灌数据建索引。
我们对灌入的数据需要建一些索引,之前没有经验,在配置好Shard后就ensureIndex了,导致到达1亿条后速度明显变慢。
其实这种先写后读的需求,完全可以等全部插入后再建索引,防止大量的锁竞争。
3、关于数据分块大小
默认的64MB,对于千万级别是可以的。像我们这种,就要适当调大,不然反复的split会导致性能下降。如果再加上持续插入数据,很可能导致一直无法split,当分片过大后就无法再进行迁移了。
4、关于自动Balance及我们的策略
经过仔细阅读文档,我发现MongoDB的Balance做的非常好,是后台自动完成的。但有一个缺陷:就是前面提到的,前台数据插入优先级更高,Balance只能在空闲时候做。
因此,我把5亿条数据分为10部分,每部分插入完成后,等待Balance平衡后,再插入下一部分。一般来说平衡的过程是非常快的,等一会再插入,能保证持续不断的性能。
关于查看各个Shard上的Chunk数量:
1
|
db.printShardingStatus()
|
此外,还修改了Chunk Size为256MB,如3中所述。