所谓片键,就是用来拆分数据的字段,通常为1-2个字段,由于片键一旦确定,并已经分片过后,基本上就不可能再修改片键了,因此初期设计和选择就非常重要了
1:不可以是数组
2:一旦插入了文档,片键不可修改,要修改就必须先删除文档,然后才能修改片键
3:大多数特殊类型的索引都不能作为片键
4:片键的数据取值应该是多样的,这样才利于分片
1:小基数片键
就是片键可取的值非常少,所以叫小基数。通常这不是个好方式,因为:片键有N个值,也就最多只能有N个块,也就是最多只能N个分片。这也意味着当某个块越来越大的时候,MongoDB无法拆分块,因此你什么也干不了,除了购买更大的硬盘。
2:升序片键
就是片键值是不断增加的,类似于自增字段。通常这不是个好方式,因为:
(1)新加入的数据始终会加入最后一个块,即所有数据都被添加到一个块上,从而导致了热点必然存在,且是单一,不可分割的热点。
(2)由于数据始终会先加入到最大块,会导致最大块需要不断的拆分出新的小块
(3)会导致数据均衡处理很困难,因为所有的新快都是由同一个分片创建的
3:随机分发的片键
就是片键值是随机散列的数据,这种方式对数据的均衡是有好处的,数据加载速度也很快,缺点是:如果需要按照片键值进行范围查找的话,就必须到所有分片上执行了。
创建一个散列片键,需要先创建散列索引,示例如下:
db.users.ensureIndex({“userId”:”hashed”});
然后对集合分片,示例如下:
sh.shardCollection(“mydb.users”,{“userId”:”hashed”});
4:基于某个业务的片键
这个就要具体问题具体分析和选择了,比如:选择用户IP,电话号码段,或者是自定义的编码段等。如果要指定特定范围的块出现在特定的分片中,可以为分片添加tag,然后为块指定tag,例如:
sh.addShardTag(“myrep2”,“gtu”) 然后:
sh.addTagRange("mydb.users",{"userId":"v0"},{"userId":"v9"},"gtu");
就可以设定v0-v9的数据存放到myrep2这个分片里面去
(1)在设置范围的时候,可以使用:ObjectId()、MinKey、MaxKey 等来作为值
(2)不用某个tag了可以删除:sh.removeShardTag(“myrep2”,“gtu”);
实际项目中,建议尽量采用准升序键加查询键构成组合片键。
其中升序键的每个值最好能对应几十到几百个数据块,而查询键则是应用程序通常都回依据其进行查询的字段。
例如:某应用,用户会定期访问过去一个月的数据,就可以在{month:1,user:1}上进行分片,month是一个粗粒度的升序字段,每个月都会增大;而user是经常会查询某特定
用户数据的查询字段。
1:注意一点:查询键不可以是升序字段,否则该片键会退化成为一个升序片键,照样会面临
热点问题
2:通常通过准升序键来控制数据局部化,而查询键则是应用上常用的查询字段