【/s2/】案例一:【/s2/】某产品线开发者搭建了一个庞大的价格存储系统。底层是关系数据库,只用来处理一些事务性操作,存储一些基础数据。
在关系数据库之上还有一套MongoDB。由于MongoDB的文档数据结构,这使得它们易于使用,并且还可以支持一定量的并发。
大多数情况下,大量数据计算后,结果可以重用,但详细数据会频繁更新,所以他们在MongoDB上设置了Redis缓存。
这就形成了数据库→MongoDB→Redis的电影方式。首先应该对方案本身进行评估并不是本文的重点。我们来看看Redis的情况。
因为数据量巨大,需要200GB的Redis,Redis是真实调用过程中请求最多的点。
当然,如果Redis失败了,也会有备用方案。将数据从后台MongoDB和数据库重新加载到Redis就是这样一个上线的简单计划。
这个系统刚开始运行的时候,一切都很好,只是运维同学有点傻眼。200GB Redis单服务器做的,失败可能性太大。
所以建议你分块来看,不了解的话会很震惊。使用的种类太多了,尤其是一些类似使用消息队列的场景。
由于开发生对Redis的使用不够重视,盲目滥用,一锤子就搞定了,弄得很棘手。
一些幸运的想法是会传染的。这个时候大家都是侥幸和偷懒,心想“这应该没问题吧。以后再说吧。先做主从,挂了就开始。”这种侥幸心理也是对Redis的虚假信心。无知的人无所畏惧。
可惜的是,事情总是怕来的。当大家都兴高采烈、肆无忌惮地使用它们的时候,系统中的一个重要节点MongoDB却因为系统内核版本的一个Bug而挂掉了!(这里就不多说MongoDB了。也是好玩的哭器)。
当然,这对于每天和故障为友的运维同学来说不算什么,对于整个系统来说也不是什么大问题,因为大部分请求调用都是在顶级Redis中完成的,只要做一定的降级,MongoDB集群拉起来之后自然就好了。
但是这个时候,别忘了Redis是一个200G-g的Redis,带一个奴隶。
所以这个时候Redis绝对没有问题。一旦出现故障,所有请求都会立即命中最底层的关系数据库。在这么大的压力下,数据库会瞬间瘫痪。
然而,因为害怕有事情发生,所以出了问题:主从Redis之间的网络出现了一点小动荡。想想主从同步这么大的事。网络一旦动荡会怎样?
主从同步失败,同步失败直接开启完全同步,于是200GB Redis瞬间启动完全同步,网卡瞬间充满。
为了保证Redis能够继续提供服务,运维同学直接关闭了从机,主从同步不存在,流量也恢复正常。但是主从备份架构变成了独立的Redis,心还悬着。
俗话说,祸不单行。由于下层的降级,Redis的并发运行增加到了每秒4万多,AOF和RDB图书馆显然承受不了。
同样,为了保证持续服务,运维同学还关闭了AOF和RDB的数据持久化,连最后一道保护都没有了(其实这道保护也没用,200GB Redis的恢复太大了)。
至此,这个Redis变成了一个完整的单机内存类型,没办法,只能祈祷它不要挂了。
挂了很久,直到MongoDB集群修复。这么幸运,没什么大事,但是你心里会踏实吗?答案是否定的。
这个案例的主要问题是:过度依赖Redis。Redis看似给系统带来了简单方便的性能提升和稳定性,但在使用中缺乏对不同场景数据的分离,造成了逻辑单点问题。
当然,我们可以通过更合理的应用架构设计来解决这个问题,但这种解决方案不够优雅和彻底,也增加了应用层架构设计的麻烦。
Redis的问题要在基础缓存层解决,这样即使有类似情况也没有问题。
因为基础缓存层已经能够适应这样的用法,所以也会让应用层的设计变得更简单(简单一直是架构设计的追求,Redis的大量随机使用本身就是追求简单的副产品,那我们为什么不把这种简单变成现实呢?)
情况二:我们来看第二种情况。某部门利用其现有的Redis服务器做了一套日志系统,先将日志数据存储到Redis中,然后通过其他程序读取数据,进行分析计算,用其做数据报表。
当他们完成这个项目时,这个日志组件让他们觉得他们仍然在享受它。他们都认为这是一种很好的做法。他们可以很容易地记录日志并快速分析。用的是什么公司的分布式日志服务?
随着时间的推移,这个Redis上已经悄然挂载了上千个客户端,每秒上万个并发,系统单核CPU利用率接近90%。这个时候这个Redis已经开始不堪重负了。
最后压垮骆驼的最后一根稻草来了,一个程序给这个日志组件写了一个7MB的日志(哈哈,这个容量可以用来写小说了,这是什么日志)。
所以Redis被封了。一旦被阻止,数千个客户端无法连接,所有日志记录操作都会失败。
其实日志本身的故障应该不会影响正常的业务,但是因为这个日志服务不是公司标准的分布式日志服务,所以很少有人关注。
开始写的开发同学不知道会有这么大的使用量,运维同学也不知道这个非法日志服务的存在。
服务本身没有很好地设计容错,所以它直接在记录日志的地方抛出异常。导致全公司相当一部分业务系统瘫痪,监控系统“5XX”错误激增。
一群人欲哭无泪,调查问题压力很大。但由于灾区太广,排查的压力可想而知。
在这种情况下,似乎是一个日志服务没有做好或者开发过程管理不到位,很多日志服务也是用Redis作为缓冲来收集数据,看起来是没有问题的。
事实上,如此大规模、大流量的日志系统,从采集到分析都要仔细考虑的技术点是巨大的,不仅仅是简单的写性能问题。
在这种情况下,Redis为程序带来了超简单的性能解决方案,但这种简单是相对的,是受场景限制的。
在这里,这种简单是毒药。无知吃了会死的。就像“一条可以在小河沟里嚣张的小鱼。那是因为它没见过海,等它到了海边……”。
本案的另一个问题:非法日志服务的存在,表面上是管理问题,实际上是技术问题。
因为Redis的使用不能像关系数据库一样由DBA监管,它的操作人员无法管理和提前知道里面放了什么数据,开发人员可以将数据写入Redis中使用,无需任何声明。
所以这里我们发现,如果没有这些场景的管理,Redis的使用在长期使用中很容易失控。我们需要一个透明层来管理和控制Redis的使用。
两个小例子说明,在Redis被误用的年代,使用它的兄弟们一定很痛苦,遭受了各种故障的狂轰滥炸:
Redis 被 Keys 命令堵塞了
Keepalived 切换虚 IP 失败,虚IP被释放了
用 Redis 做计算了,Redis 的 CPU 占用率成了 100% 了
主从同步失败了
Redis 客户端连接数爆了
……
如何改变Redis不好用的误区?
这种混乱一定是不可能继续下去的,至少在同程中是这样的。这种使用方式不能再继续下去了,用户开始从喜欢走向痛苦。
我该怎么办?这是一件很沉重的事情:“一个被人搞得乱七八糟的系统,就像一桌子烧坏了的菜。你很难回到烤箱,让人鼓掌”。
关键是已经用了,不可能把所有系统都停了,等新系统上线再瞬间切换,好吗?这是什么工作:“在高速公路上换轮胎”。
但是当问题出现的时候,总是要解决的。经过思考,我们可以总结出以下几点:
必须搭建完善的监控系统,
在这之前要先预警,不能等到发生了,我们才发现问题。
控制和引导 Redis 的使用,
我们需要有自己研发的 Redis 客户端,在使用时就开始控制和引导。
Redis的部分角色要改,
将 Redis 由 Storage 角色降低为 Cache 角色。
Redis 的持久化方案要重新做,
需要自己研发一个基于 Redis 协议的持久化方案让使用者可以把 Redis 当 DB 用。
Redis 的高可用要按照场景分开,
根据不同的场景决定采用不同的高可用方案。
剩下的开发学生的时间不多了,只有两个月的时间来完成这些事情。还是很有挑战性的。是时候考验开发生这个轮胎能不能换了。
学生开始开发我们自己的Redis缓存系统。让我们来看看这个代号为Phoenix的缓存系统的第一个版本:
首先,监控系统。总体来说,原来开源的Redis监控只是一些监控工具,不能算作一个完整的监控系统。当然,这种监控是从客户端到返回数据的全链路全方位监控。
其次,重建Redis客户端。现在广泛使用的Redis客户端,有的过于简单,有的过于沉重。简而言之,他们不是我们想要的。
例如BookSleeve和servicestack。Redis在下面。Net(还是有一些老的应用是用。Net在同一个进程中)。前者维持时间不长,后者直接收费。
好了,我们开发一个客户端,然后督促全公司的R&D用它来代替目前使用的客户端。
在这个客户端中,我们植入了日志记录,记录Redis的所有操作事件,比如耗时、密钥、值大小、断网等。
我们在后台收集这些有问题的事件,一个收集程序对它们进行分析和处理。同时,我们取消了直接的IP端口连接模式,通过一个配置中心来分配IP地址和端口。
当Redis出现问题需要切换时,可以直接在配置中心修改,配置中心会将新的配置推送给客户端,省去了Redis切换时需要业务员修改配置文件的麻烦。
另外,把Redis的命令操作分成两部分:
安全的命令,
对于安全的命令可以直接使用。
不安全的命令,
对于不安全的命令需要分析和审批后才能打开,这也是由配置中心控制的。
这样就解决了R&D人员在使用Redis时的规范问题,Redis被定位为缓存角色,除非有特殊要求,否则就按缓存角色处理。
最后,Redis的部署方式也做了修改。以前是Keepalived,现在改成主从+哨兵模式。
另外,我们自己也实现了Redis的碎片化。如果业务需要申请大容量的Redis数据库,Redis会被拆分成多个块,每个块的大小通过Hash算法来平衡。应用层也察觉不到这样的碎片。
当然客户端的方式不好,我们要做的是缓存,而不仅仅是Redis,所以我们会做一个Redis的代理,提供一个统一的入口。
代理可以多份部署,无论客户端连接到哪个代理,都可以获得完整的集群数据,从而基本完成根据场景选择不同部署方式的问题。
这样的代理也解决了多种开发语言的问题。比如运维系统是用Python开发的,也需要Redis,所以可以直接连接代理,再连接统一的Redis系统。
做一个客户端或者代理,既要做代理请求又要做Redis缓存的统一管理,以免混乱。
让缓存在可管理、可控的场景下稳定运行,让开发者可以继续安全、放心地使用Redis。但这个“乱”是虚拟的乱,因为它的底层是可以管理的。
系统架构图
当然,所有这些转变都需要在不影响业务的情况下进行。要实现这一点仍然有很大的挑战,尤其是碎片化。
将一个Redis拆分成多个Redis还可以使客户端正确地找到所需的密钥。这需要非常小心,因为稍有不慎,内存中的数据就会全部消失。
在这段时间里,我们开发了多种同步工具,几乎实现了Redis的主从协议,最终可以平稳地将Redis过渡到新的模式。
本文来自牙可爱的骚云吖~投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/624275.html