在Mongodb的sharding架构中,每个片上的数据,是按照chunk(块)为最小单位组织的。chunk是一个数据集合,默认64M。一个片上有很多chunk,但是一个chunk只能在一个片上。chunk是一个区间,比如{{"Wid":10000}-->>{"Wid":10010}},这就是一个块,Wid就是我们要说的片键,是一个表中的某个字段。表中的数据按照这个字段排序切分成块,分布到各个片上。查询的时候也是按照这个字段去查询,相当于关系数据库中分库分表的作用。
在Sharding结构中,分片策略,片键选择是影响性能的关键因素,片键不仅影响数据分布,而且影响业务逻辑,所以片键的选择不单单是均匀的将数据分布到各个片上,而且要考虑查询的性能。坏的片键有时候会导致数据分布很差,有时候会导致无法使用局部性原理,还有一些会影响数据块的拆分。
一、低效的片键
1、分布性差
升序片键。要分析分布性问题,首先我们要记住分片是基于范围的。使用升序的片键,所有最新插入的数据都会落到某个很小的连续范围内。也就是说,这些插入都会被路由到一个块上,而这个块肯定存在在某个片上,这实际上抵消了分片一个很大的好处,将插入的负载自动分布到不同的机器上,这对插入负载很高的应用是不合理的。而且,mongodb是带平衡器的,如果某个片上的chunk过多,那么平衡器会将多出的chunk转移到其他片,升序片键其实也加重了转移chunk的负担。注意,升序片键并不影响更新,只要是随机更新的就可以。
2、缺乏局部性
完全随机片键。通过第一点,我们会想,是不是随机的字段会适合作为片键,可以防止分布性问题。是的,随机片键可以将新插入的数据路由到各个片上,有效减轻单个片的插入负载。但这并不完全合理,比如完全随机片键。假设分片集合里的每个文档都包含一个MD5,而且MD5就是分片键,在对每个分片的MD5字段索引进行插入的时候,每次插入过程中,索引中的每个虚拟内存分页都有可能被访问到,实际上这就意味着索引必须总是装在内存里,如果索引和数据不断增多,超出了物理内存的限制,那么就会产生页错误(page fault),导致性能下降。
这其实是局部引用性问题。局部的概念,在这里指任意给定时间间隔内所访问的数据基本都是有关系的,例如虽然升序的片键是糟糕的,但是它提供了很好的局部性,对索引的连续插入都会发生在最近使用的虚拟内存分页里;因此,在任意时刻内存里只要有一部分索引就可以了。
再用上边的例子,比如说MD5是用户存的文件的MD5值,作为片键,用户上传100个文件,那么对索引的修改就基本会发生在随机的100个地方,但是如果我们使用用户ID作为片键,那么每次写索引基本都会发生在同一个地方,因为插入的文档都拥有相同的用户ID值。这就利用了局部性。
完全随机片键还有一个问题,对这个片键任意一个有意义的范围查询,都会被发送到所有的分片上,然后返回mongos汇总。但是对一个较粗粒度的片键进行范围查询,是可以落到单个分片上。
3、无法拆分的块
粗粒度片键。如果升序片键和完全随机片键都不好用,那么一个显而易见的选择就是粗粒度的分片建,比如上边说的用户ID。如果按照用户ID分片,你可以预料到插入会分布在各个分片上,因为无法预知哪个用户何时回插入数据。这样一来,粗粒度分片键也能拥有随机性,还能发挥分片集群的优势。
而且粗粒度的片键还能使用局部性带来的效率提升。当某个用户上传100个文件,基于用户ID字段的分片建能确保这些插入都落到同一个分片上,并几乎能写入索引的同一部分,这样效率很高。
粗粒度分片键在分布性和局部性上都表现很好,但是它也有一个很难解决的问题:块有可能无限制的增长。想想基于用户ID的片键,假如有几个特殊用户,他们上传了上百万个文件,那么一个块里就可能只有一个用户ID,这个块能拆分么?不能,因为用户ID是最小的粒度,拆分了查询就没法路由到数据。这就造成分片之间数据量不均衡。更典型的就是type,status这类的字段,因为它们的选择性是在是太低,导致无法拆分。
二、理想的片键
上边我们讨论了低效片键的问题和原因,显然,理想的片键应该结合粗粒度分片键与细粒度片键两者的优势。
一个好的片键必须包含的特性:
1、将插入数据均匀分布到各个分片上
2、保证CRUD能利用局限性
3、有足够的粒度进行块拆分
满足这些要求的的片键通常由两个字段组成,第一个是粗粒度,第二个是粒度较细。那么我们需要使用复合片键{userid:1,_id:1}。当用户同时插入数据时,我们可以预见大多数情况下,这些数据会被均匀的分布到所有的片上,而且分片里的唯一字段_id能保证对任意一个文档的查询和更新始终都能指向单个分片。如果对用户ID执行更复杂的查询,那么路由也只会将查询路由包含此用户ID存在的片上,而不会发到所有分片。
由于_id(升序)的存在,保证了块始终是能继续拆分的,哪怕用户创建了大量文档,情况也是如此。
三、总结
一个理想的片键可以极大的提升CRUD的性能,充分利用分片的优势。一般情况下,片键都会选择一个粗粒度+细粒度的组合片键,而且这也能充分利用复合索引的优势(注意顺序)。当然在一些系统中,比如确定用户不会上传大量文档,那么只要一个用户ID也是可以的,看系统设计。
阅读(8091) | 评论(0) | 转发(0) |