mongodb介绍
MongoDB 是一款功能完善的分布式,NoSQL,文档数据库。在高性能、动态扩缩容、高可用、易部署、易使用、海量数据存储等方面拥有很大优势。
传送门: mongodb的官方文档。
数据持久化与journal
高可用的第一步,保证数据不丢, 也就是数据的持久化。
mongodb做为内存型数据库,数据操作会先写入内存(不会立即持久化存储到磁盘),然后周期性(storage.syncPeriodSecs 配置项,默认为1分钟)全量checkpoint,再持久化到硬盘中去。如果中间出现宕机,那么数据只能恢复到最近的一次checkpoint,这样最多可能丢掉1分钟的数据。
journal 是mongodb存储引擎存储数据时的一种辅助机制。开启 journal 后,每次写入会记录一条操作日志(通过journal可以重新构造出写入的数据)。这样即使出现宕机,启动时mongodb的存储引擎会先将数据恢复到最近的一次持久化,然后重放后续的 journal 操作日志来恢复数据。
主从模式
主从模式是分布式系统最开始的冗余策略,一般用于备份或者做读写分离,可以是一主一从或者是一主多从。
其中对主节点的访问操作可读可写,当主数据有修改的时候,会将 Oplog 同步到所有连接的从节点上去。而对从节点的访问操作只可以是读数据,所有的从节点都会从主节点同步数据。
主从模式的优点
主从模式通过读写分离方式分散压力:写入、修改、删除操作,在写库(主机)上完成;把查询请求,分配到读库(从机)。这样做的好处是
- 提升资源利用率,适合读多写少的应用场景。
- 在大并发读的使用场景,可以使用负载均衡在多个从机间进行平衡。
- 从机的扩展性比较灵活,扩容操作不会影响到业务进行。
主从模式的缺点
主从模式适合做读写分离,但是不是所有的业务场景都适合做读写分离,如果写操作比较多的话,压力就来到唯一的主节点上了。另外更重要的一点是,从节点从主节点同步数据期间可能会导致数据不一致的问题,因为在从节点同步数据完成之前,只能在从节点的读到旧的数据。
当主从模式中的某个从节点故障时,可以通过负载均衡避免业务中断。但是一旦主节点出现故障,所有需要写数据的业务都会中断,直到人为操作指定新的主节点。
MongoDB 3.6 起已不推荐使用主从模式,自 MongoDB 3.2 起,分片群集组件已弃用主从复制。因为 Master-Slave 其中 Master 宕机后不能自动恢复,只能靠人为操作,可靠性也差,操作不当就存在丢数据的风险。
副本集模式
副本集节点之间同步数据的方式与主从模式相同,都是从节点通过从主节点拉取oplog并回放实现数据同步。但是在可用性方面相比于主从模式,副本集模式可以保证集群在(N-1)/2 个节点挂掉后,不丢数据且业务不中断。
-
至少一个主节点(Primary): 负责整个集群的写操作入口, 主节点挂掉之后会自动选出新的主节点。
-
一个或多个从节点(Secondary): 一般是2个或以上,从主节点同步数据,在主节点挂掉之后选举新节点。
-
零个或1个仲裁节点(Arbiter): 这个是为了节约资源或者多机房容灾用,只负责主节点选举时投票不存数据,保证能有节点获得多数赞成票。
心跳机制
副本集成员每两秒相互发送一次心跳(ping)。如果心跳在 10 秒内未返回,则其他成员将拖欠成员标记为不可访问。这种心跳机制的复杂度随着副本集成员数量的增加成几何级倍数增长,因此一般来说副本集成员不要超过50个。
投票选举机制
副本集模式可以理解副本集模式是主从模式的升级版。相比于主从模式,副本集模式没有了固定的主节点,当主节点故障,集群能投票自动选出一个新的主节点,来避免业务的中断。需要注意的是在选举成功完成之前(一般情况5s能完成),副本集无法处理写入操作。当副本集的节点数设置为偶数个且没有仲裁节点时,可能会有两个节点投票数对等的情况出现,导致一直选不出主节点,一直无法处理写操作。因此尽量设置副本集尽量设置奇数个节点
分片模式
副本集与主从模式都是将数据全量的存储主每一个节点上,当数据库的数据量增加到特别大的时候,单机的硬件配置无法负担这么多数据,数据的查询与写入性能会相应的降低。另外副本集与主从模式都是只有主节点才可以进行写操作,当写操作大量并发请求过来时,从节点都无法为主节点分担压力。
分片集群通过数据集的划分解决了以上两点问题。单个节点不用存储全量的数据,写操作的压力也可以分担到多台单机上。下面是分片集群的架构图:
-
Config:本身是一个副本集,负责存储集群配置信息,shard地址、chunk等
-
Mongos:请求路由和代理,通过 Client IP 的 hash 值决定连哪个
-
Shard/Mongod:数据实际存储的节点,每个 Shard 都是一个副本集群
数据的划分
分片集群通过分片键以及分片策略来确定数据命中哪一个chunk。chunk是mongo对数据划分范围的一个抽象概念,在config server中存储了chunk与shard的映射关系,这样在shard与数据之间多设计一层chunk,极大的增加了分片的灵活性,保证了各个分片之间的数据均衡
当有一个数据操作从客户端请求到分片集群时,mongos通过分片键以及相应的分片策略,计算出这次的数据操作落在哪个Chunk上,然后查询config server得到这个chunk对应的具体shard并将数据操作转发给这个shard。
chunk的分裂与迁移
分片模式通过对数据的划分来使数据库有横向扩展的能力,但是如何保证数据均衡的分布在每一个分片中的呢?
答案是通过chunk的分裂以及迁移。
MongoDB 中的chunk默认大小为 64 Mb(可以通过配置修改)。当一个chunk的大小超过chunk最大容量或者chunk中的文档数超过chunk最大文档数时,chunk将会被拆分。
| 集合 chunk 数量 | 分裂阈值 |
|---|---|
| 1 | 1024B |
| [1, 3) | 0.5MB |
| [3, 10) | 16MB |
| [10, 20) | 32MB |
| [20, max) | 64MB |
前面提到chunk与shard的映射存储在config sever中,分片集群的均衡器也位于config server中。均衡器是一个后台进程,用于监控每个分片上的chunk数量。当分片上的块数达到特定迁移阈值时,平衡器会尝试在分片之间自动迁移块并使每个分片拥有相同数量的块,用来保证数据的均衡分布。
分片策略的选择:
范围分片是将分片键的值划分为多个范围,每个范围对应一个chunk,如图:
hash分片是将分片键的值通过hash函数求出一个数,再根据这个数划分为多个范围,每个范围对应一个chunk,如图:
hash分片的最大好处在于保证数据在各个节点上的均匀分布(这里的均匀指的是在写入的时候就均匀,而不是通过MongoDB的balancing功能)。比如MongoDB中默认的_id是objectid,objectid是一个12个字节的BSON类型,前4个字节是机器的时间戳,那么如果在同一时间大量创建以ObjectId为_id的数据 会分配到同一个shard上,此时若将_id设置为hash index 和 hash sharding key,就不会有这个问题。但是hash分片的缺点也很明显,范围查询的时候效率低
分片键的选择
- 基数。分片键的基数决定了分片集群可以创建的最大chunk的数目。在任何给定的时间,唯一的分片值只能存在一个chunk上。例如:使用性别进行分片,则只能分为“男”和“女”2个chunk,不能随着数据增多而分裂为更多的chunk,因为一个分片值只能存储在同一个chunk中。
- 频率。频率代表给定值在该列中出现的比率,与关系型数据库中select distinct ...异曲同工。如果大多数文档包含了这些值的子集,那么存储这些文档的chunk将成为集群中的瓶颈,随着数据的增长,他们将会成为不可分割的数据块,降低了集群水平扩展的有效性。例如:集合people用来统计各个名族的人信息,使用名族作为分片字段,那么根据我国56个名族的人数分布,占据人口总数92%的汉族将占据一个chunk,这样会导致该chunk非常巨大,失去了分片的意义。
- 变化率。单调递增或单调递减的分片键可能将数据写到集群中的单个分片上。如果分片键值始终在增加,则所有新插入都将路由到以maxKey为上限的块。 如果分片键值始终在减小,则所有新插入都将路由到以minKey为下限的块。 包含该块的分片将成为写操作的瓶颈
如何开启分片
假设我们要开启分片的数据库名称为dbName,collection名称为collectionName,document索引为document_id
1.给需要创建分片键的collection创建索引
db.collectionName.createIndex({document_id:1})
2.切换到admin,给相关db开启分片
use admin
db.adminCommand({ enablesharding:"dbName"})
3.给相关文档设置分片键
db.runCommand({ shardcollection: 'dbName.collectionName', key: {document_id: 1}})
总结
相比于单机,主从模式可以做到异地容灾以及读写分离,副本集模式解决了在主节点宕机后需要手动切换主节点的问题,而分片模式解决了一台机器不足以存储数据,不足以提供足够IO吞吐量的问题。从单机,主从模式,副本集模式到分片模式,mongodb的可用性与扩展性都是递增的。
下期我会给大家介绍在分片模式下mongoDB的读写策略
欢迎搜索关注柴柴爱Coding微信公众号,这里有 免费的学习资源、全方位的进阶路线、各岗位面试资源、程序设计源码 一只会Coding的柴柴等你哦~