Rumah >pangkalan data >tutorial mysql >MongoDB中shard key的选择
将存储在MongoDB数据库中的Collection进行分片需要选定分片Key(Shard key),对于分片Key的选定直接决定了集群中数据分布是否均衡
将存储在MongoDB数据库中的Collection进行分片需要选定分片Key(Shard key),对于分片Key的选定直接决定了集群中数据分布是否均衡、集群性能是否合理。那么我们究竟该选择什么样的字段来作为分片Key呢?有如下几个需要考虑点。
以下述记录日志的Document为例:
{
server : "ny153.example.com" ,
application : "apache" ,
time : "2011-01-02T21:21:56.249Z" ,
level : "ERROR" ,
msg : "something is broken"
}
基数
Mongodb中一个被分片的Collection的所有数据都存放在众多的Chunk中。一个Chunk存放分片字段的一个区间范围的数据。选择一个好的分片字段非常重要,否则就会遭遇到不能被拆分的大Chunk。
用上述的日志为例,如果选择{server:1}来作为一个分片Key的话,一个server上的所有数据都是在同一个Chunk中,很容易想到一个Server上的日志数据会超过200MB(默认Chunk大小)。如果分片Key是{server:1,time:1},那么能够将一个Server上的日志信息进行分片,直至毫秒级别,绝对不会存在不可被拆分的Chunk。
将Chunk的规模维持在一个合理的大小是非常重要的,只有这样数据才能均匀分布,并且移动Chunk的代价也不会过大。
写操作可扩展
使用分片的一个主要原因之一是分散写操作。为了实现这个目标,尽可能的将写操作分散到多个Chunk就尤为重要了。
用上述的日志实例,选择{time:1}来作为分片key将导致所有的写操作都会落在最新的一个Chunk上去,这样就形成了一个热点区域。如果选择{server:1,application:1,time:1}来作为分片Key的话,那么每一个Server上的应用的日志信息将会写在不同的地方,如果有100个Server和应用对,有10台Server,那么每一台Server将会分担1/10的写操作。
查询隔离
另外一个需要考虑的是任何一个查询操作将会由多少个分片来来提供服务。最理想的情况是,一个查询操作直接从Mongos进程路由到一个Mongodb上去,并且这个Mongodb拥有该次查询的全部数据。因此,如果你知道最为通用的查询操作的都以server作为一个查询条件的话,以Server作为一个起始的分片Key会使整个集群更加高效。
任何一个查询都能执行,不管使用什么来作为分片Key,但是,如果Mongos进程不知道是哪一个Mongodb的分片拥有要查询的数据的话,Mongos将会让所有的Mongod分片去执行查询操作,再将结果信息汇总起来返回。显而易见,这回增加服务器的响应时间,会增加网络成本,也会无谓的增加了Load。
排序
在需要调用sort()来查询排序后的结果的时候,以分片Key的最左边的字段为依据,Mongos可以按照预先排序的结果来查询最少的分片,并且将结果信息返回给调用者。这样会花最少的时间和资源代价。
相反,如果在利用sort()来排序的时候,,排序所依据的字段不是最左侧(起始)的分片Key,那么Mongos将不得不并行的将查询请求传递给每一个分片,然后将各个分片返回的结果合并之后再返回请求方。这个会增加Mongos的额外的负担。
可靠性
选择分片Key的一个非常重要因素是万一某一个分片彻底不可访问了,受到影响的Chunk有多大(即使是用貌似可以信赖的Replica Set)。
假定,有一个类似于Twiter的系统,Comment记录类似如下形式:
{
_id: ObjectId("4d084f78a4c8707815a601d7"),
user_id : 42 ,
time : "2011-01-02T21:21:56.249Z" ,
comment : "I am happily using MongoDB",
}
由于这个系统对写操作非常敏感,所以需要将写操作扁平化的分布到所有的Server上去,这个时候就需要用id或者user_id来作为分片Key了。使用Id作为分片Key有最大粒度的扁平化,但是在一个分片宕机的情况下,会影响几乎所有的用户(一些数据丢失了)。如果使用User_id作为分片Key,只有极少比率的用户会收到影响(在存在5个分片的时候,20%的用户受影响),但是这些用户会再也不会看到他们的数据了。