mongoDB分片集群部署

1.MongoDB背景

  • 一款完善的分布式文档数据库
  • 一款非常出名的NoSQL 数据库
  • 可以支撑着每天近千万级 QPS 峰值读写,数万亿级数据量存储服务
  • 具有高性能、动态扩缩容、高可用、易部署、易使用、海量数据存储等方面优势很大

2.高可用架构

2.1 Master-Slave 模式

  • Master-Slave 由主从角色构成:
  • Master ( 主 ):可读可写,当数据有修改的时候,会将 Oplog 同步到所有连接的Salve 上去。
  • Slave ( 从 ):只读,所有的 Slave 从 Master 同步数据,从节点与从节点之间不感知。
  • 对读写分离的思考
  • 数据不一致

Master节点写,Slave 节点同步Master数据并对外提供读,这是一个异步的过程。 最终数据会被 Slave 同步到,在数据同步完成之前,数据是不一致的,这个时候去 Slave 节点读就存在读到旧的数据。

  • 对容灾的思考
  • 只能从master拉取数据,master宕机,salve只读,且salve节点间并不同步
  • 可能存在丢数据的情况

Master 节点出现故障的时候,由于 Slave 节点有备份数据,有数据就好办呀。只要有数据还在,对用户就有交代。这种 Master 故障的时候,可以通过人为 Check 和操作,手动把 Slave 节点指定为 Master 节点,这样又能对外提供服务了

2.2 Replica Set 副本集模式

  • Primary( 主节点 )

只有 Primary 是可读可写的,Primary 接收所有的写请求,然后把数据同步到所有 Secondary 。一个 Replica Set 只有一个 Primary 节点,当 Primary 挂掉后,其他 Secondary 或者 Arbiter 节点会重新选举出来一个 Primary 节点,这样就又可以提供服务了。 读请求默认是发到 Primary 节点处理,如果需要故意转发到 Secondary 需要客户端修改一下配置(注意:是客户端配置,决策权在客户端)。 那有人又会想了,这里也存在 Primary 和 Secondary 节点角色的分类,岂不是也存在单点问题? 这里和 Master-Slave 模式的最大区别在于,Primary 角色是通过整个集群共同选举出来的,人人都可能成为 Primary ,人人最开始只是 Secondary ,而这个选举过程完全自动,不需要人为参与。

  • Secondary( 副本节点 )

数据副本节点,当主节点挂掉的时候,参与选主。 思考一个问题:Secondary 和 Master-Slave 模式的 Slave 角色有什么区别? 最根本的一个不同在于:Secondary 相互有心跳,Secondary 可以作为数据源,Replica 可以是一种链式的复制模式。

  • Arbiter( 仲裁者 )
    不存数据,不会被选为主,只进行选主投票。使用 Arbiter 可以减轻在减少数据的冗余备份,又能提供高可用的能力
  • 优缺点
  • 可用性大大增强,因为故障时自动恢复的,主节点故障,立马就能选出一个新的 Primary 节点。
  • 有一个要注意的点:每两个节点之间互有心跳,这种模式会导致节点的心跳几何倍数增大,单个 Replica Set 集群规模不能太大,一般来讲最大不要超过 50 个节点。
  • 节点数的讲究:参与投票节点数要是奇数,这个非常重要。

偶数会导致脑裂,也就是投票数对等的情况,无法选出 Primary。 举个例子,如果有 3 张票,那么一定是 2:1 ,有一个人一定会是多数票,如果是 4 张票,那么很有可能是 2:2 ,那么就有平票的现象。

2.3 Sharding 模式

  • Replica Set 模式已经非常好的解决了可用性问题,为什么还会往后演进呢?

- 因为在当今大数据时代,有一个必须要考虑的问题:就是数据量。 - 用户的数据量是永远都在增加的,理论是没有上限的,但 Replica Set 却是有上限的。怎么办? 举个例子,假设说你的单机有 10TiB 的空间,内存是 500 GiB,网卡是 40 G,这个就是单机的物理极限。当数据量超过 10 TiB,这个 Replica Set 就无法提供服务了。你可能会说,那就加磁盘喽,把磁盘的容量加大喽。是可以,但是单机的容量和性能一定是有物理极限的(比如说你的磁盘槽位可能最多就 60 盘)。单机存在瓶颈怎么办?

  • 解决Replica Set模式的数据量上限:利用分布式技术
  • 纵向优化

纵向优化是传统企业最常见的思路,持续不断的加大单个磁盘和机器的容量和性能。CPU 主频不断的提升,核数也不断地加,磁盘容量从 128 GiB 变成当今普遍的 12 TiB,内存容量从以前的 M 级别变成现在上百 G 。带宽从以前百兆网卡变成现在的普遍的万兆网卡,但这些提升终究追不上用互联网数据规模的增加量级

  • 横向优化

横向优化通俗来讲就是加节点,横向扩容来解决问题。业务上要划分系统数据集,并在多台服务器上处理,做到容量和能力跟机器数量成正比。单台计算机的整体速度或容量可能不高,但是每台计算机只能处理全部工作量的一部分,因此与单台高速大容量服务器相比,可能提供更高的效率。 扩展的容量仅需要根据需要添加其他服务器,这比一台高端硬件的机器成本还低,代价就是软件的基础结构要支持,部署维护要复杂。

  • Sharding 模式角色
  • 代理层:mongos
  • 配置中心:副本集群(mongod)
  • 数据层:Shard 集群

mongodb 集群 数据 三台一样 mongodb集群同步_nosql

  • 代理层

代理层的组件也就是 mongos ,这是个无状态的组件,纯粹是路由功能。向上对接 Client ,收到 Client 写请求的时候,按照特定算法均衡散列到某一个 Shard 集群,然后数据就写到 Shard 集群了。收到读请求的时候,定位找到这个要读的对象在哪个 Shard 上,就把请求转发到这个 Shard 上,就能读到数据了。

  • 数据层

数据层是啥?就是存储数据的地方。你会惊奇的发现,其实数据层就是由一个个 Replica Set 集群组成。在前面我们说过,单个 Replica Set 是有极限的,怎么办?那就搞多个 Replica Set ,这样的一个 Replica Set 我们就叫做 Shard 。理论上,Replica Set 的集群的个数是可以无限增长的。

  • 配置中心

代理层是无状态的模块,数据层的每一个 Shard 是各自独立的,那总要有一个集群统配管理的地方,这个地方就是配置中心。里面记录的是什么呢? 比如:有多少个 Shard,每个 Shard 集群又是由哪些节点组成的。每个 Shard 里大概存储了多少数据量(以便做均衡)。这些东西就是在配置中心的。 配置中心存储的就是集群拓扑,管理的配置信息。这些信息也非常重要,所以也不能单点存储,怎么办?配置中心也是一个 Replica Set 集群,数据也是多副本的。

  • Sharding 模式下数据写入过程:

mongodb 集群 数据 三台一样 mongodb集群同步_mongodb_02

  • 推荐使用的姿势
  • 使用姿势一:怎么保证高可用

如果是 Replicate Set 模式,那么客户端要主动感知主从切换。以前用过 Go 语言某个版本的 MongoDB client SDK,发现在主从切换的时候,并没有主动感知,导致请求还一直发到已经故障的节点,从而导致服务不可用。 所以针对这种形式要怎么做?有两个方案: 1)用 Sharding 模式,因为 Sharding 模式下,用户打交道的是 mongos ,这个是一个代理,帮你屏蔽了底层 Replica Set 的细节,主从切换由它帮你做好; 2)客户端自己感知,定期刷新(这种就相对麻烦)

  • 使用姿势二:怎么保证数据的高可靠?

- 客户端配置写多数成功才算成功。没错,这个权限交由由客户端配置。如果没有配置写多数成功,那么很可能写一份数据成功就成功了,这个时候如果发生故障,或者切主,那么数据可能丢失或者被主节点 rollback ,也等同用户数据丢失。 - mongodb 有完善的 rollback 及写入策略(WriteConcern)机制,但是也要使用得当。怎么保证高可靠?一定要写多数成功才算成功。

  • 使用姿势三:怎么保证数据的强一致性?

- 客户端要配置两个东西: 1)写多数成功,才算成功; 2)读使用 strong 模式,也就是只从主节点读; 只有这两个配置一起上,才能保证用户数据的绝对安全,并且对外提供数据的强一致性。

3.部署分片集群

3.1 安装包/版本

  • 下载地址

https://www.mongodb.com/try/download/community?tck=docs_server

  • 版本

mongodb-linux-x86_64-enterprise-rhel70-5.0.8.tgz

3.2 集群目录规划

  • mongodb安装目录:/usr/local/mongodb/mongodb
  • 目录结构
mkdir -p /usr/local/mongodb/{config,mongos,shard1,shard2,shard3}/{data,logs}
  
[root@hadoop01 mongodb]# tree /usr/local/mongodb
/usr/local/mongodb
├── bin
│   ├── install_compass
│   ├── mongo
│   ├── mongod
│   └── mongos
├── config
│   ├── data
│   └── logs
├── LICENSE-Community.txt
├── mongos
│   ├── data
│   └── logs
├── MPL-2
├── README
├── shard1
│   ├── data
│   └── logs
├── shard2
│   ├── data
│   └── logs
├── shard3
│   ├── data
│   └── logs
└── THIRD-PARTY-NOTICES

3.3 集群节点信息

  • 节点IP、组件及端口规划:
ip地址mongos-routerconfig-servershard192.168.85.136hadoop01:27017hadoop01:27018hadoop01:27019 hadoop01:27020 hadoop01:27021192.168.85.137hadoop02:27017hadoop02:27018hadoop02:27019 hadoop02:27020 hadoop02:27021192.168.85.138hadoop03:27017hadoop03:27018hadoop03:27019 hadoop03:27020 hadoop03:27021

3.3.集群搭建

3.3.1 配置环境变量

  • 命令
# 添加环境变量
echo "PATH=$PATH:/usr/local/mongodb/bin" > /etc/profile.d/mongodb.sh
  
# 重新加载环境变量文件
source /etc/profile.d/mongodb.sh
  
# 版本查看
# mongo --version
  
MongoDB shell version v5.0.8
Build Info: {
    "version": "5.0.8",
    "gitVersion": "c87e1c23421bf79614baf500fda6622bd90f674e",
    "openSSLVersion": "OpenSSL 1.0.1e-fips 11 Feb 2013",
    "modules": [],
    "allocator": "tcmalloc",
    "environment": {
        "distmod": "rhel70",
        "distarch": "x86_64",
        "target_arch": "x86_64"
    }
}

3.3.2 安装所需依赖及解压缩包

yum install -y libcurl openssl xz-libs

tar -zxvf mongodb-linux-x86_64-enterprise-rhel70-5.0.8.tgz
#我们将文件移动到usr/local/下
mv mongodb-linux-x86_64-rhel70-5.0.8  /usr/local/mongodb

3.3.3 搭建分片副本集(Shard)集群

3.3.3.1 分片副本集shard1配置
  • 创建shard1复制集配置文件mongod.conf
cat > /usr/local/mongodb/shard1/mongod.conf <<EOF
systemLog:
  destination: file
  logAppend: true
  path: /usr/local/mongodb/shard1/logs/mongod.log
  
# Where and how to store data.
storage:
  dbPath: /usr/local/mongodb/shard1/data
  journal:
    enabled: true
  
# how the process runs
processManagement:
  # fork and run in background
  fork: true  
  # location of pidfile
  pidFilePath: /usr/local/mongodb/shard1/mongod.pid  
  timeZoneInfo: /usr/share/zoneinfo
  
# network interfaces
net:
  port: 27019
  bindIp: 192.168.85.136
  
sharding:
    clusterRole: shardsvr
      
replication:
    replSetName: shard1
EOF
3.3.3.2 分片副本集shard2配置
  • 创建shard2复制集配置文件mongod.conf
cat > /usr/local/mongodb/shard2/mongod.conf <<EOF
systemLog:
  destination: file
  logAppend: true
  path: /usr/local/mongodb/shard2/logs/mongod.log
  
# Where and how to store data.
storage:
  dbPath: /usr/local/mongodb/shard2/data
  journal:
    enabled: true
  
# how the process runs
processManagement:
  # fork and run in background
  fork: true
  # location of pidfile
  pidFilePath: /usr/local/mongodb/shard2/mongod.pid  
  timeZoneInfo: /usr/share/zoneinfo
  
# network interfaces
net:
  port: 27020
  bindIp: 192.168.85.136
  
sharding:
    clusterRole: shardsvr
      
replication:
    replSetName: shard2
EOF
3.3.3.3 分片副本集shard3配置
  • 创建shard3复制集配置文件mongod.conf
cat > /usr/local/mongodb/shard3/mongod.conf <<EOF
systemLog:
  destination: file
  logAppend: true
  path: /usr/local/mongodb/shard3/logs/mongod.log
  
# Where and how to store data.
storage:
  dbPath: /usr/local/mongodb/shard3/data
  journal:
    enabled: true
  
# how the process runs
processManagement:
  # fork and run in background
  fork: true
  # location of pidfile
  pidFilePath: /usr/local/mongodb/shard3/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
  
# network interfaces
net:
  port: 27021
  bindIp: 192.168.85.136
  
sharding:
    clusterRole: shardsvr
      
replication:
    replSetName: shard3
EOF
3.3.3.4 启动各分片副本集节点
  • 启动
#启动
[root@hadoop01 bin]# mongod -f /usr/local/mongodb/shard1/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 7881
child process started successfully, parent exiting

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/shard2/mongod.conf
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/shard3/mongod.conf

ps -aux|grep mongod
3.3.3.5 shard1初始化副本集
  • 登陆任意一台服务器,初始化副本集
mongo 192.168.85.136 --port 27019
rs.initiate(
  {
    _id: "shard1",
    members: [
      { _id : 0, host : "192.168.85.136:27019" },
      { _id : 1, host : "192.168.85.137:27019" },
      { _id : 2, host : "192.168.85.138:27019" }
    ]
  }
)
3.3.3.6 shard2初始化副本集
  • 登陆任意一台服务器,初始化副本集
mongo 192.168.85.136 --port 27020
rs.initiate(
  {
    _id: "shard2",
    members: [
      { _id : 0, host : "192.168.85.136:27020" },
      { _id : 1, host : "192.168.85.137:27020" },
      { _id : 2, host : "192.168.85.138:27020" }
    ]
  }
)
3.3.3.7 shard1初始化副本集
  • 登陆任意一台服务器,初始化副本集
mongo 192.168.85.136 --port 27021
rs.initiate(
  {
    _id: "shard3",
    members: [
      { _id : 0, host : "192.168.85.136:27021" },
      { _id : 1, host : "192.168.85.137:27021" },
      { _id : 2, host : "192.168.85.138:27021" }
    ]
  }
)
3.3.3.8 查看分片副本集节点状态
  • 命令
rs.status()

3.3.4 搭建config servers集群

3.3.4.1 创建配置文件mongod.conf
  • 创建配置文件,需自行修改ip(136/137/138)
cat > /usr/local/mongodb/config/mongod.conf <<EOF
systemLog:
  destination: file
  logAppend: true
  path: /usr/local/mongodb/config/logs/mongod.log

# Where and how to store data.
storage:
  dbPath: /usr/local/mongodb/config/data
  journal:
    enabled: true

# how the process runs
processManagement:
  # fork and run in background
  fork: true
  # location of pidfile
  pidFilePath: /usr/local/mongodb/config/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo

# network interfaces
net:
  port: 27018
  bindIp: 192.168.85.13x

sharding:
  clusterRole: configsvr

replication:
  replSetName: config
EOF
3.3.4.2 分别启动configServer
  • 命令
mongod -f /usr/local/mongodb/config/mongod.conf
3.3.4.3 初始化configServer副本集
  • 命令
#连接
mongo 192.168.85.136 --port 27018
初始化configServer副本集
rs.initiate(
  {
    _id: "config",
    configsvr: true,
    members: [
      { _id : 0, host : "192.168.85.136:27018" },
      { _id : 1, host : "192.168.85.137:27018" },
      { _id : 2, host : "192.168.85.138:27018" }
    ]
  }
)
  
#其中,”_id” : “config”应与配置文件中配置的 replicaction.replSetName 一致,”members” 中的 “host” 为三个节点的 ip 和 port
  
查看节点状态
rs.status()

3.3.5 搭建路由集群Router(mongos)

3.3.5.1 创建配置文件
  • 创建配置文件mongos.conf
cat > /usr/local/mongodb/mongos/mongos.conf <<EOF
systemLog:
  destination: file
  logAppend: true
  path: /usr/local/mongodb/mongos/logs/mongos.log

# how the process runs
processManagement:
  # fork and run in background
  fork: true
  # location of pidfile
  pidFilePath: /usr/local/mongodb/mongos/mongos.pid
  timeZoneInfo: /usr/share/zoneinfo

# network interfaces
net:
  port: 27017
  bindIp: 192.168.85.13x

sharding:
  configDB: config/192.168.85.136:27018,192.168.85.137:27018,192.168.85.138:27018
EOF
3.3.5.2 分别启动mongos server
  • 命令
mongos -f /usr/local/mongodb/mongos/mongos.conf

3.3.6 启用分片

  • 命令
mongo 192.168.85.136 --port 27017
sh.addShard( "shard1/192.168.85.136:27019,192.168.85.137:27019,192.168.85.138:27019")
sh.addShard( "shard2/192.168.85.136:27020,192.168.85.137:27020,192.168.85.138:27020")
sh.addShard( "shard3/192.168.85.136:27021,192.168.85.137:27021,192.168.85.138:27021")

3.4 集群测试

  • 目前配置服务、路由服务、分片服务、副本集服务都已经串联起来了,但我们的目的是希望插入数据,数据能够自动分片。连接在mongos上,准备让指定的数据库、指定的集合分片生效。
#连接任意一台mongos节点
mongo 192.168.85.136 --port 27017
use admin

# 为数据库启用分片
# 为testdb数据库启用分片
mongos> sh.enableSharding("testdb")
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1651837860, 2),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1651837860, 1)
}

#为集合启用分片
为order集合设置分片规则
mongos> sh.shardCollection("testdb.order", {"_id": "hashed" })
{
        "collectionsharded" : "testdb.order",
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1651837882, 74),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1651837882, 67)
}


#插入100000条数据进行验证
use testdb
for (i = 1; i <= 1500000; i=i+1){
    db.order.insert({'price': 1})
}

#查看插入的数据量
mongos> db.order.find().count()
100000

#查看分片情况
db.order.stats();
"ns" : "testdb.order",
        "count" : 99999,
        "size" : 8388810,
        "storageSize" : 4153344,
        "totalIndexSize" : 9805824,
        "totalSize" : 13959168,
        "indexSizes" : {
                "_id_" : 4915200,
                "_id_hashed" : 4890624
        },
        "avgObjSize" : 83,
        "maxSize" : NumberLong(0),
        "nindexes" : 2,
        "scaleFactor" : 1,
        "nchunks" : 6,
        "shards" : {
                "shard1" : {
                        "ns" : "testdb.order",
                        "size" : 2757979,
                        "count" : 32877,
                        "avgObjSize" : 83,
                        "storageSize" : 1368064,
                        "freeStorageSize" : 679936,
                        "capped" : false,
                        "wiredTiger" : {
                                "metadata" : {
                                        "formatVersion" : 1
                                }
                                .........

                "shard2" : {
                        "ns" : "testdb.order",
                        "size" : 2816622,
                        "count" : 33576,
                        "avgObjSize" : 83,
                        "storageSize" : 1396736,
                        "freeStorageSize" : 692224,
                        "capped" : false,
                        "wiredTiger" : {
                                "metadata" : {
                                        "formatVersion" : 1
                                }
                                .........
                "shard3" : {
                        "ns" : "testdb.order",
                        "size" : 2814209,
                        "count" : 33546,
                        "avgObjSize" : 83,
                        "storageSize" : 1388544,
                        "freeStorageSize" : 688128,
                        "capped" : false,
                        "wiredTiger" : {
                                "metadata" : {
                                        "formatVersion" : 1
                                }
                                .........
 以看到数据分到3个分片,各自分片数量为: shard1 “count” : 33273,shard2 “count” : 33377,shard3 “count” : 33350

3.5 后期运维

3.5.1 启动关闭

  • 启动顺序
mongod -f /usr/local/mongodb/config/mongod.conf
mongod -f /usr/local/mongodb/shard1/mongod.conf
mongod -f /usr/local/mongodb/shard2/mongod.conf
mongod -f /usr/local/mongodb/shard3/mongod.conf
mongos -f /usr/local/mongodb/mongos/mongos.conf
  • 关闭
killall mongod
killall mongos