分片键决定了MongoDB集群如何对数据进行分布,mongos实例如何将操作请求转发给集群,因此分片键的选择将直接影响MongoDB的数据库性能。
一、分片键的影响
1. Write Scaling(写扩展)
某些集群键可以利用集群提供的写能力,而另外一些集群键则不能。我们以集合的默认对象ID即_ID字段为例,看分片键对写操作的影响。MongoDB为每个新创建的文档产生一个唯一的对象ID,然而该值的大部分表示的是时间戳,这意味则对象ID的值将以一个规律的、可预测的方式生成。当然这些对象ID的值是具有很高的基(即拥有大量不同的值),但是由于这些值是线性增长的,导致了插入操作将会将数据存储到同一个Chunk中,当然也会在同一个分片中。这样该分片的写能力将决定整个集群的写能力。如果我们很少有插入操作,主要是更新操作的话,那么具有线性增长特性的分片键将不会阻碍数据的写性能。因此选择一个具有大量基的同时能够将写操作分布到整个集群的分片键是一个良好的选择。MongDB 2.4版本提供了基于HASH的分区,能够将写操作随机的分布到整个集群。
2. Query Isolation(查询隔离)
mongos实例为应用程序提供接口与分片集群进行交互,它隔离了复杂的集群数据分区问题。在集群环境下,使用分片键且只有一个分区参与的查询将是最快的查询。那些不包含分片键的查询将查询所有的分片,mongos实例将操作转发给所有分片后将会等待所有分片返回结果后才向应用程序返回,因此这些查询将会是耗时的查询。如果我们查询条件包含分区键或者一部分分区键,mongos实例将会把查询请求转发给一个或者几个分片,这将会提高查询的性能。因此我们需要考虑:
- 应用中那些字段将经常作为查询条件
- 应用中那些查询的性能最重要
二、选择分片键的考虑因素
分片键的选择是否合适,对数据库集群的性能、容量以及功能都有巨大的影响。分片键选择的合理性依赖于数据的模式以及我们查询、写数据的方式。下面几条都是选择分片键的潜在考虑因素。
1. 选择容易分开的字段为分片键
易分散的分片键可以方便MongoDB把数据分布到各个分片,只有有限取值的分片键将导致Chunk难以分裂。比如,性别字段就不是一个良好的候选分片键,它的不同值太少,只有两个。而邮政编码就是一个比较好的候选分片键,在中国它的取值是每个乡镇一个,可以达到上万个;身份证号就是一个更好的候选分片键了,每个人一个,可以达到上亿的不同值。
2. 选择具有随机性字段为分片键
有的字段虽然具有大量的不同取值,但是这些字段在插入时不具有随机性,而具有规律性。这种规律性使得插入时一个分片成为热点,其他分片不能负担写压力,从而产生性能瓶颈。比如上面提到的_ID字段,虽然它具有很大的基,但是它不具有随机性,每次插入时都会插入到同一个Chunk上;而身份证号码则同时拥有很大的基同时有具有随机性。
3. 选择经常作为查询条件字段为分片键
这类分片键可以使得查询时mongos仅仅将查询发送给特定的mongod实例,不需要等待多个实例返回数据后再进行合并。这一点与上面一条存在冲突,一个具有高度随机性的字段将难以实现查询时仅仅涉及特定的分片,因为其数据一定是打散后分布在多个分片的。
从MongoDB 2.4版本支持Hash分片键看,数据分布的均衡性优先级高于查询的分区过滤。
4.考虑使用组合分片键
有时候集合中的现有字段单独作为分片键不合适,把现有一个字段的计算值或者直接使用组合键作为分片键会产生一个较为理想的结果。
三、基的重要性
我们上面提到,对数据库集群来说,数据分布的均衡性优先级很高,而字段的基直接决定该字段是否合适作为分片键。基是指一个字段不同取值的个数,如性别字段的基为2,在中国省字段的基为31个,而身份证字段的基为13亿个。分片键的基决定了系统将数据分片到各机器的能力。当分片键具有较少的基时,所有所有的键值相同导致MongoDB不能分裂Chunk。迁移这些不可分裂的Chunk将更加耗时,也即使迁移后也难以保证数据在各个分片上的平衡。Chunk数量被基约束住后,我们就不能利用MongoDB集群特性为集合部署更多的机器。