分片

将数据拆分,将其分散存储在不同的机器上的过程。

理解集群的组件

MongoDB 的分片允许你创建一个包含很多机器(分片)的集群,将数据子集分散在集群中,每个分片维护着一个数据集合的子集。与单机架构比集群可以使应用程序具有更大的数据处理能力。在分片之前要进行一次路由过程,应用程序只需要连接到路由服务器,就可以像单机服务器一样进行正常的请求。

快速建立一个简单的集群

  1. $ mongo --nodb
  2. > cluster = new ShardingTest({"shards":3,"chunksize":1})

对集合启用分片

  1. > sh.enableSharding("test")

对集合分片时,要选择一个片键。片键是集合的一个键,MongoDB 根据这个键拆分数据。随着集合的不断增长,片键就会成为集合上最重要的索引。只有被索引过的键才能够作为片键。分片之前,集合实际上是一个数据块。分片依据片键将集合分为多个数据块,这些数据块被分布在集群的每个分片上。

包含分片的查询能够直接发送到目标分片或者是集群分片的一个子集,这样的查询叫做定向查询。有一些查询必须被发送到所有分片,这样的查询叫做分散-聚合查询。mongos 将查询分散到所有分片上,然后将各个分片的查询结果聚集起来。

何时分片

分片用来:

  • 增加可用 RAM

  • 增加可用磁盘空间

  • 减轻单台服务器的负载

  • 处理单个 mongod 无法承载的吞吐量

由于分片过少会造成系统性能下降,因此至少创建 3 个或以上的分片。

拆分块

mongos 会记录在每个块中插入了多少数据,一旦达到某个阈值,就会检查是否需要对块进行拆分。如果这个块确实需要拆分,mongos 就会在配置服务器上更新这个块的元信息。块拆分只需要改变元数据即可,而无需进行数据移动。尽可能保证 mongos 进程可用,mongos 进程的波动会使得无法达到阀值点,造成运行开销过大。

均衡器

负责数据转移。周期性的检查分片间是否存在不均衡,如果存在,则会开始块的迁移。每个 mongos 有时也会扮演均衡器的角色。如果有一些集合达到了均衡阀值,均衡器开始数据迁移。它会从负载比较大的分片中选择一个块,并询问改块是否需要的迁移之前对其拆分。完成必要的拆分后,就会迁移至块数量较少的机器上。

选择片键

检查使用情况

对数据进行分片时,要选择一两个字段用于拆分数据,这个键叫做片键。一旦拥有多个分片,就不能再修改片键,所以选择合适的片键非常重要。根据不同的需求对片键进行评估,并选择判断所选片键是否能使用自己的情况。

数据分发

拆分数据常用的分发方式有:升序分键、随机分发的片键和基于位置的片键。

升序片键

每个新创建的文档都会被分配到最大块中。所有的写请求都会被路由到一个分片,这个分片随着数据的写入,不断拆分出新的小块。

随机分发的片键

随着更多数据的写入,数据的随机性使得,新插入的数据会比较均衡地分发在不同的块中。由于数据的写入是随机分发的,各分片增长的速度大致相同,这就减少了需要进行迁移的次数。弊端在于 MongoDB 在随机访问超出 RAM 大小的数据时效率不高。

基于位置的片键

位置片键不必与实际的物理位置字段相关,它可以是 IP、经纬度、或者是地址,数据会根据这个“位置”进行分组。所有与改键比较接近的文档会被保存在同一范围的块中。

片键策略

散列片键

如果追求的是速度的极致,那么散列片键是最佳选择。散列片键可使其他任何键随机分发,因此在大量查询中使用升序建,但同时又希望写入数据随机分发的话,散列是非常好的选择。无法使用散列键做具体目标的范围查询。

  1. > db.users.ensureIndex({"username":"hashed"}); // 创建散列索引
  2. > sh.shardCollection("app.users",{"username":"hashed"}); // 对集合分片

GridFS 的散列片键

GridFS 将文件拆分为块,而分片将集合拆分为块。GridFS 集合包含大量的文件数据,所以它适合做分片。

流水策略

当有一些服务器比其他服务器更强大,我们希望它承担更多的负载。例如可以将 10 个分片强制所有数据插入到 SSD,然后让均衡器将旧的块移动到其他分块上。

片键规则和指导方案

片键限制

片键不能是数组,文档一旦插入,片键无法被修改。不能在地理空间索引上进行分片。

片键的势

分片在势比较高的字段上性能更佳。复合键的势比较高。

参考

[1] MongoDB权威指南