(1.4)mongodb sh,mongodb分片集群
【1】mongodb 分片集群介绍
转自官网:https://www.mongodb.com/docs/manual/sharding/
MongoDB通过sharding支持水平扩展。
(1.1)分片集群
一个 MongoDB分片集群由以下组件组成:
- shard:每个分片包含分片数据的一个子集。每个分片都可以部署为副本集。
- mongos:
mongos
充当查询路由器,在客户端应用程序和分片集群之间提供接口。从 MongoDB 4.4 开始,mongos
可以支持 对冲读取以最小化延迟。 - config servers:配置服务器存储集群的元数据和配置设置。
下图描述了分片集群中组件的交互:
MongoDB 在集合级别对数据进行分片,将集合数据分布在集群中的各个分片上。
(1.2)分片键
MongoDB 使用分片键将集合的文档分布在分片之间。分片键由文档中的一个或多个字段组成。
- 从版本 4.4 开始,分片集合中的文档可能会缺少分片键字段。在跨分片分布文档时,缺失的分片键字段被视为具有空值,但在路由查询时则不会。有关更多信息,请参阅 缺少分片键字段。
- 在 4.2 及更早的版本中,分片集合的每个文档中都必须存在分片键字段。
您在对集合进行分片时选择分片键。
- 从 MongoDB 5.0 开始,您可以通过更改集合的分片键来重新分片集合。
- 从 MongoDB 4.4 开始,您可以通过向现有分片键添加一个或多个后缀字段来优化分片键。
- 在 MongoDB 4.2 及更早版本中,分片后无法更改分片键的选择。
文档的分片键值决定了它在分片中的分布。
- 从 MongoDB 4.2 开始,您可以更新文档的 shard key 值,除非您的 shard key 字段是不可变
_id
字段。有关详细信息,请参阅 更改文档的分片键值。 - 在 MongoDB 4.0 及更早版本中,文档的分片键字段值是不可变的。
(1.3)分片键索引、分片密钥策略
要对填充的集合进行分片,该集合必须具有 以分片键开头的索引。对空集合进行分片时,如果集合还没有指定分片键的适当索引,则 MongoDB 会创建支持索引。请参阅 分片键索引。
分片键的选择会影响分片集群的性能、效率和可扩展性。具有最佳硬件和基础设施的集群可能会因选择分片键而受到瓶颈。分片键及其后备索引的选择也会影响集群可以使用的分片策略。
提示
也可以看看:
(1.4)块(chunk)、自动平衡器(balance)和均匀的块分布
MongoDB 将分片数据分成块(64M)。每个块都有一个基于 shard key的包含下限和互斥上限。
为了在集群中的所有分片上实现块的均匀分布,平衡器在后台运行以跨分片迁移块。
也就是说,一波数据写入,这部分所有数据一开始只放入到一个分片,完成后自动平衡器在后台运行,慢慢的根据判断平衡一部分数据到其他分片节点
(1.5)分片的优势
读/写
MongoDB在分片集群中的分片之间分配读写工作负载 ,允许每个分片处理集群操作的子集。通过添加更多分片,读写工作负载都可以在集群中水平扩展。
对于包含分片键或复合分片键前缀的查询,mongos
可以将查询定位到特定分片或分片集。这些有针对性的操作通常比 广播到集群中的每个分片更有效。
从 MongoDB 4.4 开始,mongos
可以支持对冲读取以最小化延迟。
存储容量
分片将数据分布在集群中的分片上,允许每个分片包含整个集群数据的一个子集。随着数据集的增长,额外的分片会增加集群的存储容量。
高可用性
将配置服务器和分片部署为副本集提供了更高的可用性。
即使一个或多个分片副本集完全不可用,分片集群也可以继续执行部分读写。也就是说,虽然无法访问不可用分片上的数据,但针对可用分片的读取或写入仍然可以成功。
(1.6)分片前的注意事项
分片集群基础设施要求和复杂性需要仔细规划、执行和维护。
一旦一个集合被分片,MongoDB 就不会提供对一个分片集合进行分片的方法。
虽然您可以稍后重新分片您的集合 ,但请务必仔细考虑您的分片键选择以避免可伸缩性和性能问题。
详细参考:选择一个分片键
要了解对集合进行分片的操作要求和限制,请参阅分片集群中的操作限制。
如果查询不包括分片键或复合分片键的前缀 ,则mongos
执行广播操作,查询 分片集群中的所有分片。这些分散/收集查询可能是长时间运行的操作。
(1.7)分片和非分片集合
数据库可以混合有分片和非分片集合。分片集合被分区并分布在 集群中的分片上。未分片的集合存储在 主分片上。每个数据库都有自己的主分片。
(1.8)连接到分片集群
您必须连接到mongos路由器才能与分片集群中的任何集合进行交互。这包括分片和非分片集合。客户端不应连接到单个分片以执行读取或写入操作。
您可以使用或 MongoDB驱动程序mongos
连接到相同的方式 。mongod
mongosh
【分片策略】
MongoDB 支持两种分片策略,用于跨分片集群分布数据。
(Hashed Sharding)
哈希散列分片涉及计算分片键字段值的散列。然后根据哈希散列的分片键值为每个块分配一个范围。
MongoDB 在使用哈希散列索引解析查询时会自动计算散列值。应用程序不需要计算哈希。
虽然一系列分片键可能“接近”,但它们的散列值不太可能在同一个块上。基于散列值的数据分布有助于更均匀的数据分布,尤其是在分片键单调变化的数据集中。
但是,散列分布意味着对分片键的基于范围的查询不太可能针对单个分片,从而导致更多集群范围的 广播操作
有关详细信息,请参阅散列分片。
(Ranged Sharding)
范围分片涉及根据分片键值将数据划分为范围。然后根据分片键值为每个块分配一个范围。
值“接近”的一系列分片键更有可能驻留在同一个块上。这允许有针对性的操作,因为mongos
可以将操作路由到仅包含所需数据的分片。
远程分片的效率取决于选择的分片键。考虑不周的分片键会导致数据分布不均匀,这可能会抵消分片的一些好处或导致性能瓶颈。有关基于范围的分片,请参阅 分片键选择。
有关详细信息,请参阅远程分片。
(Zones in Sharded Clusters)
区域可以帮助改善跨多个数据中心的分片集群的数据局部性。
在分片集群中,您可以根据shard key创建分片数据区域。您可以将每个区域与集群中的一个或多个分片相关联。一个分片可以与任意数量的区域相关联。在平衡集群中,MongoDB仅将区域覆盖的块迁移到与该区域关联的分片。
每个区域涵盖一个或多个分片键值范围。区域覆盖的每个范围始终包括其下边界,但不包括其上边界。
可以应用区域的一些常见部署模式如下:
- 隔离特定分片集上的特定数据子集。
- 确保最相关的数据驻留在地理上距离应用程序服务器最近的分片上。
- 根据分片硬件的硬件/性能将数据路由到分片。
下图说明了具有三个分片和两个区域的分片集群。
该A
区域表示一个范围,其下限为1
,上限为10
。
该B
区域表示一个范围,其下边界为10
,上边界为20
。
分片Alpha
并Beta
拥有A
区域。ShardBeta
也有该B
区域。
ShardCharlie
没有与之关联的区域。集群处于稳定状态,没有任何块违反任何区域。
在定义要覆盖的区域的新范围时,您必须使用分片键中包含的字段。
如果使用复合分片键,范围必须包含分片键的前缀。有关更多信息,请参阅区域中的分片键。
【2】搭建 mongodb sh 分片集群
(2.0)架构规划
10个实例:38017-38026
(0)mongos:38017
(1)configserver:38018-38020
3台构成复制集(1主2从,不支持 arbiter)38018-38020(复制集名字 configserver)
(2)shard 节点:
sh1: 38021-23 (1主2从,其中一个节点为 arbiter,复制集名字为 sh1 )
sh2: 38024-26 (1主2从,其中一个节点为 arbiter,复制集名字为 sh2 )
(2.1)Shard 节点的搭建
可以支持 PSS(1主2从)、PSA(1主1从+纯投票节点 ARBITER)
<1> 6个节点的文件和配置信息:
# 构造实例目录 mkdir -p /data/mongodb/{38017,38018,38019,38020,38021,38022,38023,38024,38025,38026}/{data,log} useradd mongodb -s /sbin/false # 构造配置文件 cat <<eof >/data/mongodb/38021/mongod.conf systemLog: destination: file path: /data/mongodb/38021/log/mongodb.log logAppend: true storage: journal: enabled: true dbPath: /data/mongodb/38021/data directoryPerDB: true #engine: wiredTiger wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true processManagement: fork: true net: bindIp: 0.0.0.0 port: 38021 replication: oplogSizeMB: 2048 replSetName: sh1
sharding:
clusterRole: shardsvr eof # 构造不同实例的配置文件 cp /data/mongodb/38021/mongod.conf /data/mongodb/38022/mongod.conf cp /data/mongodb/38021/mongod.conf /data/mongodb/38023/mongod.conf cp /data/mongodb/38021/mongod.conf /data/mongodb/38024/mongod.conf cp /data/mongodb/38021/mongod.conf /data/mongodb/38025/mongod.conf cp /data/mongodb/38021/mongod.conf /data/mongodb/38026/mongod.conf sed -i 's#38021#38022#g' /data/mongodb/38022/mongod.conf sed -i 's#38021#38023#g' /data/mongodb/38023/mongod.conf sed -i 's#38021#38024#g' /data/mongodb/38024/mongod.conf sed -i 's#38021#38025#g' /data/mongodb/38025/mongod.conf sed -i 's#38021#38026#g' /data/mongodb/38026/mongod.conf sed -i 's#: sh1#: sh2#g' /data/mongodb/38024/mongod.conf sed -i 's#: sh1#: sh2#g' /data/mongodb/38025/mongod.conf sed -i 's#: sh1#: sh2#g' /data/mongodb/38026/mongod.conf # 启动多个实例 chown -R mongodb:mongodb /data/mongodb chmod -R 775 /data/mongodb su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38021/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38022/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38023/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38024/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38025/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38026/mongod.conf &"
# 查阅
ps -ef|grep mongod
netstat -nltp|grep 3802
# 快速清理
rm -rf /data/mongodb/{38021,38022,38023,38024,38025,38026}/{data,log}/*
<2> 初始化 sh1 sh2 2个复制集
# 构造 sh1 mongo --port 38021 admin config = { _id: 'sh1', members: [ {_id: 0, host: '192.168.191.25:38021'}, {_id: 1, host: '192.168.191.25:38022'}, {_id: 2, host: '192.168.191.25:38023', "arbiterOnly": true}] } rs.initiate(config) // 初始化后,登录上会显示是主还是从 ,如:my_repl:PRIMARY> rs.status() // 查看集群所有描述 rs.isMaster() // 查看集群成员,以及当前节点是否是 rs.conf() // 查看一下集群的配置信息,比如成员的权重、是否是投票节点等等 quit() # 构造 sh2 mongo --port 38024 admin config = { _id: 'sh2', members: [ {_id: 0, host: '192.168.191.25:38024'}, {_id: 1, host: '192.168.191.25:38025'}, {_id: 2, host: '192.168.191.25:38026', "arbiterOnly": true}] } rs.initiate(config) // 初始化后,登录上会显示是主还是从 ,如:my_repl:PRIMARY> rs.status() // 查看集群所有描述 rs.isMaster() // 查看集群成员,以及当前节点是否是 rs.conf() // 查看一下集群的配置信息,比如成员的权重、是否是投票节点等等 quit()
(2.2)configserver 节点配置
可以支持 PSS(1主2从)、不支持 PSA(1主1从+纯投票节点 ARBITER)
<1> 配置文件与信息
# 构造实例目录 mkdir -p /data/mongodb/{38017,38018,38019,38020,38021,38022,38023,38024,38025,38026}/{data,log} useradd mongodb -s /sbin/false # 构造配置文件 cat <<eof >/data/mongodb/38018/mongod.conf systemLog: destination: file path: /data/mongodb/38018/log/mongodb.log logAppend: true storage: journal: enabled: true dbPath: /data/mongodb/38018/data directoryPerDB: true #engine: wiredTiger wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true processManagement: fork: true net: bindIp: 0.0.0.0 port: 38018 replication: oplogSizeMB: 2048 replSetName: configReplSet sharding: clusterRole: configsvr eof # 构造不同实例的配置文件 cp /data/mongodb/38018/mongod.conf /data/mongodb/38019/mongod.conf cp /data/mongodb/38018/mongod.conf /data/mongodb/38020/mongod.confsed -i 's#38018#38019#g' /data/mongodb/38019/mongod.conf sed -i 's#38018#38020#g' /data/mongodb/38020/mongod.conf # 启动多个实例 chown -R mongodb:mongodb /data/mongodb chmod -R 775 /data/mongodb su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38018/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38019/mongod.conf &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/38020/mongod.conf &" # 查阅 ps -ef|grep mongod netstat -nltp|grep 380 # 快速清理 rm -rf /data/mongodb/{38018,38019,38020}/{data,log}/*
<2> 初始化复制集
# 构造 configReplSet mongo --port 38018 admin config = { _id: 'configReplSet', members: [ {_id: 0, host: '192.168.191.25:38018'}, {_id: 1, host: '192.168.191.25:38019'}, {_id: 2, host: '192.168.191.25:38020'}] } rs.initiate(config) // 初始化后,登录上会显示是主还是从 ,如:my_repl:PRIMARY> rs.status() // 查看集群所有描述 rs.isMaster() // 查看集群成员,以及当前节点是否是 rs.conf() // 查看一下集群的配置信息,比如成员的权重、是否是投票节点等等 quit()
(2.3)mongos 节点配置(必须最后做)
用 38017 做该路由节点
cat <<eof >/data/mongodb/38017/mongod.conf systemLog: destination: file path: /data/mongodb/38017/log/mongodb.log logAppend: true processManagement: fork: true net: bindIp: 0.0.0.0 port: 38017 sharding: configDB: configReplSet/192.168.191.25:38018,192.168.191.25:38019,192.168.191.25:38020 eof chown -R mongodb:mongodb /data/mongodb chmod -R 775 /data/mongodb su - mongodb -c "/usr/local/mongodb/bin/mongos --config /data/mongodb/38017/mongod.conf &"
(2.4)分片集群添加节点
连接到一个 mongos ( 192.168.191.25:38017 ) 做如下操作配置:
mongo 192.168.191.25:38017/admin // (1)添加分片 db.runCommand({ addshard: "sh1/192.168.191.25:38021,192.168.191.25:38022,192.168.191.25:38023"}) db.runCommand({ addshard: "sh2/192.168.191.25:38024,192.168.191.25:38025,192.168.191.25:38026"}) // (2)列出所有分片信息 db.runCommand({ listshards: 1 }) // (3)整体状况查看 sh.status()
【3】实际分片的配置使用
分片策略见前文
(3.0)运维查阅使用命令
# (1)构建 库、表分片
// (1.1)打开 test 库的分片功能 use admin db.runCommand( { enablesharding: "autoshard" }) // (1.2)给该集合的 id 列 加个索引(btree) use autoshard db.autotable.ensureIndex({ id: 1 }) // (1.3)配置分片集合,以及分片 Key use admin db.runCommand({ shardcollection: "autoshard.autotable",key: {id: 1} })
sh.shardCollection( "zonedb.zonetab",{order_id: 1}) // 与上面等同
// (1.4)查看数据信息
db.autotable.getShardDistribution() // 表数据在分片节点的分布情况
sh.status() // 所有 分片集群相关情况
// (1.5)删除分片节点(谨慎,严重影响业务)
sh.getBalancerState() // 确认 balance 是否在工作
db.runCommand({removeShard: "sh2"}) // 删除操作一定会立即出发 balance,把数据迁移到其他分片节点
#(2)查看分片集群相关信息
// 查看分片集群、分片节点信息 use admin db.runCommand({ isdbgrid: 1}) // 判断是否是 shard 集群 db.runCommand({ listshards: 1}) // 列出所有分片信息 // 列出开启分配的数据库 use config db.databases.find({"partitioned": true}) db.databases.find() // 列出所有数据库分片情况 // 查看分片的 分片键 db.collections.find().pretty() // 上面所有信息都可以 sh.status() // 列出所有分配信息
(3.1)range 自动分片配置及测试
登录 mongos
mongo --port 38017 admin
原理:所有数据都在主节点,然后在后台自动平衡器会把数据迁移到对应的分片节点;
注意,如果是 range 分区,那么相同的 分区键值会存放到同一个分片节点上去
那么当 range 分区键列如果重复率很高,会导致数据存放非常不均匀;
// (1)打开 test 库的分片功能 use admin db.runCommand( { enablesharding: "autoshard" }) // (2)给该集合的 id 列 加个索引(btree) use autoshard db.autotable.ensureIndex({ id: 1 }) // (3)配置分片集合,以及分片 Key use admin db.runCommand({ shardcollection: "autoshard.autotable",key: {id: 1} }) // (4)构建测试数据 use autoshard for(i=1;i<10000;i++){ db.autotable.insert({"id": i,"name": "zhangsan","age": 70,"date": new Date()})}
db.autotable.getShardDistribution()
// 如下图,我们可以发现,所有数据都在分区2中,但因为只有一个 chunks 所以没有迁移到到其他节点,又因为一个chunks 64MB大小
(3.2)Zones 方式进行 range 手动定制分片
登录 mongos
mongo --port 38017 admin
// (1)打开 test 库的分片功能 use admin db.runCommand( { enablesharding: "zonedb" }) // (2)给该集合的 id 列 加个索引(btree) use zonedb db.zonetab.ensureIndex({ order_id: 1 }) // (3)配置分片集合,以及分片 Key use admin sh.shardCollection( "zonedb.zonetab",{order_id: 1})
// (4)每个分片,添加一个 tag
sh.addShardTag("sh1","zonedb_zonetable_min_500")
sh.addShardTag("sh2","zonedb_zonetable_501_max")
sh.status() // 可以查看到
// (5)把 tag 对应其范围
sh.addTagRange(
"zonedb.zonetab",
{"order_id": MinKey },
{"order_id": 500 },"zonedb_zonetable_min_500"
)
sh.addTagRange(
"zonedb.zonetab",
{"order_id": 501 },
{"order_id": MaxKey},"zonedb_zonetable_501_max"
)
// (6)构建测试数据 use zonedb for(i=1;i<1000;i++){ db.zonetab.insert({"order_id": i,"name": "zhangsan","age": 70,"date": new Date()})}
// (7)查看分片分布
use zonedb db.zonetab.stats()
db.zonetab.getShardDistribution()
(3.3)hash 分片 案例
先登录 mongos,然后操作
mongo --port 38017 admin
// (1)打开 test 库的分片功能 use admin db.runCommand( { enablesharding: "hashdb" }) // (2)给该集合的 id 列 加个索引(btree) use hashdb db.hashtab.ensureIndex({ id: "hashed" }) // (3)配置分片集合,以及分片 Key use admin sh.shardCollection("hashdb.hashtab",{id: "hashed"}) // (4)构建测试数据 use hashdb for(i=1;i<600;i++){ db.hashtab.insert({"id": i,"name": "zhangsan","age": 70,"date": new Date()})}
db.hashtab.getShardDistribution()
// 如下图,插入了600行数据进去,hash分区基本是均匀的
【4】Balancer 自动平衡器介绍
(4.1)Balancer 的触发情况
mongs 的一个重要功能,自动巡查所有 shard 节点上的 chunk 的情况,自动做 chunk迁移;
什么时候工作、触发?
1、自动运行,会检测系统不繁忙的时候做迁移
2、在做节点删除的时候,立即开始迁移工作
3、balancer 可以设置在预定的时间窗口内运行
有时候需要可以关闭和开启 blancer(备份的时候)
sh.stopBalancer()
sh.startBalancer()
(4.2)自定义 Balancer 自动平衡的时间段
# 关于整体的 balancer 策略 use config sh.getBalancerState() sh.setBalancerState( true ) sh.getBalancerWindow() // 操作前,查看 balancer 计划 db.settings.update({_id: "balancer"},{$set:{activeWindow:{start: "3:00" , stop: "5:00"}}},true) // 建议避开高峰期和备份时间 sh.getBalancerWindow() // 操作后,查看 balancer 计划 sh.status() // 这个在 balancer 模块也可以看到 # 关于集合级别的 balancer sh.disableBalancing("students.grades") // 关闭某个库下集合的 balancer sh.enableBalancing("students.grades") // 打开某个库下集合的 balancer db.getSiblingDB("config").collections.findOne({_id: "students.grades"}).noBalance;