主从结构
MongoDB的高可用和别的中间件的高可用方案基本类似。比如在MySQL里,接触了分库分表和主从同步;在Redis里,Redis也有主从结构;在Kafka里,分区也是有主从结构的。
所以先介绍启用了主从同步
我们的系统有一个关键组件 - MongoDB,但是在最开始的时候,MongoDB没有启用主从,是一个单节点的。因此每年总会有一两次,MongoDB崩溃不可用。所以我把MongoDB改成了主从同步,最开始的时候业务量不多,为了节省成本,我们用了推荐的配置一主两从。这种改变的好处是:当主节点崩溃后,从节点可以选举出一个新的主节点。
直接说用了几个主从节点,如果问到主从同步的话,回答oplog的内容。
引入仲裁节点
另一种思路是引入仲裁节点,所谓仲裁节点是指这个节点参与主从集群的主节点选举,但是只参与投票,类似于Elasticsearch里的仅投票节点。
这种机制在别的中间件也见过了,这类节点的好处在于它们只参与投票,也就是只关心主从选举,所以只需要很少的资源就可以运行起来
最开始的时候,我们只是部署了一主两从,两个从节点都会同步数据。后面为了进一步提高可用性,引入了仲裁节点。这些仲裁节点被部署在轻量级的服务器上,成本非常低。在引入了这些仲裁节点后,就算有一个从节点崩溃了,整个集群也基本没什么影响,因为这个时候还是有足够的节点可以投票
启用主从模式的配置服务器
我们MongoDB最开始部署的时候,配置服务器并没有启用主从模式,毕竟当时想节省资源。但是后面发现,配置服务器这个对集群的影响太大了,一旦不可用,整个集群就基本不可用了。这种情况下,我们只好引入了主从结构的配置服务器。目前配置服务器本身就有一主两从。
虽然主节点还是存在崩溃的可能,但是在主节点崩溃之后会有主从选举。更加重要的是,在主节点崩溃之后,整个配置服务集群还是可读的。而我们也知道,在一个 MongoDB 集群里面,元数据也是读多写少的。两者一结合,整个 MongoDB 集群的可用性就提高了。
多数据中心的主从结构
在MongoDB里有一个推荐的架构
不过整除来说,大部分公司没那么多资源部署,一个简化版本就是用两个数据中心,部署一主三从或两从。
我们公司本身业务规模比较大,对MongoDB的依赖也很严重,所以我们还部署了多数据中心的主从结构。有两个数据中心(可以同域,可以异地),其中一个数据中心,部署了一主一从,另外一个数据中心部署了两个从节点。万一一个数据中心崩溃了,另一个数据中心也还是可用的。
在主从选举的时候,我们也会倾向于选择和主节点在同一个数据中心的从节点,也就是图里面深黄色的从节点。因为正常来说,同一个数据中心内部的从节点,数据会比较新。
同时为了保证在主从选举的时候优先选择同一个数据中心的节点,我们还调整了从节点的优先级。
在整个主从结构都面完了之后,你进一步总结一下。
基本上目前主流的这种大型中间件,在提高可用性上用的方法无外乎就是分片和主从结构。除了 MongoDB,类似的还有 Redis、Elasticsearch、Kafka。
引入分片
分片既可以提高可用性,也可以提高性能。在MongoDB里,引入分片比关系型数据库简单很多,可以直接说在开发新业务的时候就启用了分片功能
随着业务的增长,后面使用MongoDB的时候,都要求开启分片功能,来进一步提高可用性的性能。
另一种思路是为已有的数据添加分片功能。
最后也要总结下
目前来说,支持大数据高并发的中间件基本上也有类似的分片功能。或者说,这一类的中间件明面上都是对等结构,而对等结构里面的每一个“节点”又是一个主从集群。就算是关系型数据库的分库分表,也可以看作是这种对等结构 + 主从结构的模式。
调整写入语义
写入语义的调整一般有两种思路,一是朝着可用性的角度调整,另一个是朝着性能的角度调整。
以可用性为例
最开始的时候,我们遇到过一个 Bug,就是数据写入到 MongoDB 之后,偶尔会出现数据丢失的问题。因为之前我在 Kafka 上也遇到过类似的问题,所以我就怀疑是不是写入语义没做好。然后我就去排查,果然有发现,在这个数据丢失的场景下,Write Concern 的 w 取值不是默认的 majority,而是 1,也就是说只需要主节点写入就可以。 很明显,在这种情况下,万一写入之后主节点崩溃了,那么从节点就算被提升成主节点,也没有这一条数据。所以,后面我就把这个改回了 majority。同时我还去排查了一个 j 参数,确认设置成了 true。这样一来,数据就不太可能丢失了。