概述
分片,Sharding,将数据拆分至不同数据节点的方式,MongoDB高可用集群的一种形式;需存储海量数据时,一台机器满足不了需求,单台机器读写吞吐性能也会是瓶颈。Sharding技术通过在多台机器上分割数据,使得系统能存储和处理海量数据。
分片的原因
- 复制所有的写入操作到主节点
- 延迟的敏感数据会在主节点查询
- 单个副本集限制在12个节点
- 当请求量巨大时会出现内存不足
- 本地磁盘不足
- 垂直扩展价格昂贵
框架图
三个主要组件(三类节点):
- Shard:数据节点,用于存储实际的数据块,分片节点可以是一个普通的数据节点也可以是一个副本集。实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
- Config Server:配置节点,mongod实例,存储分片的配置信息,包括整个Cluster Metadata、由哪些分片、chunk信息等
- Query Routers:路由节点,客户端由此接入,由该路由节点路由至不同的数据节点,整个集群看上去像单一数据库,前端应用可以透明使用。
高可用集群
一般来说,MongoDB高可用集群有3种部署形式:
- Master Slave 模式
- Replica Set 副本集模式
- Sharding 模式
Master Slave
分布式系统最常见的部署模式,有一主一从和一主多从形式,常用于热备、读写分离。
Master节点可读可写,通过MongoDB Oplog定时将Master节点接收到的写操作同步到所有Salve节点;所有Slave 从 Master 同步数据,从节点之间不感知。
简单示意图
搭建 Master-Slave 模式:
启动 Master 节点:mongod --master --dbpath /data/masterdb/
启动 Slave 节点:mongod --slave --source <masterhostname><:<port>> --dbpath /data/slavedb/
,--source
:指定数据的复制来源,也就是 Master 的地址;
这种读写模式有个数据一致性的问题,毕竟同步不是实时的,适用于对数据一致性要求不严格的场景。
MongoDB 3.6 起已不推荐使用主从模式,自 MongoDB 3.2 起,分片群集组件已弃用主从复制。因为 Master-Slave 其中 Master 宕机后不能自动恢复,只能靠人为操作,可靠性也差,操作不当就存在丢数据的风险。
Replica Set
包含三类节点角色:
- Primary
主节点,只有 Primary 可读可写,接收所有的写请求,然后把数据同步到所有 Secondary 。一个 Replica Set 只有一个 Primary 节点,Primary 挂掉后,其他 Secondary 及 Arbiter 节点会重新选举出来一个 Primary 节点,继续提供服务。
读请求默认是发到 Primary 节点处理,客户端可配置读 Secondary 节点。 - Secondary
数据副本节点,当主节点挂掉的时候,参与选主。Secondary 相互有心跳,Secondary 可以作为数据源,Replica 可以是一种链式的复制模式。 - Arbiter
仲裁者,不存数据,不会被选为主,只进行选主投票。可减轻在减少数据的冗余备份,又能提供高可用的能力。
功能: - 读写分离,数据多副本
- 故障自动恢复
- 节点之间保持心跳请求应答,可感知集群的整体状态
由于Secondary需要和其他若干个Secondary节点保持心跳信息,即,每两个Secondary节点之间都互有心跳,如何维护这个心跳信息呢?不难猜到,Secondary节点数量不宜过大。
Sharding
MongoDB长于海量存储,而数据量的增长是没有上限。参考关系型数据库在应对海量数据的纵向扩展(升级机器配置)和横向扩展(增加机器数量),其中横向扩展又包括水平和垂直维度的分库分表。
Sharding是横向扩展的思路。基于图1 Sharding架构图,不难发现Sharding模式是架构在前面提到的Replica Set模式基础上,一个Shard就是一个Replica Set集群。理论上,Replica Set 的集群的个数是可以无限增长的。
原理
Sharding模式下,数据如何选择某个Shard进行存储呢?即分片算法。
Chunks
MongoDB将数据分割到Chunks。每个Chunk有一个基于分片键的左闭右开的区间。分片Shard可以含有多个Chunks。
MongoDB 使用分片集群平衡器在分片集群中迁移Chunks,目地是Chunks在分片集群中均匀分布。
不能移动文件数超过 250000 或超过 设置的 chunk 大小 / 平均文件大小 1.3 倍的 chunk。
分片键
MongoDB 根据分片键分割 collection 中的文档。
MongoDB中提供自动分片的方式,会根据数据块(chunk)大小的设定,对片键进行拆分;
分片算法
Chunk切分根据分片策略进行实施,分片策略的内容包括分片键和分片算法。
MongoDB支持的分片算法:
- 范围分片(range sharding)
假设集合根据A字段(如订单号)来分片,需提前知道A字段的范围(假如是0~100
),然后设置Chunk数量N(假如设置为4),集合存储到具体某个Chunk,如A字段的范围区间为[0-25)
的集合会存储到Chunk1。优点:能很好地满足范围查询的需求。缺点:如果Shard Key有明显递增、递减趋势,则新插入的文档会分布到同一个chunk,读写压力会集中到一个节点,从而导致单点的性能瓶颈。 - 哈希分片(hash sharding)
事先根据分片键计算出一个新的哈希值(64位整数),再根据哈希值按照范围分片的策略进行chunk的切分。hash算法可保证随机性,文档可以离散地分布到多个Chunk上,能够一定程度上避免范围分片的缺陷。缺点:在执行范围查询时,因为所有的范围查询都必然导致对所有Chunk进行检索,拿到全部Chunk的检索后再聚合返回到客户端。
另一个区别:哈希分片只能选择单个字段,而范围分片允许采用组合式的多字段作为分片键。
MongoDB 4.4+版本,可将单个字段的哈希分片和一个到多个的范围分片键字段来进行组合,比如指定 x:1,y 是哈希的方式。
分片键选择:
- 分片键基数取值越大越有利于扩展
- 分片键的取值分布应该尽可能均匀
- 业务读写模式,尽可能分散写压力,而读操作尽可能来自一个或少量的分片
- 分片键应该能适应大部分的业务操作
部署实战
在单机环境下,设置分片结构端口分布如下:
Shard Server 1:27020
Shard Server 2:27021
Shard Server 3:27022
Shard Server 4:27023
Config Server :27100
Route Process:40000
步骤一:启动Shard Server
mkdir -p /www/mongoDB/shard/s0
mkdir -p /www/mongoDB/shard/s1
mkdir -p /www/mongoDB/shard/s2
mkdir -p /www/mongoDB/shard/s3
mkdir -p /www/mongoDB/shard/log
/usr/local/mongoDB/bin/mongod --port 27020 --dbpath=/www/mongoDB/shard/s0 --logpath=/www/mongoDB/shard/log/s0.log --logappend --fork
# 省略
步骤二: 启动Config Server
mkdir -p /www/mongoDB/shard/config
/usr/local/mongoDB/bin/mongod --port 27100 --dbpath=/www/mongoDB/shard/config --logpath=/www/mongoDB/shard/log/config.log --logappend --fork
像启动普通mongodb服务一样启动,不需要添加—shardsvr
和configsvr
参数。因为这两个参数的作用就是改变启动端口
步骤三: 启动Route Process
/usr/local/mongoDB/bin/mongos --port 40000 --configdb localhost:27100 --fork --logpath=/www/mongoDB/shard/log/route.log --chunkSize 500
mongos启动参数中,chunkSize这一项是用来指定chunk的大小的,默认200MB。
步骤四: 配置Sharding
使用MongoDB Shell登录到mongos,添加Shard节点
/usr/local/mongoDB/bin/mongo admin --port 40000
connecting to: 127.0.0.1:40000/admin
mongos> db.runCommand({ addshard:"localhost:27020" })
{ "shardAdded" : "shard0000", "ok" : 1 }
......
mongos> db.runCommand({ addshard:"localhost:27029" })
{ "shardAdded" : "shard0009", "ok" : 1 }
mongos> db.runCommand({ enablesharding:"test" }) #设置分片存储的数据库
{ "ok" : 1 }
mongos> db.runCommand({ shardcollection: "test.log", key: { id:1,time:1}})
{ "collectionsharded" : "test.log", "ok" : 1 }
步骤五: 程序代码内无需太大更改,直接按照连接普通的mongo数据库那样,将数据库连接接入接口40000
编程
使用Sharding模式后,应用层编码有些注意事项:
- 要使用 mapReduce 和 aggregrate 代替 group
- db.eval() 不向下兼容
- $where 不允许引用 db 对象
- 不支持 geoSearch
- 对分片的 collection 进行 update() 或者 remove() 操作必须包含分片键或者 _id,否则会报错
参考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/142088.html