1 缓存处理流程

  • 前台请求,后台先从缓存中读取数据,如果取到直接返回;如果取不到就从数据库中读取,如果数据库中有,就将数据更新到缓存中,同时返回结果;如果数据库中也没有,直接返回空结果。

缓存处理流程.png

2 缓存预热

2.1 问题现象

  • Redis 服务器启动后迅速宕机。

2.2 问题排查

  • ① 请求数量较高。
  • ② 主从之间数据吞吐量较大,数据同步操作频度较高。

2.3 解决方案

  • 前置准备工作
    • ① 日常例行统计数据访问记录,统计访问频度较高的热点数据。
    • ② 利用 LRU 数据删除策略,构建数据留存队列,例如:storm 和 kafka 配合。
  • 准备工作:
    • ① 将统计结果中的数据分类,根据级别,Redis 优先加载级别较高的热点数据。
    • ② 利用分布式多服务器同时进行数据读取,提高数据加载过程。
  • 实施:
    • ① 使用脚本程序固定数据预热过程。
    • ② 如果条件允许,使用了 CDN(内容分发网络),效果会更好。

2.4 总结

  • 缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统中,避免用户在请求的时候,先查询数据库,然后再将数据缓存的问题!这样,用户可以直接查询事先被预热的缓存数据。

3 缓存雪崩(cache avalanche)

3.1 问题现象

  • 系统平稳运行过程中,忽然数据库的连接量增加,应用服务器无法及时处理请求,大量 408 、500 错误页面出现,导致客户反复刷新页面获取数据,进而引起数据库崩溃、应用服务器崩溃,重启应用服务器无效,Redis 服务器崩溃,Redis 集群崩溃,重启数据库后再次被瞬间流量放倒。

缓存雪崩.png

3.2 问题排查

  • 在一个较短的时间内,缓存内大量的 key 同时过期:
    • ① 此周期内请求访问过期的数据,Redis 没有命中,Redis 向数据库获取数据。
    • ② 数据库同时受到大量的请求无法及时处理,Redis 大量请求被积压,开始出现超时现象,数据库流量激增,数据库崩溃。
    • ③ 数据库重启后依然面对缓存中没有数据可用,进而导致 Redis 服务器资源被严重占用,Redis 服务器崩溃,Redis 集群呈现崩塌,集群瓦解。
    • ④ 应用服务器无法及时获取数据响应请求,来自客户端的请求越来越多,应用服务器崩溃。
    • ⑤ 应用服务器、Redis、数据库全部重启,效果不理想。
  • 总而言之:短时间范围内,大量key集中过期

3.3 解决方案(道)

  • ① 更多的页面静态化处理,比如:前端进行节流和防抖动处理。
  • ② 构建多级缓存架构:Nginx 缓存 + Redis 缓存 + Ehcache 缓存。
  • ③ 检测 MySQL 严重耗时业务进行优化:
    • 对数据库的瓶颈排查:超时查询、耗时较高的业务等。
  • ④ 灾难预警机制:
    • 监控 Redis 服务器的性能指标
      • CPU 占用、CPU 使用率。
      • 内存容量。
      • 查询平均响应时间。
      • 线程数。
  • ⑤ 限流、降级:短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,等到业务低速运转后再逐步放开访问。

3.4 解决方案(术)

  • ① Redis 的逐出算法策略 volatile-lru 和 volatile-lfu 切换。
  • ② 数据有效期调整:
    • 根据业务数据有效期进行分类错峰:A 类 90 分钟,B 类 80 分钟,C 类 70 分钟。
    • 过期时间使用固定时间 + 随机值的形式,避免发生 key 集中到期的现象。
  • ③ 超热数据使用永久 key 。
  • ④ 定期维护(自动 + 人工):对即将过期数据进行访问量分析,确认是否延时,配合访问量统计,做热点数据的延时。
  • ⑤ 使用锁或队列(慎用,效率低):用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况。

3.5 总结

  • 缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力,如果能够有效避免过期时间集中,可以有效解决雪崩现象的出现,配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。

4 缓存击穿(hotspot invalid)

4.1 问题现象

  • 系统平稳运行过程中,数据库连接量瞬间激增,Redis 服务器无大量 key 过期,Redis 内存平稳、无波动,Redis 服务器 CPU 正常,但是,数据库崩溃。

缓存击穿.png

4.2 问题排查

  • Redis 中某个 key 过期,这个 key 的访问量巨大,多个请求从服务器直接压到 Redis 后,都没有命中,Redis 在短时间内发起了大量对数据库同一数据的访问。
  • 总而言之:单个 key 高热数据,key 过期

4.3 解决方案(术)

  • ① 预先设定热门数据:
    • 以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息 key 的过期时长。
    • 注意:购物节不仅仅指当天,以及后续的若干天,访问峰值呈现逐渐降低的趋势。
  • ② 现场调整:监控访问量,对自然流量激增的数据延长过期时间或设置为永久 key 。
  • ③ 后台刷新数据:启动定时任务,高峰期来了之前,刷新数据有效期,确保不丢失。
  • ④ 二级缓存:设置不同的失效时间,保证不会被同时淘汰。
  • ⑤ 加锁:分布式锁,防止被击穿,需要注意性能瓶颈,慎重。

4.4 总结

  • 缓存击穿是单个高热数据过期的瞬间,数据访问量较大,没有命中 Redis 后,发起了大量对同一数据的数据库访问,导致对数据库服务造成压力。
  • 应对策略应该在业务数据分析和预防方面进行,配合运行监控测试号和即时调整策略,毕竟单个 key 的过期监控难度较高,配合雪崩处理策略即可。

5 缓存穿透(cache penetration)

5.1 问题现象

  • 系统平稳运行过程中,应用服务器流量随时间增量较大,Redis 服务器命中率随时间逐步降低,Redis 内存平稳、内存无压力,Redis 服务器 CPU 占用激增,数据库服务器压力激增,数据库崩溃。

缓存穿透.png

5.2 问题排查

  • ① Redis 中大面积出现没有命中。
  • ② 出现非正常的 URL 访问。

5.3 问题分析

  • 获取的数据在数据库中不存在,数据库没有查询到对应的数据,Redis 获取到 null 数据没有进行持久化,直接返回(黑客攻击服务器)。

5.4 解决方案(术)

  • ① 缓存 null :对查询结果为 null 的数据进行缓存(长期使用,定期清理),设定短时限,如:30 ~ 60 秒,最高 5 分钟。
  • ② 白名单策略:
    • 提前预热各种分类数据 id 对应的 bitmaps ,id 作为 bitmaps 的 offset,相当于设置了数据白名单。当加载正常数据时,放行;加载异常数据的时候直接拦截(效率偏低)。
    • 使用布隆过滤器。
  • ③ 实时监控:
    • 实时监控 Redis 命中率(业务正常范围内,通常会有一个波动值)和 null 数据的占比。
      • 非活动时段波动:通常检测 3 ~ 5 倍,超过 5 倍纳入重点排查对象。
      • 活动时段波动:通常检测 10 ~ 50 倍,超过 50 倍纳入重点排查对象。
    • 根据倍数不同,启动不同的排查流程,然后使用黑名单进行防控。
  • ④ key 加密:问题出现后,临时启动防灾业务 key ,对 key 进行业务层传输加密服务,设定校验程序,过来的 key 检测,例如:每天随机分配 60 个加密串,挑选 2 ~ 3 个,混淆到页面数据 id 中,发现访问 key 不满足规则,驳回数据访问。

5.5 总结

  • 缓存击穿访问了一个不存在的数据,跳过了合法数据的 Redis 数据缓存阶段,每次访问数据库,都会对数据库造成压力。
  • 通常此类数据刚出现的时候是一个较低的值,慢慢会提升,当出现此类情况时,及时报警,应对策略在临时预案防范方面多做文章。
  • 无论黑名单还是白名单,都会对整体系统造成压力,警报解除后尽快移除。

6 性能指标监控

6.1 监控指标

6.1.1 性能指标:Performance

名称 描述
latency 响应请求的平均时间
instantaneous_ops_per_sec 平均每秒处理请求总数
hit_rate(calculated) 缓存查询命中率(通过查询总次数与查询得到非nil数据总次数计算而来)

6.1.2 内存指标:Memory

名称 描述
used_memory 当前内存使用量
mem_fragmentation_ratio 内存碎片率(关系到是否进行碎片整理)
evicted_keys 为避免内存溢出删除的key的总数量
blocked_clients 基于阻塞操作(BLPOP等)影响的客户端数量

6.1.3 基本活动指标:Basic Activity

名称 描述
connected_clients 当前客户端连接总数
connected_slaves 当前连接slave总数
master_last_io_seconds_ago 最后一次主从信息交换距现在的秒
keyspace key的总数

6.1.4 持久性指标:Persistence

名称 描述
rdb_last_save_time 当前服务器最后一次RDB持久化的时间
rdb_changes_since_last_save 当前服务器最后一次RDB持久化后数据变化总量

6.1.5 错误指标:Error

名称 描述
rejected_connections 被拒绝连接的客户端总数(基于达到最大连接值的因素)
keyspace_misses key未命中的总次数
master_link_down_since_seconds 主从断开的秒数

6.2 监控方式

6.2.1 概述

  • 工具:
    • CloudInsight Redis
    • Prometheus
    • Redis-stat
    • Redis-faina
    • RedisLive
    • zabbix
  • 命令:
    • benchmark。
    • redis-cli:
      • monitor:启动服务器调试信息。
      • slowlog:慢日志。

6.3.2 benchmark

  • 命令:
  1. redis-benchmark [-h ] [-p ] [-c ] [-n <requests]> [-k ]
  • 作用:测试当前服务器的并发性能。
  • 参数:

benchmark参数.png

  • 示例:50 个连接,10000 次请求对应的性能
  1. redis-benchmark

benchmark 示例1.gif

  • 示例:100 个连接,5000 次请求对应的性能
  1. redis-benchmark -c 100 -n 5000

benchmark 示例2.gif

6.3.3 monitor

  • 命令:
  1. monitor
  • 作用:打印服务器调试信息。

  • 示例:

  1. ./redis-cli
  1. monitor

monitor 示例.gif

6.3.4 slowlog

  • 命令:
  1. slowlog [get|len|rest]
  • 参数:
    • get:获取慢查询日志。
    • len:获取慢查询日志条目数。
    • reset:重置慢查询日志。
  • 相关配置:
  1. #设置慢查询的时间下线,单位:微妙
  2. slowlog-log-slower-than 1000
  1. #设置慢查询命令对应的日志显示长度,单位:命令数
  2. slowlog-max-len 100