开始表演
“喂,表弟在哪呢?赶紧过来,给你补补课,下次别再被虐了”。
上次表弟不是面试被虐了嘛,我答了一半奶茶就喝完了,这口干了肯定就说不了啊对吧。谁知道这臭小子回去找我妈告状了,说我讹他奶茶。这是人干的事嘛??

“呵,人在美国,刚下飞机”,表弟嘚瑟的说道。
“呵,我还人在蒙古,刚下航母呢,小崽子来不来,不来拉倒”。这孩子就是皮,尽学一些有的没的。
“来来来,马上,这次我准备充足些,保证您不会口干”。我心想:“这小子长大了,知道孝敬我老人家了,应该会买我最喜欢的芝芝芒芒吧”。
过了一会儿,门铃响了。只见表弟扛着一个桶装水大摇大摆的走进来,“哥,今天这水管够!”

给我气的啊!算了算了,我忍了,不然不知道这小子还会不会整出什么幺蛾子。
“上次面试被问到缓存高可用是吧,今儿哥给你说说常见的缓存高可用方案,简单的说就是数据分片+数据冗余,当然还包括一些必要的监控和数据持久化。”
客户端分片
正所谓鸡蛋不能放在一个篮子了,缓存也是如此。
假设我们就一个缓存节点,那这个节点挂了是不是就没了。请求都打到数据库去了,在高并发请求下这数据库可顶不住。
于是就可以把缓存分开放在多个节点上,这样假使其中的一个节点挂了,也不会让请求全部打到数据库上(当然,可能部分打到数据库是就有可能顶不住,缓存最重要的就是命中率,有时候命中率低一点点可能就是致命打击,需要完备的监控和多方准备)。
并且将缓存分散到多个节点还能解决单一缓存节点性能问题,即受到单机内存、网卡带宽等限制,单机所能承受的请求量有限。
那么问题来了?该如何将缓存分散到各节点上呢?
“这个我知道,一般都用 hash 算法得到 key 的 hash 值,然后再将 hash 值取模”,表弟自信的说道。

“你说的没错,不过这种分片方式当某个缓存节点宕机,或者新增某个缓存节点的时候会导致数据的大量搬迁”。
比如 1000 mod 3 = 1
,假设现在一个节点宕机了,此时计算的结果是1000 mod 2 = 0
。这就导致大量访问的节点产生变化,导致缓存miss,然后得去数据库重新加载数据到另外的节点上。
所以还有一种是一致性 hash 算法,常见的算法是通过将 值空间组织成一个虚拟的圆环,然后将节点的 ip 或者主机名做 hash ,映射到某个值上。然后将缓存 key 做 hash 之后映射到某个值做顺时针寻找,遇到的第一个节点就是要访问的节点。

并且为了使得缓存分散的更加的均匀,引入虚拟节点的概念,例如将节点的 ip 加个后缀再 hash 等方法。

因为值空间是不变的,这样的话即使某个节点挂了或者新增节点,都只会影响小部分的 key 产生搬迁,大部分老的数据其实不会搬迁。
“表哥,你有点东西啊,不愧是受过社会毒打的老油条,但是这好像有问题啊,假设节点1宕机了,此时请求到节点2,节点2的数据从数据库拉的是最新的,那节点1又恢复了,接下来的访问又回到了节点1,此时数据不就是老的嘛?”,表弟摸了摸他毛绒绒的小胡子说道。
“表弟你也有点东西啊,没错。其实不管普通 hash 还是一致性 hash,宕机的节点恢复了可能都会产生老数据的问题,因此需要给缓存加个过期时间,让它能有悔改的机会!”
这就是在客户端分片的缓存高可用了。当然还需要配备数据冗余,一般像 redis 会做个主从或者主备,像节点1一般会有两台。一台主,一台从。这样主挂了之后从机可以顶上。像 Memcached 这种没有主从机制的,就需要我们自己来维护了,比如写缓存写主又写从。
有个从机兜底,防止在负载很高的情况下,某个节点挂了然后 key 都迁移到别的节点上。即使分散均匀,别的节点也可能挡不住,然后发生雪崩。比如节点1挂了,key 均匀的分散到节点2和节点3上,但是节点2此时的负载本来就挺高的,它也顶不住挂了,然后此时key都压到了节点3 上,那节点3不也得嗝屁了嘛。
中间层分片
“那你知道在客户端分片有什么缺点不?”,我边打开水桶上面的包装边向表弟问道,又有点渴了。
“客户端分片有个缺点,就是不通用,比如我用 java 写的客户端,公司别的项目组用的是 go,那又得写一套,并且之后一个逻辑改了,多个语言的客户端还得都改一遍,难以维护”。表弟思索了一下说道。
“那该怎么办呢?”,我再追问道,有面试官那味儿了。
“可以引入个中间层?把放在客户端的路由逻辑搬到中间层中,采用通用的协议交互。这样就不用重复开发了并且易维护!” 表弟好像有点感觉了。
“哎哟,你说的没错,业界已经有很多现成的,比如 Codis,Twemproxy 等,你要是有兴趣可以自己写个?哥到时候给你提pr”。
“那在我写之前,哥?要不再说说Codis,Twemproxy?让我参考参考”,表弟试探性说道。
“我说你个鬼,这要说下去今儿这一桶水我告诉你还真不够,你自个儿研究去,对了中间层毕竟多了一层,所以性能上可能会有一些损耗”。
缓存自带分片
“除了客户端和中间层其实有些缓存组件自带分片功能,比如 redis cluster,一共16384个槽,咱们只要划分好哪些槽放在哪个实例上就好了,它帮咱们分片了。”
“还有记得我刚才说的数据冗余吗?不管是什么方式分片,数据都是要冗余的,要么是主从,要么是多副本”。
“像 redis 还有个哨兵机制,简单的说哨兵会告诉我们当前需要跟哪一台 redis 交互。哨兵会监控节点的情况,当主节点宕机的时候它会选举从节点作为新的主节点。这样咱们也就轻松了。”
不过哨兵自身也需要高可用,总的来说需要维护的东西就变多了。
“晓得了吧?数据分片+数据冗余,再配备类似哨兵这样的监控来进行主从切换,这就是缓存的高可用啦,当然有些要求高的还会再做多层缓存backup。”,说的我是真的渴了,戳了戳那桶水上的盖子,淦!这咋喝,戳都戳不动啊,家里又没饮水机。
“懂了老哥,还有分片可以在客户端分,逻辑都在客户端,并且不易跨平台,但是没啥性能损耗。而中间层分片可以跨平台,但是有性能损耗,毕竟请求都得先经过中间层来回一遍。缓存组件自带好像不错,但运维可能有点要求。” 表弟自信的总结道。
“可以可以,不枉我这一番口舌,下次记得找回场子” 。
只见老弟从背后掏出了一杯芝芝芒芒!“给,老哥”。
哇这小子不知道怎么藏的,竟然逃过了我的火眼金睛,还算这小子有良心。“走!哥带你峡谷巅峰游,今儿我小区第一皇子,EQ二连闪现大,一个天崩地裂分分钟带你超神!”
“您可拉到吧?哪次不是我村里第一上单爸爸出来抗的?”
“行行行,赶紧的上号!”。

我是 yes,从一点点到亿点点,我们下篇见。
网友评论