1、如何创建块
在前面的已经了解MongoDB分片架构中块的基本概念,那么在此来讲述如何创建块?当你决定分配数据时,必须为块区间选择一个键(前面我们一直在用username)这个键叫做片键(shard key),片键可以是任意字段或者字段的组合,比如如下文档中:
<pre name="code" class="java"><pre name="code" class="java">{"username":"paul","age":23} {"username":"simon","age":17} {"username":"widdly","age":16} {"username":"grill","age":95} {"username":"bertango","age":55}
如果我们选择age字段作为片键并得到一个块区间[20,26),则此块会包含以下文档:
{"username":"paul","age":23} {"username":"grill","age":95} {"username":"bertango","age":55}
如你所见,这个块中的所有文档其age字段值都在这个块的区间内。
2、对集合分片
对一个集合分片时,无论集合里有什么数据MongoDB都只会创建一个块,这个块的区间使(负无穷,正无穷),其中负无穷是MongoDB可以表示最小值(也叫做$minKey),正无穷是最大值(也叫$maxKey)。
注意:如果被分片的集合中包含大量数据,MongoDB会立刻把这个初始化分割为多个较小的块。
事实上,由于上面例子中的集合太小并不能触发分割,所以在插入更多数据之前,都只有一个块(负无穷,正无穷),尽管如此,为了达到演示的目的,我们假设这个数据量已经足够大了。
MongoDB会把初始化块(负无穷,正无穷)分割成为两个新块,分割位置一般选在已有数据区间的中点附近,因此,如果大约一半文档的age字段小于20,且另外一半大于20.MongoDB就会很可能会选择20,这样就得到两个块:(负无穷,20]和[20,正无穷),如果我们往块[20,正无穷)里继续插入数据,它会再一次被分割(比如说分割成[20,30)和[30,正无穷)),这样集合中就有了3个块(负无穷,20)、[20,30)和[30,正无穷),在插入更多数据的同时,MongoDB会持续地将已有块分割成更多的新块。
块的区间可以只包含一个值(比如仅包含用户名为paul的用户),但是各块的区间必须互不相同(不能有两个块得区间同为["a","f"),另外也不能有区间有互相重叠的块,而且每个块得区间必须紧邻下一个块的区间,因此如果要分割一个区间为[4,8)的块,结果可以使[4,6)好[6,8)(因为二者合起来能覆盖原块得区间),但不可以是[4,5)和[6,8)(因为这样集合将丢失区间[5,6)中的所有数据),也不能是[4,6)和[5,8)(因为会造成块得部分重叠),每个文档必须属于且仅属于一个块,如下图演示一个块分割成两个块:
说明:
由于MongoDB不强制要求任何形式的结构定义,你可能会纳闷,那没有值可以作为片键的文档会被放到哪里呢?
实际上MongoDB并不允许插入无片键的文档(尽管使用null作为片键也是可以的),也不允许修改文档的片键值(如用$set命令)。给文档一个新片键的唯一方法是先删除文档,然后在客户端修改片键的值,再重新插入文档。
如果在一些文档中使用字符串,而在另外一些文档中使用数字呢?这也是可以的,因为在MongoDB中类型之间有严格的次序,如果在age字段插入一个字符串(或者数组、布尔值、null等),MongoDB会按照类型对其排序,类型先后次序如下:
null<数字<字符串<对象<数组<二进制数据<objectId<布尔值<日期<正则表达式。
在同一种类型内,排序与你所期望的很可能相同:2<4或者a<z,在理解分片的例子中每一个块都是几百GB大小,但在真实系统中,块大小默认仅有200MB,这是因为挪动数据的代价非常大,要花费很长的时间,占用系统资源,而且会明显增加网络流量,你可以自己试试看,先插入200MB数据到某一个集合中,然后试着取回所有200MB数据,接着想象一下在建多个索引的系统上做同样的事情,同时该系统上还有其他数据流量存在的情况,你肯定不会愿意看着应用程序逐渐降低性能直到停止,而MongoDB还在后台拖拖拉拉地挪动数据,实际上,如果一个块过大,MongoDB根本就不会移动它,反过来你也不会希望块过小,因为每个块都需要有一点点管理开销(以便你不必为跟踪不计其数的数据块而烦恼),综合考虑之下,我们发现200MB恰好是兼顾可移动性和最小开销的最佳选择。
总结:
块是一个逻辑概念,而非物理实现,一个块中的文档在物理上并不连续存储于磁盘上或者以任何形式聚集在一起,它们可能分散在整个集合的任何角落里,一个文档属于一个块当且仅当其片键值在对应的块区间内。