设为首页 加入收藏

TOP

[原创]分布式系统之缓存的微观应用经验谈(二) 【主从和主备高可用篇】(一)
2019-09-17 17:44:49 】 浏览:45
Tags:原创 分布式 系统 微观 应用 经验谈 主从 可用

分布式系统之缓存的微观应用经验谈(二) 【主从和主备高可用篇】

 

前言


  近几个月一直在忙些琐事,几乎年后都没怎么闲过。忙忙碌碌中就进入了2018年的秋天了,不得不感叹时间总是如白驹过隙,也不知道收获了什么和失去了什么。最近稍微休息,买了两本与技术无关的书,其一是 Yann Martel 写的《The High Mountains of Portugal》(葡萄牙的高山),发现阅读此书是需要一些耐心的,对人生暗喻很深,也有足够的留白,有兴趣的朋友可以细品下。好了,下面回归正题,尝试写写工作中缓存技术相关的一些实战经验和思考。

 

正文


  在分布式Web程序设计中,解决高并发以及内部解耦的关键技术离不开缓存和队列,而缓存角色类似计算机硬件中CPU的各级缓存。如今的业务规模稍大的互联网项目,即使在最初beta版的开发上,都会进行预留设计。但是在诸多应用场景里,也带来了某些高成本的技术问题,需要细致权衡。本系列主要围绕分布式系统中服务端缓存相关技术,也会结合朋友间的探讨提及自己的思考细节。文中若有不妥之处,恳请指正。

  为了方便独立成文,原谅在内容排版上的一点点个人强迫症。

 

  第二篇这里尝试聊聊缓存的主从(Master-Slave),以及相关的高可用实现(High-Availability)(具体应用依然以Redis 举例)

  另见:分布式系统之缓存的微观应用经验谈(一) 【基础细节篇】(https://www.cnblogs.com/bsfz/p/9568591.html 


  一、先简单谈谈主从分离(Master-Slave)

    (注:由于目前个人工作中大多数情况应用的是Redis 3.x,以下若有特性关联,均是以此作为参照说明。)

 

    1.1 关于主从分离的取舍观点

 

      是否采用主从分离(这里特指读写分离),个人目前的观点是,它在很多场景里,并不是一个很好的方案。

 

      我更想说的是,甚至任何涉及数据同步的环节,包括DB读写分离、缓存数据复制等等,如果没有特殊场景强制要求,那么尽量规避。

 

      虽然在互联网的一些应用场景里,读远大于写,也演变了一套看似完整的读写实践方案,大体归为“主写从读”、“一写多读”、“多读多写”。但本质上,在大多数环境下,均存在一定缺陷,无论是基于服务底层的数据同步延迟,还是开发中的逻辑切换,都带来了一些可能本末倒置的隐患和生硬。同时,在集群模式下读写分离成本实在过高,不仅仅是一致性问题,必须慎重考虑和权衡。

 

      如果没明白读写分离方案基于什么本质什么条件、包含哪些细节问题,那你的设计可能就很勉强,甚至出现某些关键问题时,反而很难去分析和解决。去年跟一前辈朋友取经,他们一个业务服务兜兜转转实际测出的结果是读QPS约2000,写QPS不到500,这些的分离哭笑不得,程序性能也没得到优化,反而增加了完全浪费的技术成本,以及因为读写分离带来的程序本不该处理的例外问题,也是折腾。

    1.2 主从分离的一些细节

      以 Redis为例,每个Redis实例(Instance)都是作为一个节点(node),并且默认主节点(master node),它们均可以手动转换为从节点(slave node)。每个slave node只能隶属于一个 master node, 而每个master node可以拥有n个slave node。任何主从同步都离不开复制的概念,在Redis中主要命令是 slaveof host port (指定一个master即可),这样master node的数据将自动异步的同步到各个slave中,这算是最基本的形态了。

      

      在进行相关软性配置时,个人推荐最好保持配置文件(config-file)的一致,这里指多个salve node。对于slave node的操作默认是只读(read-only),也建议保持这个设置。如果更改为可写权限,那么对slave node的修改是不会反向同步至master node中的,而且就算通过其他方式实现了反向同步,中间将大量存在类似传统RDBMS里的幻读问题,这里并发不大但足够繁琐,追踪到具体原因也是得不偿失。(而对应程序开发中,往往写操作也是都直接进master node执行。)

 

      另外顺便提下,主从的硬件配置可以一致,但是我依然推荐slave node 可以稍微高一些。稍微注意,slave node的内存尽量不要小于 master node 的预算内存。

      对于node之间的数据延迟问题,外在因素一般都是网络I/O影响为主,CPU影响为次,换句话说,往往CPU的负载都足够(详见上一篇中提到的一些关联处),而网络I/O则会比较明显。在部署时候,没有特殊场景,都是同机房内联而对外隔离,但即使这样,也需要额外注意延迟的接收程度,每次同步复制的TCP数据包,并非是真正的实时处理,这个类似于之前提到的 AOF 和 RDB 的设计思想,分为实时复制和间隔性复制,前者更及时但带宽消耗大,而后者正好相反。在Redis中主要以 repl-disable-tcp-nodelay切换,默认使用前者,个人也较为推荐,但是在单次数据量较频较大,业务场景的时效要求不高,完全可以设置为后者,从而节省不少性能,也连锁提升了一定稳定性。

 

      对于拓扑结构的设计,应用最多的就是单层拓扑,针对大量类似 keys全表扫描的操作,slave node会做到分担性能压力的作用。如果还想极致一些,把整体阻塞降到最低,可以将拓扑结构转换为树状,最简单的做法,将某个slave node直接转移到最底部,但会带来更多时效上的牺牲,所以需要考虑场景的接受程度了。同时,这里可能在具体架构落地的环节里,会比较分身乏术,需要开始考虑交由专业的运维来参与部署了,涉及上下节点间的通信、带宽监控、级联之间的复制问题,以及一些更独立的高频率统计和管理。ps下,这点一直作为备用,但在截止到目前的工作生涯里还没有找到必要的机会去采用。

 

      对于复制/同步数据本身,无论是全量、还是增量,由于异步性(个人认为不可能设计为同步)和一定的时效损耗,必定存在一个偏移值(offset)。以Redis举例,master node和slave node中,各自对自己的当前复制时的offset做记录,如果两个角色的偏移值存在较大差异(可参考查询对应repl_offset),那么大概率存在频繁的阻塞,包括网络和自身脚本命令的阻塞。一般内部网络都是专线环境,并且都是独立部署,所以优先排查命令执行效率,和不必要的扫描问题(可参考上篇讨论:https://www.cnblogs.com/bsfz/p/9568591.html)。但是无论如何,延迟或者说偏移过大的问题,总不可能完全规避,所以在开发里要么利用专业的监控服务,要么使用不同驱动库定时判断,这也无疑增加了编码复杂度,哪怕一些开源库已经尽力做了一些工作。

 

  二、尝试谈谈相关的高可用(High-Availability)

 

    缓存既然作为一种通用的中间件(当然,某些场景里也可能是最后一层,见第一篇),决定了在诸多场景里其交互频率(包括QPS)大多远远高于其他服务,这就需要其具备极高的稳定性、可用性。个人在前面阐述了一些关于主从分离的细节,下面尝试谈谈

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇java Activiti6 工作流引擎 webso.. 下一篇全栈工程师的理解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目