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 集群
- 代理层:
代理层的组件也就是 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 模式下数据写入过程:
- 推荐使用的姿势
- 使用姿势一:怎么保证高可用?
如果是 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