资料总结 资料总结
首页
go
java
云原生
  • mysql
  • redis
  • MongoDB
  • 设计模式详解
  • 数据结构与算法
  • 前端
  • 项目
  • 理论基础
  • 运营
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

linghui Wu

一只努力学飞的鱼
首页
go
java
云原生
  • mysql
  • redis
  • MongoDB
  • 设计模式详解
  • 数据结构与算法
  • 前端
  • 项目
  • 理论基础
  • 运营
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据库表设计
  • mysql

  • redis

  • MongoDB

    • MongoDB使用
    • MongoDB索引
    • MongoDB集群
      • PSS模式(官方推荐模式)
      • PSA模式
      • 单机
      • 注意事项
        • 搭建
      • 安全认证
      • 连接方式
      • 成员角色属性
        • Priority
        • Vote
      • 成员角色
        • Primary
        • Secondary
        • Arbiter
      • 成员配置
      • 高可用
        • 选举
        • 自动故障转移
        • 备节点是怎么感知到主节点已经发生故障的?
        • 如何降低故障转移对业务产生的影响?
        • 如何优雅的重启复制集?
        • 复制集数据同步机制
      • oplog
      • 查看oplog
      • 幂等性
      • oplog的写入被放大,导致同步追不上
      • 编码注意事项
      • 复制延迟
      • 数据回滚
      • 同步源选择
        • 分片集群
      • 应用场景
      • 核心概念/角色
      • 分片集群搭建
      • 使用分片集群
      • 分片策略
        • chunk (数据块)
        • 分片算法
        • 分片键(ShardKey)的选择
        • 分片键(ShardKey)的约束
      • 分片标签
      • 数据均衡
        • 手动均衡
        • 自动均衡
        • chunk分裂
        • 数据均衡带来的问题
        • 两地三中心集群架构
        • 全球多写集群架构
  • InfluxDB
  • 数据库
  • MongoDB
wulinghui
2022-04-24
目录

MongoDB集群

# 复制集架构

所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持复制集内所有成员存储相同的数据集

# PSS模式(官方推荐模式)

PSS模式由一个主节点和两个备节点所组成,即Primary+Secondary+Secondary。

# PSA模式

PSA模式由一个主节点、一个备节点和一个仲裁者节点组成,即Primary+Secondary+Arbiter

仲裁者/Arbiter节点不存储数据副本,也不提供业务的读写操作。Arbiter节点发生故障不影响业务,仅影响选举投票。

# 单机

只有一台服务器,也要以单节点模式启动复制集

  • 单机多实例启动复制集
  • 单节点启动复制集

# 注意事项

  • 因为正常的复制集节点都有可能成为主节点,它们的地位是一样的,因此硬件配置上必须一致;
  • 为了保证节点不会同时宕机,各节点使用的硬件必须具有独立性。 也就是不会有互相影响。如共享ssd
  • 复制集各节点软件版本必须一致,以避免出现不可预知的问题。
  • 增加节点不会增加系统写性能,写性能只能是配置、jonmal、单机硬件。

# 搭建

复制集通过replSetInitiate命令 或mongo shell的rs.initiate()进行初始化

# mongo --port 28017
# 初始化复制集
> rs.initiate() 
# 将其余成员添加到复制集
> rs.add("192.168.65.174:28018") 
> rs.add("192.168.65.174:28019")
# 查看复制集整体状态:
> rs.status()
# 查看当前节点角色
> db.isMaster()


rs.secondaryOk() 为当前的连接设置 从节点可读
rs.reconfig() 通过重新应用复制集配置来为复制集更新配置
rs.conf() 返回复制集配置信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 安全认证

use admin
#创建用户
db.createUser( {
    user: "fox",
    pwd: "fox",
     roles: [ { role: "clusterAdmin", db: "admin" } ,
         { role: "userAdminAnyDatabase", db: "admin"},
         { role: "userAdminAnyDatabase", db: "admin"},
         { role: "readWriteAnyDatabase", db: "admin"}]
})

#mongo.key采用随机算法生成,用作节点内部通信的密钥文件。
openssl rand -base64 756 > /data/mongo.key

#权限必须是600
chmod 600 /data/mongo.key 

# 创建keyFile前,需要先停掉复制集中所有主从节点的mongod服务,然后再创建,否则有可能出现服务启动不了的情况。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 连接方式

mongodb://userName:passWord@ip:port,ip2:port2,ip3:port3/test?authSource=admin&replicaSet=rs0

# 成员角色属性

# Priority

当 Priority 等于 0 时,它不可以被复制集选举为主,Priority 的值越高,则被选举为主的概率更大

# Vote

当 Vote 等于 0 时,不可以参与选举投票,此时该节点的 Priority 也必须为 0,即它也不能被选举为主。

由于一个复制集中最多只有7个投票成员,因此多出来的成员则必须将其vote属性值设置为0,即这些成员将无法参与投票。

# 成员角色

# Primary

主节点,其接收所有的写请求,然后把修改同步到所有备节点。

# Secondary

备节点,与主节点保持同样的数据集。当主节点“挂掉”时,参与竞选主节点。

    • Hidden = false:正常的只读节点,是否可选为主,是否可投票,取决于 Priority,Vote 的值;
    • Hidden = true:隐藏节点,对客户端不可见, 可以参与选举,但是 Priority 必须为 0,即不能被提升为主。由于隐藏节点不会接受业务访问,因此可通过隐藏节点做一些数据备份、离线计算的任务,这并不会影响整个复制集。
    • Delayed :延迟节点,必须同时具备隐藏节点和Priority0的特性,会延迟一定的时间(SlaveDelay 配置决定)从上游复制增量,常用于快速回滚场景。

# Arbiter

仲裁节点,只用于参与选举投票,本身不承载任何数据,只作为投票角色。

# 成员配置

  • 配置隐藏节点
  • 配置延时节点
  • 查看复制延迟
  • 添加投票节点
  • 移除复制集节点
  • 更改复制集节点

# 高可用

# 选举

选举使用Raft算法,选举成功的必要条件是大多数投票节点存活。

一个复制集最多可以有50个成员,但只有7个投票成员。(这是因为一旦过多的成员参与数据复制、投票过程,将会带来更多可靠性方面的问题。)

原理扩展:

  • 支持chainingAllowed链式复制,即备节点不只是从主节点上同步数据,还可以选择一个离自己最近(心跳延时最小)的节点来复制数据。
  • 增加了预投票阶段,即preVote,这主要是用来避免网络分区时产生Term(任期)值激增的问题
  • 支持投票优先级,如果备节点发现自己的优先级比主节点高,则会主动发起投票并尝试成为新的主节点。

当复制集内存活的成员数量不足大多数时,整个复制集将无法选举出主节点,此时无法提供写服务,这些节点都将处于只读状态。此外,如果希望避免平票结果的产生,最好使用奇数个节点成员,比如3个或5个。当然,在MongoDB复制集的实现中,对于平票问题已经提供了解决方案:

  • 为选举定时器增加少量的随机时间偏差,这样避免各个节点在同一时刻发起选举,提高成功率。
  • 使用仲裁者角色,该角色不做数据复制,也不承担读写业务,仅仅用来投票。

# 自动故障转移

# 备节点是怎么感知到主节点已经发生故障的?

一个影响检测机制的因素是心跳,在复制集组建完成之后,各成员节点会开启定时器,持续向其他成员发起心跳。

另一个重要的因素是选举超时检测,一次心跳检测失败并不会立即触发重新选举。

触发选举的条件是:

  1. 当前节点是备节点。
  2. 当前节点具备选举权限。
  3. 在检测周期内仍然没有与主节点心跳成功。

# 如何降低故障转移对业务产生的影响?

  • 在复制集发生主备节点切换的情况下,会出现短暂的无主节点阶段,此时无法接受业务写操作。
  • 如果主节点属于强制掉电,那么整个故障转移过程将会变长
  • 对于非常重要的业务,建议在业务层面做一些防护策略,比如设计重试机制。

# 如何优雅的重启复制集?

  1. 逐个重启复制集里所有的Secondary节点
  2. 对Primary发送rs.stepDown()命令,等待primary降级为Secondary
  3. 重启降级后的Primary

# 复制集数据同步机制

主节点与备节点之间是通过oplog来同步数据的,这里的oplog是一个特殊的固定集合,当主节点上的一个写操作完成后,会向oplog集合写入一条对应的日志,而备节点则通过这个oplog不断拉取到新的日志,在本地进行回放以达到数据同步的目的。

# oplog

  • MongoDB oplog 是 Local 库下的一个集合,用来保存写操作所产生的增量日志(类似于 MySQL 中 的 Binlog)。
  • 它是一个 Capped Collection(固定集合),即超出配置的最大值后,会自动删除最老的历史数据,MongoDB 针对 oplog 的删除有特殊优化,以提升删除效率。
  • 主节点产生新的 oplog Entry,从节点通过复制 oplog 并应用来保持和主节点的状态一致;

# 查看oplog

use local
db.oplog.rs.find().sort({$natural:-1}).pretty()
1
2

# 幂等性

每一条oplog记录都描述了一次数据的原子性变更,对于oplog来说,必须保证是幂等性的。

幂等性的代价 : 有些指令会在内部被转化,如 数组的$push/$pull/$addToSet操作被转换为了$set操作、$inc 转化为 $set。

# oplog的写入被放大,导致同步追不上

生产环境案例:

用户的文档内包含一个很大的数组字段,1000个元素总大小在64KB左右,这个数组里的元素按时间反序存储,新插入的元素会放到数组的最前面($position: 0),然后保留数组的前1000个元素($slice: 1000)。

上述场景导致,Primary上的每次往数组里插入一个新元素(请求大概几百字节),oplog里就要记录整个数组的内容,Secondary同步时会拉取oplog并重放,Primary到Secondary同步oplog的流量是客户端到Primary网络流量的上百倍,导致主备间网卡流量跑满,而且由于oplog的量太大,旧的内容很快被删除掉,最终导致Secondary追不上,转换为恢复/RECOVERING状态。

# 编码注意事项

  1. 数组的元素个数不要太多,总的大小也不要太大
  2. 尽量避免对数组进行更新操作
  3. 如果一定要更新,尽量只在尾部插入元素,复杂的逻辑可以考虑在业务层面上来支持

# 复制延迟

如果备节点的复制不够快,就无法跟上主节点的步伐,从而产生复制延迟(replication lag)问题。这是不容忽视的,一旦备节点的延迟过大,则随时会发生复制断裂的风险,这意味着备节点的optime(最新一条同步记录)已经被主节点老化掉,于是备节点将无法继续进行数据同步。

注意: 复制延迟是不可避免的,这意味着主备节点之间的数据无法保持绝对的同步

措施:

  • 增加oplog的容量大小,并保持对复制窗口的监视。
  • 通过一些扩展手段降低主节点的写入速度。
  • 优化主备节点之间的网络。
  • 避免字段使用太大的数组(可能导致oplog膨胀)。

# 数据回滚

当复制集中的主节点宕机时,备节点会重新选举成为新的主节点。那么,当旧的主节点重新加入时,必须回滚掉之前的一些“脏日志数据”,以保证数据集与新的主节点一致。

措施:

  • 应用上可以通过设定更高的写入级别(writeConcern:majority)来保证数据的持久性。
  • 当rollback发生时,MongoDB将把rollback的数据以BSON格式存放到dbpath路径下rollback文件夹中,后期可以手工补充回来。

# 同步源选择

MongoDB是允许通过备节点进行复制的(以减轻主节点的压力),这会发生在以下的情况中:

  • 在settings.chainingAllowed开启的情况下,备节点自动选择一个最近的节点(ping命令时延最小)进行同步。settings.chainingAllowed选项默认是开启的,也就是说默认情况下备节点并不一定会选择主节点进行同步,这个副作用就是会带来延迟的增加,你可以通过下面的操作进行关闭:

​ cfg = rs.config() cfg.settings.chainingAllowed = false rs.reconfig(cfg)

  • 使用replSetSyncFrom命令临时更改当前节点的同步源,比如在初始化同步时将同步源指向备节点来降低对主节点的影响。

​ db.adminCommand( { replSetSyncFrom: "hostname:port" })

# 分片集群

分片(shard)是指在将数据进行水平切分之后,将其存储到多个不同的服务器节点上的一种扩展方式。

在分片模式下,存储不同的切片数据的节点被称为分片节点。

# 应用场景

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

# 核心概念/角色

  • 数据分片 : 用于存储真正的数据,并提供最终的数据读写访问。可以是单独的mongod实例,也可以是一个复制集。在生产环境中也一般会使用复制集的方式,这是为了防止数据节点出现单点故障。
  • 配置服务器(Config Server): 配置复制集中保存了整个分片集群中的元数据,其中包含各个集合的分片策略,以及分片的路由表等。 也可以是一个复制集结构。
  • 查询路由(mongos) : 分片集群的访问入口,其本身并不持久化数据,将用户的请求正确路由到对应的分片。在分片集群中可以部署多个mongos以分担客户端请求的压力。

# 分片集群搭建

  • 搭建数据分片节点
  • 搭建 mongos
  • mongos加入第1个分片
  • 创建分片集合
  • mongos加入第2/3....个分片

# 使用分片集群

  • 先开启database的分片功能
  • 执行shardCollection命令,对集合执行分片初始化
  • 向分片集合写入数据

# 分片策略

# chunk (数据块)

集群在操作分片集合时,会根据分片键找到对应的chunk,并向该chunk所在的分片发起操作请求。

# 分片算法

chunk切分是根据分片策略进行实施的,分片策略的内容包括分片键和分片算法。

MongoDB支持两种分片算法:范围分片 、哈希分片、范围和哈希分片组合

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

哈希分片,由于哈希算法保证了随机性,所以文档可以更加离散地分布到多个chunk上,这避免了集中写问题。

哈希分片但是只能选择单个字段,而范围分片允许采用组合式的多字段作为分片键。

# 分片键(ShardKey)的选择

  • 分片键的基数(cardinality),取值基数越大越有利于扩展。
  • 分片键的取值分布应该尽可能均匀。
  • 业务读写模式,尽可能分散写压力,而读操作尽可能来自一个或少量的分片。
  • 分片键应该能适应大部分的业务操作。

# 分片键(ShardKey)的约束

ShardKey 必须是一个索引。

# 分片标签

通过为分片添加标签(tag)的方式来控制数据分发。一个标签可以关联到多个分片区间(TagRange)。

分片标签适用于一些特定的场景 : 如,集群中可能同时存在OLTP和OLAP处理。

// 使用: 
//让分片拥有指定的标签
sh.addShardTag("shard01","oltp")
sh.addShardTag("shard02","oltp")
sh.addShardTag("shard03","olap")
// 声明TagRange 
sh.addTagRange("main.devices",{shardKey:MinKey},{shardKey:MaxKey},"oltp")
sh.addTagRange("other.systemLogs",{shardKey:MinKey},{shardKey:MaxKey},"olap")
// 最终: main.devices集合将被均衡地分发到shard01、shard02分片上,而other.systemLogs集合将被单独分发到shard03分片上。
1
2
3
4
5
6
7
8
9

# 数据均衡

现象:

  • 一方面,在没有人工干预的情况下,chunk会持续增长并产生分裂(split),而不断分裂的结果就会出现数量上的不均衡;
  • 另一方面,在动态增加分片服务器时,也会出现不均衡的情况。

目标:

  1. 所有的数据应均匀地分布于不同的chunk上。 由业务场景和分片策略来决定
  2. 每个分片上的chunk数量尽可能是相近的。 可以使用手动均衡、自动均衡方式来处理。

# 手动均衡

  • 初始化集合时预分配一定数量的chunk
  • 通过splitAt、moveChunk命令进行手动切分、迁移

# 自动均衡

均衡器会在后台对各分片的chunk进行监控,一旦发现了不均衡状态就会自动进行chunk的搬迁以达到均衡。

自动均衡是开箱即用的,可以极大简化集群的管理工作。

# chunk分裂

在默认情况下,一个chunk的大小为64MB,数据量超过了chunk大小,则MongoDB会自动进行分裂,将该chunk切分为两个相同大小的chunk。

chunk分裂是基于分片键进行的,如果分片键的基数太小,则可能因为无法分裂而会出现jumbo chunk(超大块)的问题(多次迁移失败后,将会导致无法迁移)。

所以超大块对水平扩展有负面作用,该情况不利于数据的均衡,业务上应尽可能避免。

# 数据均衡带来的问题

  • 数据均衡会影响性能,可以设置再晚上去做迁移。
  • 对分片集合中执行count命令可能会产生不准确的结果。 替代办法是使用db.collection.countDocuments({})方法做聚合操作。
  • 在执行数据库备份的期间,不能进行数据均衡操作,否则会产生不一致的备份数据。

# 两地三中心集群架构

双中心双活+异地热备=两地三中心

同一中心设置为主,同城的距离近的,优先级越高。 这样如果出现故障恢复的时延就更短。

# 全球多写集群架构

分片+指定tag + 各个分片中再设置一些不同分片的oplog数据同步。

编辑 (opens new window)
上次更新: 2023/01/24, 15:21:15
MongoDB索引
InfluxDB

← MongoDB索引 InfluxDB→

最近更新
01
架构升级踩坑之路
02-27
02
总结
02-27
03
语法学习
02-27
更多文章>
| Copyright © 2021-2025 Wu lingui |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式