`

MongoDB Sharding深入学习

 
阅读更多

对于MongoDB的Sharding(分片)技术并不陌生,但是发现里面其实还是有不少值得深入学习的东西。笔记整理一下发上来跟大家分享。

-----------------------------------

一、MongoDB分片机制:

1、一个分片包含数据的某一子集。若某一分片包含多台服务器。则每台服务器都拥有完整的数据副本。

2、一分片多区间:

一个区间数据称为一个数据块(chunk)。当我们把一个块的区间一分为二时,一个块也变成了两个块。

当一个块变得越来越大时,MongoDB会自动将其分割成两个较小的块。如果分片间比例失调,则MongoDB会通过迁移块来确保均衡。

3、如何创建块:

当决定分配数据时,必须为块区间选择一个键,这个键叫做片键,片键可以是任意字段或字段的组合。

分片是对集合分片。在插入更多数据的同时,MongoDB会持续地将已有块分割成更多的新块。

块的区间可以只包含一个值,但是各块的区间必须互不相同,也不能有区间相互重复的块,而且每个块的区间必须紧邻下一个块的区间。

每个文档必须属于且仅属于一个块。

真实系统中,块大小默认仅有200M,200M恰好是兼顾可移动性和最小开销的最佳选择。

块是一个逻辑概念,而非物理实现。一个块中的文档在物理上并不连续存储于磁盘上或以任何形式聚集在一起,它们可能分散在整个集合的任何角落里。一个文档属于一个块当且仅当其片键值在对应的块区间内。

4、片键:

因为MongoDB不强制要求定义文档结构,所以MongoDB不允许插入无片键的文档(尽管可以使用null作为片键值),也不允许更新文档的片键值。给文档一个新片键唯一只能先删除文档,再在客户端修改片键的值后,重新插入文档。

允许一些文档中片键使用字符串,另一些文档中使用数字。MongoDB中类型有严格的次序,MongoDB会按照类型对其排序:

null<数字<字符串<对象<数组<二进制数据<ObjectId<布尔值<日期<正则表达式

5、平衡:

如果存在多个可用分片,只要块的数量足够多,MongoDB就会把数据迁移到其它分片上,整个迁移过程叫平衡。由叫做平衡器的进程负责执行。

平衡器的优点在于自动化,它每天基于分片整体大小来移动块。要保持数据均匀分布,还要最小化被移动的数据量。

要触发一轮平衡,一个分片必须比块最小的分片多出至少9个块。通过等待更严重的不平衡发生,MongoDB能够最小化无意义的数据传输。

6、自定义块大小与递增块大小:

自定义块大小:--chunkSize N (N就是想要的块大小),请勿随意改动块大小。

递增块大小:MongoDB对于前十几个块,MongoDB会特意自动降低块大小,从200M降到64M,这就是为了更好的照顾用户,当数据块多起来,它会自动把块大小递增回200MB。

7、Mongos允许把一个集群当做一台服务器。

二、片键

1、选择片键:

(1)小基数片键:由于片键值数量有限,因此称为小基数片键。如果选择了一个基数很小的片键,到头来肯定会得到一堆既巨大又无法移动,还不能分割的块。

如果打算用小基数片键的原因是需要在那个字段上进行大量查询,请使用组合片键(一个片键两个字段),并确保第二个字段有非常多不同的值可以供MongoDB用来进行分割。

片键值无法更新,因此也不可能通过更新所有文档来使用一个更独特的片键值。

(2)升序片键:

人们往往会尝试使用诸如时间戳或者OjbectId一类的字段来做片键,但这并不像他们所期望的那样可行。这种片键创造了单一且不可分散的热点。

包括ObjectId、日期、自增主键,只要键值趋向于无穷大,就会面临这个问题。

除非自己清楚在干什么,否则不要使用升序片键。

(3)随机片键:

为了避免热点,人们会选择一个取值随机的字段来分片。这种片键一开始还不错,但随着数据量增大,它会变得越来越慢。

考虑到数据序列的随机性,一般情况下这些数据可能不会出现在内存中。所以此时的MongoDB会给RAM带来更大的压力,而且还会引发大量磁盘IO。

另外片键上必须有索引,因此如果选择了从不依据它进行查询的随机键,基本上可以说浪费了一个索引。(每增加一个索引都会使写操作变慢,所以保持索引数量尽可能低也是非常重要的)。

2、好片键:

(1)准升序键加搜索键:希望数据大致上按照时间排序,但是同时也要均匀分布。这样既能把我们正在读写的数据保持在内存,又可以使负载均衡的分散在集群中。

(2)可以通过组合片键来实现目标:{coarselyAscending:1,search:1}。

其中coarselyAscending键的每个值最好能对应几十到几百个数据块,而search键则应当是应用程序通常都会依据其进行查询的字段。search字段不可以是升序片键,如果是则会遇到使用升序片键同样的问题。search字段应该具备非升序、分布随机且基数适当的特点。

(3)基于上面的内容,概括出的片键公式:{coarseLocality:1,search:1}.

其中coarseLocality字段用来控制数据局部化。search字段则是数据上常用到的一个检索字段。

三、集合分片:

1、配置服务器只是普通的mongod实例,由于需要它们中的部分配置服务器一直正常运行,所以把它们分到不同的故障域里。

2、分片还不支持认证,但在2.0版已发生变化。

3、应该使用副本集而非单个服务器来做每个分片。

4、如果服务器严重失衡,应该使用maxSize选项指定分片能增长到的最大存储量,单位MB。

5、MongoDB不会从maxSize处截断一个分片,或是阻止它再增加哪怕一个字节,而是会停止将数据移动到该分片,当然还可能会移走一部分数据。所以设置这个值时可以略微低一些。

6、对已经包含数据的集合进行分片,数据片键上必须有索引。所有文档必须也都必须有片键值(且不能为null)。在集合分片完成后,才可以插入片键值为null的文档。

7、如果等服务器容量耗尽再添加分片,新分片可能会导致应用程序在一系列连锁反应下逐步瘫痪。要在有足够空间做调度时添加分片,如果没有就等到夜间再添加分片,所以添加分片要尽早做,经常做。

四、使用集群:

1、计数:当在一个分片集合上进行计数时,可能得不到期望结果,可能是一个比实际文档量多的数。因为如果有一个迁移正在进行,许多文档就会同时存在于多个分片上,计数值可能会大于实际的文档数。

通过复制将一个块迁移到新的分片上,然后再从源分片上将它删除。

2、通常要确保分片间不存在重复,唯一方法是在每次要执行写操作时锁住整个集群,直至写操作确认成功。这样系统很难实现高性能写。

因此,除片键以外的任何键都难以保证唯一性,可以保证片键的唯一性是因为特定文档只能属于一个数据块。只需要在那个分片上唯一,就能保证在整个集群中是唯一的。

因此推出一个结论:除非在_id上分片,否则能够创建出不唯一的_id值,这确实是可能的。

3、更新:

如果要更新单个文档,一定要在条件中使用片键(update的第一个参数)。如果不这么做,会提示错误禁止更新。

在批量更新中可以使用任何条件。

如果碰到奇怪的错误,考虑一下执行的操作是否对整个集群必须是原子化的,这类操作是不被允许的。

4、MapReduce:

每个分片都会执行它自己的映射(map)及收敛(reduce)。mongos选出一个领头羊分片以接受来自其他分片的收敛结果并在此完成最后收敛,一旦数据被收敛到最终形态,就会按照指定的方式输出。

分片将任务分解到多台机器,因此能比单台服务器更快地执行MapReduce,但它仍不适合做实时运算。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics