当前位置:   article > 正文

【MongoDB 分片集群架构详解】

分片集群架构

MongoDB 分片集群架构详解

1. 分片简介

分片(shard) 是指在将数据进行水平切分之后,将其存储到多个不同的服务器节点上的一种扩展方式。
分片在概念上非常类似于应用开发中的“水平分表”。不同的点在于 MongoDB 本身就自带了分片管理的能力。

1.1 为什么要使用分片

MongoDB 复制集实现了数据的多副本及高可用,但是一个复制集能承载的容量和负载是有限的。
在你遇到下面的场景时,就需要考虑使用分片了:

	-  存储容量需求超出单机的磁盘容量。
	-  活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据,影响性能。
	-  写IOPS超出单个 MongoDB 节点的写服务能力
  • 1
  • 2
  • 3

1.2 垂直扩容(Scale Up)VS 水平扩容(Scale Out)

  1. 垂直扩容:用更好的服务器,提高 CPU 处理核数、内存数、带宽数等。
  2. 水平扩容:将任务分配到多台计算机上。

2. 分片集群架构

MongoDB 分片集群(Sharded Cluster)是对数据进行水平扩展的一种方式。MongoDB 使用分片集群支持大数据集和高吞吐的业务场景。在分片模式下,存储不同的切片数据的节点被称为分片节点,一个分片集群包含了多个分片节点。除了分片节点,集群中还需要一些配置节点、路由节点,以保证分片机制的正常运作。

MongoDB 分片集群概念图

2.1 核心概念

2.1.1 数据分片

分片用于存储真正的数据,并提供最终的数据读写访问。分片仅仅是一个逻辑的概念,它可以是一个单独的 MongoDB 实列,也可以是一个复制集。图中的 Shard 1、Shard 2 都是复制集分片。在生产环境中一般会使用复制集的方式,这是为了防止数据节点出现单点故障

2.1.2 配置服务器(config Server)

配置服务器包含多个节点,并组成一个复制集结构,对应于图中的 ConfigReplSet。配置复制集中保存了整个分片集群中的元数据,其中包含各个集合的分片策略,以及分片的路由表等。

2.1.3 查询路由(mongos)

mongos 是分片集群的访问入口,其本身并不持久化数据。mongos 启动之后,会从配置服务器中加载元数据,之后 mongos 开始提供访问服务,并将用户的请求正确路由到对应的分片。在分片集群中可以部署多个 mongos 以分担客户端的压力。

3. 环境搭建

3.1 分片集群搭建

分片集群搭建

3.2 使用 mtools 搭建分片集群

使用 mtools 搭建分片集群

4. 使用分片集群

为了使集合支持分片,需要先开启 database 的分片功能

sh.enableSharding("shop")
  • 1

执行 shardCollection 命令,对集合执行分片初始化

sh.shardCollection("shop.product",{productId:"hashed"},false,{numInitialChunks:4})
  • 1

shop.product集合将productId作为分片键,并采用了哈希分片策略,除此以外,
“numInitialChunks:4”表示将初始化4个chunk。 numInitialChunks必须和哈希分片策略配合使
用。而且,这个选项只能用于空的集合,如果已经存在数据则会返回错误。

4.1 向分片集合写入数据

向 shop.product 集合写入一批数据

db=db.getSiblingDB("shop");
var count=0;
for(var i=0;i<1000;i++){
 var p=[];
 for(var j=0;j<100;j++){
 p.push({
 	"productId":"P-"+i+"-"+j,
 	name:"羊毛衫",
 	tags:[
 		{tagKey:"size",tagValue:["L","XL","XXL"]},
 		{tagKey:"color",tagValue:["蓝色","杏色"]},
 		{tagKey:"style",tagValue:"韩风"}
 		]
 	});
 }
 count+=p.length;
 db.product.insertMany(p);
 print("insert ",count)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4.2 查询数据的分布

db.product.getShardDistribution()
  • 1

数据分布

5. 分片策略

通过分片功能,可以将一个非常大的集合分散存储到不同的分片上,如图所示:
分片策略
假设这个集合大小是1TB,那么拆分到4个分片上之后,每个分片存储256GB的数据。这个当然是最理
想化的场景,实质上很难做到如此绝对的平衡。一个集合在拆分后如何存储、读写,与该集合的分片
策略设定是息息相关的。在了解分片策略之前,我们先来介绍一下chunk。

5.1 什么是chunk

chunk 的意思是数据块,一个 chunk 代表了集合中的 “一段数据”,例如,用户集合(db.users)在切分成多个 chunk 之后如图所示:
chunk-users
chunk 所描述的是范围区间,例如,db.users 使用了 userId 作为分片建,那么 chunk 就是 userId 的各个值(或者哈希值)的连续区间。在集群操作分片集合时,会根据分片建找到对应的 chunk ,并向该 chunk 所在的分片发起操作请求, 而 chunk 的分布在一定程度上会影响数据的读写路径,这由以下两点决定:

	1. chunk 的切分方式,决定如何找到数据所在的 chunk。
	2. chunk 的分布状态,决定如何找到 chunk 所在的分片。
  • 1
  • 2

5.2 分片算法

chunk 切片是根据分片策略进行实施的,分片策略的内容包括分片键和分片算法。 当前 MongoDB 支持两种分片算法:

5.2.1 范围分片

假设集合根据 x 字段来进行分片,x 的完整取值范围为 [ minKey , maxKey ] (x 为整数,这里 minKey、maxKey 为整数的最小值 和 最大值),其将整个取值范围划分为多个 chunk ,例如:

1. chunk1 包含 x 的取值在 [ minKey, 75 ] 的所有文档。
2. chunk2 包含 x 的取值在 [ -75 , 25 ] 直接的所有文档,以此类推。
  • 1
  • 2

分片策略-范围分片
范围分片能很好地满足范围查询的需求。
比如向查询 x 的值在 [ -30 , 10 ] 之间的所有文档,这时 mongos 直接将请求定位到 chunk2 所在分片的服务器,就能查询出所有符合条件的文档。

范围分片的缺点在于:如果 Shard Key 有明显递增(或者递减)趋势,则新插入的文档分布到同一个 chunk ,此时写压力会集中到一个节点,从而导致单点的性能瓶颈。

一些常见的导致递增的 Key 如下:

1. 时间值
2. ObjectId,自动生成的 _id 由时间、计数器组成
3. UUID,包含系统时间、时钟序列
4. 自增整数序列
  • 1
  • 2
  • 3
  • 4
5.2.2 哈希分片(hash sharding)

哈希分片会事先根据分片键计算出一个新的哈希值(64为整数),再根据哈希值按照范围分片的策略进行 chunk 的切分。
适用于日志、物联网等高并发场景。
哈希分片策略
哈希分片于范围分片是互补的。
由于哈希算法确保了随机性,所有文档可以更加离散地分布到多个 chunk 上,这避免了集中写问题。然而,在执行一些范围查询时,哈希分片并不是高效的。
因为所有的范围查询都必然导致对所有 chunk 进行检索,如果集群有 10 个分片,那么 mongos 将需要对 10 个分片分发查询请求。哈希分片与范围分片的另一个区别是:
哈希分片只能选择单个字段,而范围分片允许采用组合式的多字段作为分片键。

哈希分片仅支持单个字段的哈希分片

{ x : "hashed" }
{x : 1 , y : "hashed"} // 4.4 new
  • 1
  • 2

4.4 以后的版本,可以将单个字段的哈希分片和一个到多个的范围分片键字段来进行组合,比如指定 x:1,y 是哈希的方式。

5.2.3 分片标签

MongoDB 允许通过分片添加标签(tag) 的方式来控制数据分发。
一个标签可以关联到多个分片区间(RagRange)。均衡器会优先考虑 chunk 是否正处于某个分片区间上(被完全包含),如果是则会将 chunk 迁移到分片区间所关联的分片,否则按一般情况处理。

分片标签适用于一些特定的场景。例如,集群中可能同时存在 OLTP 和 OLAP 处理,一些系统日志的重要性相对较低,而且主要以少量的统计为主。为了便于单独扩展,我们可能希望将日志与实时类的业务数据分开,此时就可以使用标签。

为了让分片拥有指定的标签,需要执行 addShardTag 命令

	sh.addShardTag("shard01","oltp")
	sh.addShardTag("shard02","oltp")
	sh.addShardTag("shard03","olap")
  • 1
  • 2
  • 3

实时计算的集合应该属于 OLTP 标签,声明 TagRange

sh.addTagRange("main.devices",{shardKey:MinKey},{shardKey:MaxKey},"oltp")
  • 1

而离线计算的集合,则属于 OLAP 标签

sh.addTagRange("other.systemLogs",{shardKey:MinKey},{shardKey:MaxKey},"olap")
  • 1

main.devices 集合将被均衡地分发到 Shard01、Shard02 分片上,而 other.systemLogs 集合将被单独分发到 Shard03 分片上。

5.2.4 分片键(ShardKey)的选择

在选择分片键时,需要根据业务的需求及范围分片、哈希分片的不同特点进行权衡。一般来说,在设计分片键时需要考虑的因素包括:

  1. 分片键的基数(cardinality),取值基数越大越有利于扩展。

     	1.1 以性别作为分片键 :数据最多被拆分为 2 份
     	1.2 以月份作为分片键 :数据最多被拆分为 12 份
    
    • 1
    • 2
  2. 分片键的取值分布应该尽可能均匀。

  3. 业务读写模式,尽可能分散写压力,而读操作尽可能来自一个或少量的分片。

  4. 分片键应该能适应大部分的业务操作。

5.2.5 分片键(shardKey)的约束

shardKey 必须是一个索引。非空集合须在 ShardCollection 前创建索引;空集合 ShardCollection 自动创建索引
4.4 版本之前:

1. shardKey 大小不能超过 512 Bytes;
2. 仅支持单字段的哈希分片键;
3. Document 中必须包含 ShardKey;
4. ShardKey 包含的 Field 不可用修改。
  • 1
  • 2
  • 3
  • 4

4.4 版本之后:

1. ShardKey 大小无限制;
2. 支持复合哈希分片键;
3. Document 中可以不包含 ShardKey,插入时被当做 NULL 处理;
4. 为 ShardKey 添加后缀 refineCollectionShardKey 命令,可以修改 ShardKey 包括的 Field。
  • 1
  • 2
  • 3
  • 4

而在 4.2 版本之前,ShardKey 对应的值不可以修改;4.2 版本之后,如果 ShardKey 为非 _id 字段,那么可以修改 ShardKey 对应的值。

6. 数据均衡

6.1 均衡的方式

一种理想的情况是,所有加入的分片都发挥了相当的作用,包括提供更大的存储容量,以及读写访问性能。因此,为了保证分片集群的水平扩展能力,业务数据应当尽可能地保持均匀分布。这里的均匀性包含以下两个方面:

1.	所有的数据应均匀地分布于不同的 chunk 上。
2.	每个分片上的 chunk 数量尽可能是相近的。
  • 1
  • 2

其中,第一点有业务场景和分片策略来决定,而关于第2点,我们有以下两种选择

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/533608
推荐阅读
  

闽ICP备14008679号