业务连续性专题:DBless
前言
最近遇到一个生产故障:随着业务的增长,对 DB 的查询 QPS 有持续的上涨。但是涨幅其实并不大,也就 20%左右的样子。但是由于云计算概念的流行,我们生产环境一个数据库集群中并非我们一个实例,在我们业务峰值的时候,恰好叠加了另外一个批处理业务的尖刺,导致了 DB CPU 被打满,最终引起了业务的中断。
反思
对于这么一个故障,我们其实在很多地方值得改进:
监控和预警:对于数据库实例的资源消耗情况,需要有即时的监控和预警。在有风险苗头的时候及时的介入和处置。当然,本次故障由于是一个尖刺导致的,给的反应时间并没有这么充裕。
流量隔离:在云计算的背景下,多实例共库已经不可避免。和容器一样,每个实例应该有自己的 quota。在低峰时,可以借用其他实例的资源,当高峰资源不足时,每个实例就必须严格按照自己的 quota 限流,保证整体业务的可用性。
主备机制:需要具备极端情况下将一个 DB 中部分实例切换到备库的能力。这样可以做到故障的快速恢复。
在这些常规思考的基础上,我们可以有更挖深一点:要实现高的业务连续性,每个资源都不能成为单点和瓶颈。对于计算资源我们有集群,有 Serverless 来分散风险。但是对于存储资源,虽然我们也有集群,但是由于数据同步时延的限制,数据库的集群一般不会有太多的节点,同时往往也是通过主从的方式运作。同时,上述的尖刺场景,往往也可能把一个 DB 集群都打爆。所以,对于存储的去单点和去强依赖,我们可能需要更进异步,考虑一下 DBless。
架构设计
DBless,顾名思义,就是需要考虑在 DB 不可用的情况下,我们的应用能有多大的可能性持续提供服务。
要实现 DBless,我们可以从三方面入手我们的架构设计。
首先,对于一些配置数据,我们可去数据库依赖。对于存粹的技术性配置,没有业务运营诉求的,我们就完全没有必要引入数据库。可以考虑用 etcd、git+redis、甚至直接在程序里面进行配置代码化。对于这类的技术性配置,其实只是一个只读的参数,用上述的非关系型存储会比用 DB 更好的实现高可用。
其次,对于一些有业务运营诉求的配置信息,用上述配置代码化的方式,就不是太适合了。对于这部分数据,大概率是读多写少,同时有一定的数据同步延迟的容忍度。这类数据我们需要考虑备份、缓存和降级三个手段:
备份:由于是读多写少。我们可以采用多副本的方式,提升对于单个存储不可用的忍受度。在修改的时候可以采用多写的方式,共识或是分布式事务均可。无非就是通过牺牲写的性能换取更好的读可靠性。
缓存:这个很明显,由于存在大量的读操作,对于热点数据可以采用缓存的方式,近一步降低数据库的压力。
降级:降级用来在数据库真实出现问题的时候做兜底的保护,在必要的时候可以在不影响业务连续性的前提下将 DB 访问快速的降低下来,让 DB 快速恢复。要做到能降级,就需要能实现配置信息去数据库依赖。这里有两个手段:一种可以拉长缓存的有效期甚至到永久有效,通过牺牲数据更新失效(其实也就是降级写操作)来达到保护数据库的目标;另外一种可以通过配置注入的方式,如果配置信息不多的话,将热点的配置信息 dump 成文件直接注入缓存。
最后,对于交易类信息。这部分信息读写操作比例基本对等,同时对于数据一致性的要求较高,对于数据更新延迟的容忍读较低。这部分的数据的 DBless 就不是太容易实现了,但是还是有几种方式可以考虑。
降级读操作,通过切库保证写操作的高可用。同时,更新只是记录更新的日志,待数据库恢复后进行刷盘。这种方式可以将对数据存储的操作从故障库移到临时的 FO 库或缓存中,一定程度上去除对于故障库的依赖,实现业务的连续性。
降级写操作,保证度操作的高可用。对于读的影响面更大的数据,可以通过这种方式,将每次读操作在备库或者缓存中留存快照。在数据库故障的情况下,通过快照支撑业务的连续性。
通过延迟更新保证业务连续性。对于数据更新的时效要求有一定容忍度的数据,可以采用这种延迟更新的方式,通过消息中间件或者缓存暂时记录更新请求,待 DB 恢复后进行回放。
总之,对于配置型、运营型、交易型数据,我们分别可以采用不同的手段,在一定程度上降低对于 DB 的依赖,可以更好的应对数据库故障,保障业务的连续性。
版权声明: 本文为 InfoQ 作者【agnostic】的原创文章。
原文链接:【http://xie.infoq.cn/article/930bd8eef2e9ee48a6af6e8d2】。文章转载请联系作者。
评论