如何设计副本集
为了保证数据写入的一致性,MongoDB只允许一个primary节点,以及n个secondary节点。
那么选择多少个节点比较合适呢?
primary 选举
- 多数同意原则
必须要求多数节点同意选举,某节点才可能被选举成为primary,多数节点 = n / 2 +1;
例如 3个节点,最少2个,4个节点最少3个
为什么需要多数同意原则?因为需要保证primary只有一个!
- 一票否决
只要有一个节点否决,那么此次选举则失败,重新选举。 - 自我申请选举
节点连接不上primary时,就会发起选举,并征求所有节点的同意。 - 同意其它节点选举的条件
主要有三个,1. 判断是否可以连上primary 2. 判断是否有优先级更高的可用节点 3. 判断这个节点是否同步了最新的操作。
所有的副本集数据同步操作都按照时间戳进行顺序排列,比如等数据同步操作全部完成之前,所有选举都会被否决。
节点数量选择
数量的选择通常需要考虑的几个因数:
- 尽可能保证选举能正常完成
这个保证就有很多考量了,比如2个节点,一旦其中一个出故障了,那就无法完成选举,也就意味着数据库无法工作。为了保证数据库的可用性,会考虑将节点分散在多个机房,那么机房之间的通信可能会中断,当部分机房出现通信孤岛时,剩下的需要具有大多数节点,那也可能一直处于无法选举成功的状态。
所以当有6个节点,两个机房时,通常会一个机房放4个节点,另外一个放2个,而不是3+3;如果有3个机房,倒可以2+2+2,某一个机房挂了,还可以继续工作(两个机房同时挂掉的概率会远小于一个的概率)
- scondary可以提供读服务,如果读请求很大,可以根据请求量增加节点数,提高请求速度。
仲裁节点
当项目很小,数据量和请求量都很小时,系统的可用性要求不高的情况下,处于对成本的考量,可能只想要2个节点,但根据选举的规则,两个节点完全无法提高数据库的可用性。
这时候就可以引入仲裁节点,仲裁节点相当于一个空节点,不存储数据,只参与投票,因此不能被客户端访问,也不能成为primary。
由于仲裁节点不存储数据,就不需要很好的硬件配置,可以大幅降低成本,可以放在一个跟其它节点不同故障域的机房,以便它具有上帝视角,即使只有两个普通节点,其中一个挂掉了,也可以正常选举出primary。
有奇数个节点时,没必要用仲裁节点,一方面是仲裁节点不会让选举更快,也不能提高数据的安全性,更要命的是,变成了偶数个节点,反而会出现n+n的情况,导致无法选举成功;偶数个节点时,可以考虑加一个仲裁节点,也仅此一个就好,没必要加多个。
仲裁节点的存在,让只用两个普通节点成为了可能,但如果可以的话,还是优先使用3个普通节点,而不是仲裁节点。因为一旦其中一个节点彻底毁坏,数据丢失,那么必须要重新加一个节点,这个时候primary需要把完整的数据同步给新节点,会大幅提高primary负荷。如果是3个普通节点,其中一个数据丢失,要换新节点,可以让scondary节点同步数据过去就好,不会增加primary的负荷。
节点优先级
优先级最高的可用节点会总是会被选上primary,赢者通吃;优先级为0的节点,只有投票权,没有被选举权(哈哈哈)。
隐藏节点
客户端无法访问,且优先级为0,通常作为备份节点或者替补节点,一旦某个节点挂了,可以恢复成正常节点,相当于热备份,减少节点不工作导致的影响
延迟节点
secondary节点通常会第一时间同步到primary的写操作,一旦有认为的意外操作,比如删除,可能会导致数据丢失。延迟节点,可以设定一个延迟同步数据的时间,意外删除操作并不会理解同步过来,也就给数据恢复提供了时间窗口。
延迟节点数据有较大延迟,因此不能让客户端访问,应当同时设置成隐藏节点。
节点索引
节点可以不需要索引。比如备份节点,只作为数据备份,那么就不需要索引了,节省空间也可以加快同步速度。
思考
为什么要搞这么多种类型的节点?
东西肯定是越简单越好,搞得复杂肯定是出于某种目的。为了简单,肯定是不想搞这么多种类型的节点,但是现实不是理想环境。当你的业务量很小的时候(业务量小,很可能意味着资金也紧张,就算有充足的资金,也应该省着点花,这年头活下去特别重要),且可预见的未来不会有太大的变化,你愿意搞3个性能一样的服务器作为数据库节点吗?出于经济考虑,肯定只想用一台服务器,副本集都不会用,甚至于数据库就部署在应用程序服务器,单台2核4G的服务器已经可以支持万级别的用户了,尤其是toB的系统,几千家商户可能一台服务器就足够了。
当用户越来越多,或者数据很重要的时候,那么数据防丢失,系统不中断就会变得越来越重要。这时候,就会考虑使用副本集,即使primary崩溃了,也会有其他的节点接替工作。副本集就最少需要三个节点,服务器成本增加2倍?不,为了节省成本,你可能只能加一台服务器,3台服务器很浪费,但根据副本集原则,最小要3个节点,所以这个时候自然就会想到,是否可以弄个假节点,部署在一台性能很差,很便宜的服务器上面,这就是仲裁节点,不需要存储数据,只作为投票人。另外,对于新增加的需要存数据的节点,也有选择,是否真的需要跟原先那台一样的配置?实际上服务器崩溃的概率很小(采用云服务器,出问题的概率会更小),而且很多业务中断一段时间也不会有太大影响,所以第二个节点,可能只需要做个数据备份的工作,只是为了防止数据丢失而已,那么第二个节点也可以采用性能很差的服务器,一方面是主要作为备份,另一方面是为了应应急。那么,你就会希望,只要第一台服务器没问题,就只让它做primary,所以你就会想到加个优先级。
再进一步,你的业务更大,请求量越来越高,大概率也开始赚钱了,那么可以进一步提升数据库节点的性能,加更多的节点。比如,第二个节点升级成和第一个一样的性能,也就是2+1(仲裁节点),这种模式可以升级很多次,比如由两个2核4G升级成4核8G、8核16G等。这个时候需要考虑个问题,万一这两个中的一个崩溃了,甚至数据丢失,虽然不会影响系统运行,但是需要及时再补一个节点,补节点需要数据拷贝,这个会很花时间也会给primary带来很大压力,所以这种情况下,你会希望,补节点的时候,数据不从primary同步,专门搞个备份节点,这时候,去掉仲裁节点就可以,维持3个节点(1primary+1secondary+1备份节点),备份节点只投票,不能被访问,也就是隐藏节点,因为它不需要被访问,所以也就没必要加索引了,毕竟索引影响写入速度,还占空间。备份节点由于只备份数据,可以选择成本很低的服务器。
延迟节点也是很必要的,避免人为的意外操作,相当于个安全垫,万一出现意外,可能只会丢失一段时间数据(时间长短取决于延迟时长)。而且通过一些log也可以找回部分数据。
方案的选择,需要根据实际情况进行权衡,不但要从技术角度,也要从成本角度(包含人力成本和经济成本),并结合业务的重要性。
对于创业型小公司,系统需要根据实际情况一步一步升级,最大化利用现有资源,避免浪费,让自己活得更久,以便迎来盈利时刻,否则可能倒在黎明前。方案没有low与不low的,只有适合不适合。
什么情况下可以读secondary节点
默认情况下,只有primary可以读写,这样可以保证数据一致性。但是secondary其实就有点浪费了,毕竟服务器没那么容易崩溃,而且当读操作远大于写操作,全部primary来处理可能会有很大压力。那么你就会想着把读请求转发给secondary,让它们分摊读请求压力,但这样也会带来很多问题,毕竟节点之间的数据同步会有时间差,正常情况下是秒级甚至毫秒级的延迟,当服务器压力大或者网络不稳定时,可能出现分钟/小时级的延迟,那么从secondary读取数据就不是最新的数据了,就有可能造成逻辑异常。那什么场景下,可以直接读取secondary呢?
- 不需要实时数据的场景,延迟几分钟也不会影响使用的场景,比如统计、报表业务,查询历史数据业务,甚至有些实时大屏显示数据。
- 定时任务,因为是定时任务,本身就不需要很高的实时性,也就没必要读取primary了。
那么什么情况下,必须从primary读取数据呢?
- 写入后的读取操作,比如用户创建成功之后的登录操作(登录涉及用户查询),这时候读取secondary就可能读取不到,就出现逻辑问题了,明明注册成功但是登录报找不到用户。
- 先判断数据状态(会变化),然后再决定是否写入。
上面的原则没啥问题,但是如何执行呢?代码也不是你一个人写的,你坚持这个原则,别的同事可能就忽略了,就算严格要求,也无法保证。要让开发者都次都去思考应该从primary读还是secondary读,这个工作量很大,也不可控。这是个很值得思考的问题!
所以,尽可能只从primary读,可以不断提高primary服务器的性能。当达到单机瓶颈时,数据量也就很大了,这时候可以考虑使用数据分片。
作为小创业公司的程序员+架构师+运维,经济成本、实现便捷性、可靠性、可拓展性都是必须考虑的问题,反而技术的高级性是最不需要考虑的问题。快速实现+低成本 是创业公司存活的两个最重要的条件。
云数据库可以节省很多人力,对于创业公司是个不错的选择。