1、平衡简介
如果存在多个可用的分片,只要块得数量足够多,MongoDB就会把数据迁移到其他分片上,这个迁移过程叫做平衡(balancing),由叫做平衡器(balancer)的进程负责执行。
2、平衡工作流程
平衡器会把数据块从一个分片挪到另外一个分片上,其优点在于自动化,即你无需担心如何保持数据在分片间的均匀分布,这项工作已经由平衡器替你搞定,不过这也是它的缺点,因为自动意味着如果你不喜欢塔做负载均衡的方式,那只能算你不走运,如果不想让某个块存在于分片3上,你可以手动移动到分片2上,但是平衡器很可能把它再挪回分片3,你只能选择要么对集合重新分片(re-shard),要么关闭平衡化。
在此之前平衡器的算法还不是很智能,它每天基于分片整体大小来移动块,在不久的将来它将变得更加先进。
平衡器的目标不仅是要保持数据均匀分布,还要最小化被移动的数据量,因此触发平衡器需要很多条件,要触发一轮平衡,一个分片必须比块最小的分片多出至少9个块,到那时候,块就会被迁移出拥挤的分片,直到与其他分片平衡为止。
平衡器并不非常激进的原因在于MongoDB希望能够避免将相同数据来回移动,如果平衡器要平衡掉没一点微小的区别,那很可能会不停地浪费资源:分片1比分片2多两个块时,它就发送一个块给分片2,接着一些写操作落到分片2上,使得分片2又比分片1多出两个块,结果同一块数据又被折腾回去,通过等待更严重的不平衡发生,MongoDB能够最小化无意义的数据传输,要知道差9个块其实也不是那么不平衡,因为这还不到2GB数据呢?
说明:
如果如上图所示,每一点轻微的不平衡都被修正,则最终必然导致大量不必要的数据移动。
3、技巧
大家都希望通过看到数据移动来向自己证明分片可以工作,这产生了一个问题:触发一轮平衡所需的数据量远远比大多数人想象的大。
比如说正在尝试分片,于是写了一个命令行脚本来插入50万份文档到分片集合中:
for(i=0;i<500000;i++){ db.foo.insert({"_id":i,"x":1,"y:2"}); }
等插入完成,我应该能看见一些数据飞来飞去了,对不对?错,如果我看一眼数据库状态,就会发生还差得远呢?这些数据大致约40MB,这不够一个块,甚至不够一个块的1/4,前面讲到了一个块默认是200MB,真要想看到数据移动话,需要插入2GB数据,也就是2500万份这样的文档,或者说现在已插入数据的50倍。
开始使用分片时,人们希望看到数据到处移动,这是人的本性使然,尽管如此,在生产系统中你不会希望发生太多迁移,因为这种操作代价极其高昂,因此一方面我们希望看到迁移实际发生,而另外一方面事实又是如果它不是看上去慢慢得让人烦躁就不可能工作的很好,对于这个矛盾,MongoDB需要实现两个技巧,让分片更善解人意,同时又不对生产希望造成破性的影响。
- 自定义块大小
第一次启动mongos时,可以声明--chunkSize N参数,其中N就是你想要的块大小,单位是MB,如果只是想试试分片,可以设置--chunkSize 1,这样只要插入几MB的数据就能看到迁移发生了。
- 递增块大小
即使是部署一个真正的应用程序,要达到2GB看起也遥遥无期,所以对于前十几个块,MongoDB会特意自动降低块大小,从200MB降至64MB,而这就是为了更好地照顾一下用户的感受,一旦数据块多起来,它会自动把块大小递增回200MB。
- 改变块大小
块大小可以通过启动时指定--chunkSize N参数或者修改config.settings集合并整体性重启来改变,尽管如此,除非是为了好玩试试1MB的块大小,否则别启动改动其大小。