Redis是一个开源高性能的Key-Value存储系统,虽然Redis本身具备了非常高的可用性,但是在实际应用中也会随着系统业务的复杂性以及不合理的使用,而导致很多的问题。本文将讲述如何通过混沌工程来暴露可能存在的使用风险,提升缓存问题的应急能力。
缓存重要性
Redis是一个开源高性能的Key-Value存储系统,因为其极高的读写性能,丰富的数据类型,原子性的操作以及其他特性而被广发运用。
Redis的应用场景包括且不限于以下场景:
- 用来做分布式缓存。
- 用来做分布式锁。
- 用来处理某些特定高并发业务,例如秒杀等。
示例架构
在实施混沌工程之前,先了解业务是如何使用Redis的。由于Redis最常用来做分布式缓存,本文以简单的商品查询场景为例,涉及的基本信息如下:
- 业务场景是查询商品信息,首先查询缓存;如果没有查询到,则查询数据库。
- 使用Jedis连接Redis,并且使用了Jedis-pool的技术。
- Redis是自建的集群(当然也可以使用云服务),并且使用Sentinel技术来提升集群的高可用性。更多信息,请参见Redis Sentinel文档。
示例架构图如下:
从架构图可以看出,在Jedis配置、缓存查询、网络传输、服务端处理这条链路上,每个环节都有可能出现问题。借助混沌工程可以了解到问题发生时对系统、业务的影响面是否符合预期。
梳理演练场景
对于示例应用,可以按照以下思路来梳理演练场景:
- 明确缓存监控的指标。
- 分析影响这些指标可能的因素、故障场景、参数等。
- 因为客户端层面的影响面可控,所以可以尝试从客户端层面去制造故障。
- 因为服务端出现故障更加真实,所以可以从服务端层面去制造故障,但对于问题定位和排查的要求会更高。
- 注入故障,观察指标的变化。
缓存监控指标
目前支持的可监控的缓存指标如下:
指标 | 说明 |
---|---|
缓存QPS | QPS是最通用也是最易观察的指标。 |
缓存命中率 | 缓存未命中可能会在大流量下引发穿透、击穿、雪崩等问题,如果业务没有做好应急处理,很容易压垮数据库。
|
缓存RT | 缓存响应时间。缓存RT对业务的影响分成多个方面。如果RT变化较少,对于业务访问缓存很少次数的情况下影响可控。但是如果一条请求需要多次访问缓存,那么哪怕RT只是几毫秒的增长,也会因为访问次数过多引起总的RT增长过多,引发蝴蝶效应,造成业务异常。 |
缓存成功率 | 缓存的请求成功率过低也会造成和命中率一样的后果。 |
业务成功率 | 在对业务成功率要求较高的业务当中,缓存必定是作为一个弱依赖存在。当缓存出现问题,系统应该有其他兜底策略。但是随着系统越来越复杂,改造的越来越频繁,原本预计的缓存弱依赖也会不经意间被改造成强依赖,一旦出现这种情况,就会导致业务受损。 |
影响因素
由于影响系统的因素有很多,例如机房、电源、集群服务、操作系统、应用配置等。
本文主要梳理操作系统层面和应用层面的影响因素:
- 系统层面的影响因素有网络、磁盘、IO、内存、CPU等因素。
- 应用层面的影响有超时配置、连接池配置、查询不合理等因素。
结合缓存监控指标、操作系统层面和应用层面的影响因素,本文从客户端和服务端两个角度来分析最终影响系统的因素和后果(假设业务请求QPS保持稳定)。
因素 | 模拟手段 | 可能后果 | 可能影响指标 |
---|---|---|---|
网络延迟 | 6379端口网络延迟 |
|
|
网络中断 | 6379端口网络丢包 |
|
|
单次查询耗时过长 | 如果Key过多,可以模拟Keys*查询 |
|
|
连接池设置不合理(连接池过小或者过高) | Jedis连接池占满 | 无法建立连接 |
|
缓存未命中 | Jedis返回值拦截 |
|
命中率 |
缓存异常 | Jedis抛异常 | 缓存强依赖,业务失败。 | 成功率 |
因素 | 模拟手段 | 可能后果 | 可能影响指标 | 如何改进 |
---|---|---|---|---|
磁盘空间不足 | 磁盘填充 |
|
|
|
网络异常 |
|
指定客户端请求超时。 |
|
|
连接池满 | 建立网络连接 | 无法分配新连接,客户端建连失败。 | 无 |
|
单次查询耗时过长 | 如果Key过多,可以模拟Keys*查询。 |
|
|
|
IO读写过高 | 磁盘读写IO过高 | 读写变慢,响应超时。 |
|
|
内存不足 | Jedis返回值拦截 |
|
|
|
CPU过高 | CPU满载 | 读写RT变长。 |
|
系统监控。 |
实战演练
下面通过Chaos故障演练平台从客户端层面来评测业务对Redis的合理使用。
演练价值
通过对不同网络延迟的演练,可以了解到缓存RT变化对系统造成的影响,以及防护策略有效性。随着业务规模的不断增长,这个简单的业务系统也会面临新的问题:
- 在某次重构中,又新增加了缓存查询,结果导致20 ms的延迟使得接口整体超时。
- 业务逻辑简单的时候,能够很好地分析强弱依赖。但是随着微服务的膨胀,以及代码多次重构,可能原有的弱依赖在某次变更中变成了强依赖,这种通过功能测试是无法发现的。
- 本示例Jedis设置的超时时间是100 ms,不同业务对RT的要求不同,您可以根据实际情况设置合理的超时时间。
上述的一些问题都要通过故障演练来发现。在日常的发布、架构升级中除了功能测试、性能测试的回归,还需要进行常态化的故障演练,同时演练的形态和场景复杂性也要不断扩充。对于故障演练来说,难的不是注入手段,而是对业务架构、业务场景的理解。故障注入不是目的,演练的目的是加深对系统的理解,这样当真实的问题来临时候,才能更加有信心地去处理。