Mongo集群化部署+高可用架构

数据库开发系列



前言

数据库的演进
随着计算机的发展,越来越多的数据需要被处理,数据库是为处理数据而产生。从概念上来说,数据库是指以一定的方式存储到一起,能为多个用户共享,具有更可能小的冗余,与应用程序彼此独立的数据集合。从功能上来说,就是数据管理软件。

到了2000年随着互联网的发展,数据量呈现爆发式增长。海量数据的诞生,传统的关系型数据库在应对大规模,超大流量的时候就显得力不从心。借此,NoSQL数据库跟NewSQLl数据库就此登场。

NoSQL 全称 “not only sql”,所有非关系型的数据库都统称为NoSQL数据库,NoSQL数据库主要有四种类型,文档数据库(MongoDB为代表),键值数据库(Redis为代表),宽列存储(Hbase为代表)和图形数据库(Neo4J为代表)。

NewSQL数据库,提供了与NoSQL相同的扩展性,但仍属于关系型模型,还保留SQL作为查询语言,保证了ACID事务性。代表数据库有Spanner,CockroachDB。
在这里插入图片描述

数据库发展到今天,可以说是百花齐放,随着2020年阿里云数据库进入全球魔力四象限的Leader象限,这也是中国数据库40年来首次进入全球顶级数据库行列,标志着国产数据库开始崛起。

数据库索引技术
数据库是用来处理数据的,那高效的存储、检索数据,是数据库管理系统必备的技能。数据库中用于提高检索效率很重要的一项技术就是索引。

数据结构指的是“一组数据的存储结构”,算法指的是“操作数据的一组方法”。数据结构是为算法服务的,算法是要作用在特定的数据结构上的。数据库提高检索效率使用的是索引,数据库索引是一种树形数据结构,如MongoDB使用的是B-tree、MySQL使用的是B+tree。
参考https://blog.csdn.net/dbaxiaosa/article/details/127750957

MongoDB介绍
本文重点介绍MongoDB,它虽然诞生于NoSQL,但4.0版本之后,已经不仅仅是NoSQL了,可以与上文中任何一位NewSQL的代表PK。MongoDB是一个开源、高性能、无模式的文档数据库,旨在简化开发和扩展。


一、MongoDB存储引擎

存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上。从MongoDB 3.2 版本开始,MongoDB 支持多数据存储引擎(Storage Engine),MongoDB支持的存储引擎有:WiredTiger,MMAPv1和In-Memory。
在这里插入图片描述
mongod 参数: --storageEngine wiredTiger | inMemory
如果参数值是wiredTiger,MongoDB使用的存储引擎是WiredTiger,将数据持久化存储在Disk Files中;
如果参数值是inMemory,MongoDB使用的存储引擎是In-Memory,将数据存储在内存中;
从MongoDB 3.2 版本开始,MongoDB默认的存储引擎是WiredTiger;

WiredTiger和MMAPv1都用于持久化存储数据,相对而言,WiredTiger比MMAPv1更新,功能更强大。
WiredTiger是在MongoDB3.0版本引入的,并且在MongoDB3.2版本开始成为MongoDB默认的存储引擎。相比较MMAPv1,WiredTiger功能更强大,而且具有更高的性能。3.0版本之前是采用的 MongoDB的MMAPv1存储引擎。WiredTiger提供文档级别(Document-Level)的并发控制,检查点(CheckPoint),数据压缩和本地数据加密( Native Encryption)等功能。
1、文档级别的并发控制
MongoDB在执行写操作时,WiredTiger 在文档级别进行并发控制,就是说,在同一时间,多个写操作能够修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待,直到在该文档上的写操作完成之后,其他写操作相互竞争,获胜的写操作在该文档上执行修改操作。
对于大多数读写操作,WiredTiger使用乐观并发控制(optimistic concurrency control),只在Global,database和Collection级别上使用意向锁(Intent Lock),如果WiredTiger检测到两个操作发生冲突时,导致MongoDB将其中一个操作重新执行,这个过程是系统自动完成的。
2、检查点
在Checkpoint操作开始时,WiredTiger提供指定时间点(point-in-time)的数据库快照(Snapshot),该Snapshot呈现的是内存中数据的一致性视图。当向Disk写入数据时,WiredTiger将Snapshot中的所有数据以一致性方式写入到数据文件(Disk Files)中。一旦Checkpoint创建成功,WiredTiger保证数据文件和内存数据是一致性的,因此,Checkpoint担当的是还原点(Recovery Point),Checkpoint操作能够缩短MongoDB从Journal日志文件还原数据的时间。
当WiredTiger创建Checkpoint时,MongoDB将数据刷新到数据文件(Disk Files)中,在默认情况下,WiredTiger创建Checkpoint的时间间隔是60s,或产生2GB的Journal文件。在WiredTiger创建新的Checkpoint期间,上一个Checkpoint仍然是有效的,这意味着,即使MongoDB在创建新的Checkpoint期间遭遇到错误而异常终止运行,只要重启,MongoDB就能从上一个有效的Checkpoint开始还原数据。
当MongoDB以原子方式更新WiredTiger的元数据表,使其引用新的Checkpoint时,表明新的Checkpoint创建成功,MongoDB将老的Checkpoint占用的Disk空间释放。使用WiredTiger 存储引擎,如果没有记录数据更新的日志,MongoDB只能还原到上一个Checkpoint;如果要还原在上一个Checkpoint之后执行的修改操作,必须使用Jounal日志文件。
3、预先记录日志
WiredTiger使用预写日志的机制,在数据更新时,先将数据更新写入到日志文件,然后在创建Checkpoint操作开始时,将日志文件中记录的操作,刷新到数据文件,就是说,通过预写日志和Checkpoint,将数据更新持久化到数据文件中,实现数据的一致性。WiredTiger 日志文件会持久化记录从上一次Checkpoint操作之后发生的所有数据更新,在MongoDB系统崩溃时,通过日志文件能够还原从上次Checkpoint操作之后发生的数据更新。

文件空间分配方式改进
MMAPv1存储引擎是在数据库级别分配文件的,将每个数据库中所有的集合和索引都混合存储在数据库文件中,即使删除了某个集合或索引,其占用的磁盘空间也很难及时自动回收。WiredTIger则在集合和索引级别分配文件,将每个数据库中所有的集合和索引都存储在单独的文件中,集合或索引删除后,其对应文件即可删除,磁盘空间回收方便。
WiredTiger的一些数据文件:
mongod.lock:用于防止多个进程连接同一个WiredTiger数据库
.wt文件:存储各个集合的数据,每个文件100MB
WiredTiger.wt:用于存储所有集合的元数据信息
WiredTiger.turtle:用于存储WiredTiger.wt的元数据信息
journal文件夹:用于存储日志文件(Write ahead log)

WiredTiger对内存的使用情况
wiredTiger对内存使用会分为两大部分,一部分是内部内存,另外一部分是文件系统的缓存。内部内存默认值有一个计算公式{ 50% of(RAM-1GB) ,or256MB },索引和集合的内存都被加载到内部内存,索引是被压缩的放在内部内存,集合则没有压缩。wiredTiger会通过文件系统缓存,自动使用其他所有的空闲内存,放在文件系统缓存里面的数据,与磁盘上的数据格式一致,可以有效减少磁盘I/O。

你需要分析是否对默认的内存做调优。一条比较好的原则就是wt的缓存足够大,能够缓存整个应用的工作集。
查看wt的缓存统计信息:
db.serverStatus().wiredTiger.cache
文档级别的并发控制
WiredTiger存储引擎使用文档级别锁,同一时刻多个写操作可以修改同一个集合中不同的文档,但不能修改同一个文档。这使得WiredTiger存储引擎的并发处理能力比MMAPv1更好。

通过检查点和预写日志实现数据持久化
按照MongoDB默认的配置,WiredTiger的写操作会先写入Cache(BTree结构),当Cache大小达到128KB时便将其持久化到预写日志文件(Write ahead log)。WiredTiger每60s或日志文件大小达到2GB时会做一次检查点Checkpoint,产生指定时间点的数据库快照(内存中数据的一致性视图),将快照中的所有数据以一致性方式持久化到数据文件中,保证数据文件和内存数据是一致的。Wiredtiger连接初始化时,首先将数据恢复至最新的快照状态,然后根据预写日志文件恢复数据,以保证存储可靠性。
Journal是一种预写式日志(write ahead log)机制,主要用来弥补CheckPoint机制的不足,如果开启了Journal日志,那么WiredTiger会将每个写操作的redo日志写入Journal缓冲区,该缓冲区会频繁地将日志持久化到磁盘上。默认情况下,Journal缓冲区每100ms执行一次持久化。此外,Journal日志达到100MB,或是应用程序指定Journal:true,写操作都会触发日志的持久化。一旦MongoDB发生宕机,重启程序时会先恢复到上一个检查点,然后根据Journal日志恢复增量的变化。由于Journal日志持久化的间隔非常短,数据能得到更高的保障,如果按照当前版本的默认配置,则其在断电情况下最多会丢失100ms的写入数据。
在这里插入图片描述
WiredTiger写入数据的流程:
1、应用向MongoDB写入数据(插入、修改或删除)
2、数据库从内部缓存中获取当前记录所在的业块,如果不存在则会从磁盘中加载(Buffer I/O)
3、WiredTiger开始执行写事务,修改的数据写入页块的一个更新记录表,此时原来的记录仍然保持不变
4、如果开启了Journal日志,则在写数据的同时会写入一条Journal日志(Redo Log)。该日志在最长不超过100ms后写入磁盘。
5、数据库每隔60s执行一次CheckPoint操作,此时内存中的修改会真正刷入磁盘。

内存使用上限可配置
使用WiredTiger存储引擎时,MongoDB数据缓存分两部分:内部缓存和文件系统缓存。内部缓存大小可以使用–wiredTigerCacheSizeGB参数来设置,默认值为:1GB或RAM的60%到1GB之间,取两值中较大者。文件系统缓存大小则不固定,MongoDB自动使用系统空闲的内存,且数据在文件系统缓存中是压缩存储的。

数据压缩
使用WiredTiger存储引擎时,数据库的集合与索引、日志文件都是压缩存储的,节省了磁盘空间。WiredTiger默认情况下,集合数据使用块压缩算法,索引数据则使用前缀压缩算法。这使得数据占用磁盘空间少,读写速度快,花费I/O时间少。
集合中数据比较少时,压缩和无压缩的写性能相单,无压缩读性能反而比压缩读性能好。当集合数据很多时,压缩的读写性能则都要比无压缩的读写性能好。

二、MongoDB 复制(副本集)

MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
复制还允许您从硬件故障和服务中断中恢复数据。
注:MongoDB复制是异步的,若配置了从Secondary读取数据,则需要业务接受数据延迟。

为什么需要复制?
保障数据的安全性
数据高可用性 (24*7)
灾难恢复
无需停机维护(如备份,重建索引,压缩)
分布式读取数据

MongoDB复制原理
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从(需要添加仲裁节点)、一主多从(根据情况是否添加仲裁节点)。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

MongoDB仲裁者(Arbiter)是复制集中的一个mongodb实例,它并不保存数据。仲裁节点使用最小的资源并且不要求硬件设备,最好不要将Arbiter部署在同一个数据集节点中,可以部署在其他服务器中,也可部署在单独的虚拟机中。
当节点数目为奇数时,可以不需要仲裁节点。
当节点数目为偶数个时,需要部署一个仲裁节点,否则偶数个节点,当主节点挂了后,其他节点会变为只读。

副本集特征:
N 个节点的集群
任何节点可作为主节点
所有写入操作都在主节点上
自动故障转移
自动恢复
在这里插入图片描述

创建分片语法

//创建mongo实例
mongod  --bind_ip "0.0.0.0" --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --logpath "YOUR_LOG_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"  --logappend --fork
//启动一个新的副本集
rs.initiate()
//添加副本集
rs.add(HOST_NAME:PORT)
//查看副本集状态使用 
rs.status() 
//查看副本集的配置
rs.conf()

参数解析

--oplogSize 日志操作文件的大(默认磁盘剩余空间的5%--dbpath   数据文件路径
--logpath  日志文件路径
--port        端口号,默认是27017
--replSet   复制集的名字,一个replica sets中的每个节点的这个参数都要用一个复制集名字,这里是test.
--replSet test/  这个后面跟的是其他standard节点的ip和端口
--maxConns   最大连接数
--fork       后台运行
--logappend   日志文件循环使用,如果日志文件已满,那么新日志覆盖最久日志。

端口资源分配:本实例一主一从,节点数为偶数,因此需要仲裁节点
Primary:IP:27017
Secondary:IP2:27017
Arb:IP3:27017

1. 主机A创建复制集rs0(10.20.40.33)
# mkdir /data/log
# mkdir /data/db1
# nohup mongod --port 27017 --dbpath=/data/db1 --logpath=/data/log/rs0-1.log --logappend --fork --shardsvr --replSet=rs0 &

2. 主机B创建复制集rs0 (10.20.40.34)
# mkdir /data/db2
# nohup mongod --port 27017 --dbpath=/data/db2 --logpath=/data/log/rs0-2.log --logappend --fork --shardsvr --replSet=rs0 &

3. 已经初始过的rs,可以新增复制集(10.20.40.3427017# mongo  
> rs.initiate({_id:"rs0",members:[{_id:0,host:"10.20.40.33:27017"}]})
> rs.add("10.20.40.34:27017")


4. 主机A登录,一下子初始化好复制集rs0配置(或者方式4等价)
# mongo  
> rs.initiate({_id: 'rs0', members: [{_id: 0, host: '10.20.40.33:27017',priority :10}, {_id: 1, host: '10.20.40.33:27017',priority :1}]}) 
> rs.isMaster() 
> rs.conf()
> rs.status()
重新配置rs0,且带有优先级
# mongo  
> config = {_id: 'rs0', members: [{_id: 0, host: '10.20.40.33:27017',priority :10}, {_id: 1, host: '10.20.40.33:27017',priority :1}]};
> rs.reconfig(config);   

5.移除分片
rs.remove("10.20.40.34:27017")

6.当节点数为偶数时,需要添加仲裁者节点
rs.addArb("10.20.40.33:27017")

7.当登录从节点时,需要执行如下命令,才可以看到同步主节点的消息
use admin
db.getMongo().setSecondaryOk()

8.可以关闭节点
db.shutdownServer()

9.shell中添加节点,命令
mongo --eval 'rs.reconfig(_id:"rs0",members:[{_id:0,host:'10.20.40.131:27017'},{_id:1,host:'10.20.40.145:27017'}])';  

注意:
1、主节点进行读写,从节点默认不能读,需要设定后才可以,访问从节点后,执行db.getMongo().setSecondaryOk()
2、主节点登录后,use admin,db.shutdownServer(),exit后,发现从节点由secondary变为primary
3、故障转移后,主节点的IP地址发生变化。因此需要客户端程序来处理这种IP变化。

三、为什么需要分片集群架构

当业务遇到如下问题时,可以使用分片集群解决:
1、存储容量受单机限制,即磁盘资源遭遇瓶颈。
2、读写能力受单机限制,可能是CPU、内存或者网卡等资源遭遇瓶颈,导致读写能力无法扩展。

关于负载均衡
MongoDB分片集群的自动负载均衡目前是由mongos的后台线程来做,并且每个集合同一时刻只能有一个迁移任务。负载均衡主要根据集合在各个shard上chunk的数量来决定的,相差超过一定阈值(和chunk总数量相关)就会触发chunk迁移。

负载均衡默认是开启的,为了避免chunk迁移影响到线上业务,可以通过设置迁移执行窗口,例如只允许凌晨02:00~06:00期间进行迁移。

use config
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "02:00", stop : "06:00" } } },
{ upsert: true }
)

四、高可用分片集群架构(+复制集)

一个 MongoDB分片集群由以下组件组成:
1、shard:每个分片包含分片数据的一个子集。每个分片都可以部署为副本集。
2、mongos:mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。
3、config servers:配置服务器存储集群的元数据和配置设置。从 MongoDB 3.4 开始,配置服务器必须部署为副本集 (CSRS)。
总之:shard、config servers组建部署副本集保障高可用,mongos是单点架构,可以部署多个节点保障高可用。
在这里插入图片描述
参照关系型数据库,我们来看看MongoDB中的对象。
在这里插入图片描述
部署测试(待完成):

端口资源分配
Shard Server 1:27020
Shard Server 2:27021
Shard Server 3:27022
Shard Server 4:27023
Config Server :27100
Route Process:40000

1. 创建Sharding复制集 rs0
# mkdir /data/log
# mkdir /data/db1
# nohup mongod --port 27020 --dbpath=/data/db1 --logpath=/data/log/rs0-1.log --logappend --fork --shardsvr --replSet=rs0 &

# mkdir /data/db2
# nohup mongod --port 27021 --dbpath=/data/db2 --logpath=/data/log/rs0-2.log --logappend --fork --shardsvr --replSet=rs0 &

1.1 复制集rs0配置
# mongo localhost:27020 
> rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'localhost:27020'}, {_id: 1, host: 'localhost:27021'}]}) 
> rs.isMaster() 



2. 创建Sharding复制集 rs1
# mkdir /data/db3
# nohup mongod --port 27030 --dbpath=/data/db3 --logpath=/data/log/rs1-1.log --logappend --fork --shardsvr --replSet=rs1 &
# mkdir /data/db4
# nohup mongod --port 27031 --dbpath=/data/db4 --logpath=/data/log/rs1-2.log --logappend --fork --shardsvr --replSet=rs1 &

2.1 查看主从关系
# mongo localhost:27030
> rs.initiate({_id: 'rs1', members: [{_id: 0, host: 'localhost:27030'}, {_id: 1, host: 'localhost:27031'}]})
> rs.isMaster() 

3. 创建Config复制集 conf
# mkdir /data/conf1
# nohup mongod --port 27100 --dbpath=/data/conf1 --logpath=/data/log/conf-1.log --logappend --fork --configsvr --replSet=conf &
# mkdir /data/conf2
# nohup mongod --port 27101 --dbpath=/data/conf2 --logpath=/data/log/conf-2.log --logappend --fork --configsvr --replSet=conf &

3.1 复制集conf配置
# mongo localhost:27100
> rs.initiate({_id: 'conf', members: [{_id: 0, host: 'localhost:27100'}, {_id: 1, host: 'localhost:27101'}]})
> rs.isMaster() 


4. 创建Route即mongos,其依赖部署的configdb
nohup mongos --port 40000 --configdb conf/localhost:27100,localhost:27101 --fork --logpath=/data/log/route.log --logappend & 

4.1 设置分片
# mongo localhost:40000
> use admin
> db.runCommand({ addshard: 'rs0/localhost:27020,localhost:27021'})
> db.runCommand({ addshard: 'rs1/localhost:27030,localhost:27031'})
> db.runCommand({ enablesharding: 'test'})
> db.runCommand({ shardcollection: 'test.user', key: {name: 1}})


总结

通过本文的学习,应该对mongodb的分布式架构有了深刻了理解

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
部署 MongoDB 分片集群,需要按照以下步骤进行操作: 1. 安装 MongoDB:在每个分片服务器和配置服务器上安装 MongoDB。可以从 MongoDB 官方网站(https://www.mongodb.com/try/download/community)下载最新的 MongoDB 版本,并按照官方文档提供的说明进行安装。 2. 配置配置服务器(config server):选择三个服务器作为配置服务器的节点。在每个节点上创建一个配置服务器实例,并将它们配置为副本集。详细的配置步骤可以参考 MongoDB 官方文档中的 "Deploy a Config Server Replica Set" 部分。 3. 启动配置服务器副本集:启动配置服务器副本集,确保副本集中的每个节点都正常运行,并且选举了一个主节点。 4. 启动分片服务器(shard server):选择多个服务器作为分片服务器的节点。在每个节点上创建一个 mongod 实例,并将其配置为副本集。详细的配置步骤可以参考 MongoDB 官方文档中的 "Deploy a Sharded Cluster" 部分。 5. 向分片集群添加分片:将分片服务器添加到分片集群中。你可以使用 `mongos` 实例连接到任意一个分片服务器,然后使用 `sh.addShard()` 命令将分片服务器添加到集群中。详细的操作步骤可以参考 MongoDB 官方文档中的 "Add Shards to a Cluster" 部分。 6. 定义分片键和路由规则:在集群中定义分片键,并根据分片键的取值范围定义路由规则。这样 `mongos` 实例就能根据分片键将数据路由到正确的分片服务器上。你可以使用 `sh.shardCollection()` 命令来定义分片键和路由规则。详细的操作步骤可以参考 MongoDB 官方文档中的 "Shard a Collection" 部分。 7. 验证和测试:最后,确保整个分片集群正常运行。你可以插入测试数据并查询数据,以确保数据正确地分布在不同的分片服务器上。 请注意,在部署 MongoDB 分片集群时,需要仔细阅读 MongoDB 官方文档并按照其中的说明进行操作。这样可以确保正确配置和部署分片集群,并获得最佳性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c+猿辅导

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值