跳至主要內容

调优

酷风大约 6 分钟

调优

基准性能

确认Redis实例是否变慢了

  • 基准性能就是指 Redis 在一台负载正常的机器上,其最大的响应延迟和平均响应延迟分别是怎样的

  • Redis 在不同的软硬件环境下,它的性能是各不相同的。

    • 在相同配置的服务器上,测试一个正常 Redis 实例的基准性能
    • 找到你认为可能变慢的 Redis 实例,测试这个实例的基准性能
    • 如果你观察到,这个实例的运行延迟是正常 Redis 基准性能的 2 倍以上,即可认为这个 Redis 实例确实变慢了
  • 实例的响应延迟情况

    • 60 秒内的最大响应延迟
    • redis-cli -h x.168.0.x -p 6379 --intrinsic-latency 60
  • 60 秒内的最大响应延迟为 28020 微秒(28.02毫秒)

  • 查看一段时间内 Redis 的最小、最大、平均访问延迟, 每间隔 1 秒

  • redis-cli -h x.168.0.x -p 6379 --latency-history -i 1

慢日志

使用复杂度过高的命令,导致查询变慢

  • 慢日志配置(命令)

    • 命令执行耗时超过 5 毫秒,记录慢日志
      • CONFIG SET slowlog-log-slower-than 5000
    • 只保留最近 500 条慢日志
      • CONFIG SET slowlog-max-len 500
  • 获取慢日志配置 config get *slowlog*

  • 获取慢日志 最近5条

    • slowlog get 5
  • 通过查看慢日志,我们就可以知道在什么时间点,执行了哪些命令比较耗时。

  • Redis 命令有以下特点,那么有可能会导致操作延迟变大

    • 经常使用 O(N) 以上复杂度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令
    • 使用 O(N) 复杂度的命令,但 N 的值非常大
  • 解决办法

    • 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
    • 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回

Big Key

简单命令出现在慢日志中
怀疑你的实例否写入了 bigke

  • BigKey

    • Redis 在写入数据时,需要为新的数据分配内存
      • 相对应的,当从 Redis 中删除数据时,它会释放对应的内存空间。
    • 如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。
      • 同样的,当删除这个 key 时,释放内存也会比较耗时,
  • 扫描 bigkey

    • 进行 bigkey 扫描时,Redis 的 OPS 会突增
      • 为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率
        • 指定 -i 参数即可
        • 它表示扫描过程中每次扫描后休息的时间间隔,单位是秒
  • redis-cli -h x.168.0.x -p 6379 --bigkeys -i 0.01

  • 优化

    • 避免写入 bigkey
    • 删除替代
      • Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL
        • 可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响
      • Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行
    • 避免使用 bigkey 在很多场景下,依旧会产生性能问题,如数据过期、数据淘汰、透明大页等

集中过期(缓存雪崩)

  • 缓存同一时间大面积的失效

  • 过期策略

    • 被动过期
      • 访问key时判断是否过期,过期删除
    • 主动过期
      • Redis内部维护了定时任务,默认每隔 100 毫秒(1秒10次)
        • 会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key
        • 如果过期 key 的比例超过了 25%,则继续重复此过程
        • 直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环
  • 主动过期方式,Redis 主线程中执行

    • 存在可能大量删除过期key的操作
    • 此时应用程序访问会出现等待的情况
    • 造成Redis 延时
      • 如果是Big key会更耗时
  • 分析排查

    • 业务代码,是否存在集中过期 key 的逻辑
      • 一般集中过期使用的是 expireat / pexpireat 命令,你需要在代码中搜索这个关键字。
    • 运维监控
      • INFO 命令
        • expired_keys 情况,累计删除过期 key 的数量,短时间内出现了突增
  • 处理

    • 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力
    • 如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
      • lazyfree-lazy-expire yes

缓存击穿

  1. 一个热点 key 过期或被删除后

    1. 导致线上原本能命中该热点 key 的请求
    2. 瞬间大量地打到数据库上,最终导致数据库被击垮。
  2. 解决办法

    1. 检查业务代码
      1. 是否需要过期,或过期策略修改
    2. 如无需过期,确保不被删除

缓存穿透

  1. 客户端请求缓存和数据库中不存在的数据,导致所有的请求都打到数据库上。
    1. 做好参数校验,对于不合理的参数要及时 return 结束
      1. 对于后端来说,要有互不信任原则
    2. 对于查不到数据的 key,也将其短暂缓存起来。
      1. 减轻数据库压力
      2. 后期排查原因(如非法数据,可做参数校验或请求屏蔽)
    3. 提供一个能迅速判断请求是否有效的拦截机制
      1. 比如布隆过滤器,Redis 本身就具有这个功能。
      2. 布隆过滤器???

业务可靠性

  1. Redis高可用
    1. 主从 + 哨兵
    2. 集群
  2. 减少缓存依赖,如使用本地缓存Guava等
  3. 业务降级
    1. 如 限流 处理

内存上限

  1. Redis 实例设置了内存上限 maxmemory,同时设置一个数据淘汰策略
  2. 内存达到了 maxmemory 后,每次写入新数据,操作延迟变大了
    1. 当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来。
    2. 踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略
    3. 一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略
allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的 
keyvolatile-lru:只淘汰最近最少访问、并设置了过期时间的 
allkeys-random:不管 key 是否设置了过期,随机淘汰 key
volatile-random:只随机淘汰设置了过期时间的 key
  1. 解决
    1. 避免big key
    2. 更换淘汰策略,如 随机淘汰比LRU要快很多 (视业务情况调整)
    3. 拆分实例,把淘汰 key 的压力分摊到多个实例上
    4. Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行
      1. lazyfree-lazy-eviction yes

网络带宽过载

  • 如果网络 IO 存在瓶颈,那么也会严重影响 Redis 的性能。

??? fork耗时严重

??? 开启内存大页

??? 开启AOF

??? 绑定CPU

??? 使用Swap

??? 碎片整理

参考