Redis缓存设计与性能优化

缓存设计

缓存击穿

数据过期的时候一下子涌入大量的请求,在缓存中不存在。

  • 加互斥锁,只允许一个线程去更新缓存数据
  • 异步预热缓存

缓存穿透

  • 请求的数据根本不存在于缓存和数据库中,每次请求都会打到数据库,导致数据库压力剧增。
  • 缓存空对象。对不存在的数据也缓存一个空值,设置较短过期时间。查同一个不存在的商品进行空值延期
  • redisson布隆过滤器。
  • 参数校验。请求前先校验 userId 是否合理,比如正整数、存在合法范围

缓存雪崩

大量缓存同时失效(或者缓存层支撑不住或者宕机),请求直接打到数据库,造成数据库瞬间崩溃或响应变慢。

  • 过期时间随机化。避免大量 key 同时过期(比如加上随机 1~5 分钟)
  • 设置热点数据永远不过期。
  • 缓存预热或提前加载。系统启动时或定时任务提前加载热门缓存

热点缓存key重建优化

使用分布式锁和dcl(双重检查),只允许一个线程去更新缓存数据。好处是全局一把锁并且一把锁只锁一个对应需要重建的数据。

优化:确定得出数据的时间可以使用trylock避免大量的锁逻辑

缓存与数据库双写不一致

  1. 双写不一致
  2. 读写并发不一致

    解决方案:
  • 一致性要求不高,设置合理的过期时间+每隔一段时间触发读的主动更新
  • 延迟双删。缺点会让每个更新操作都延时
  • 高一致性的情况。对更新缓存的操作加分布式锁,优化可以使用分布式读写锁(因为写数据库要更新缓存,读数据库也要更新缓存(在缓存没对应数据的情况))
  • 引入中间件canal监听binlog修改缓存

读多写少的情况加入缓存提高性能,写多读多的情况又不能容忍缓存数据不一致就不要加缓存了。

开发规范与性能优化

键值设计

  1. key名设计:可读性、可管理性和简洁性。以业务名或者数据库名为前缀用冒号分隔拼接,key名称不宜过长。不要包含特殊字符
  2. value设计:拒绝bigkey(防止网卡流量、慢查询)
    字符串类型为超过10KB,非字符串类型元素超过5000属于大key
  3. bigkey的危害,getall导致redis阻塞,网络拥塞,过期删除时候没有过期异步删除会阻塞redis
  4. 如何优化bigkey
  • 对大key数据拆分经过hash分开存入big list: list1、list2、...listN
  • 如果bigkey不可避免,也要尽量避免getall和直接删除所有字段,可以分批删除
  • 控制key的生命周期,使用expire设置过期时间
  • 选择适合的数据类型

命令使用

  • O(N)命令关注N的数明确N的值。有遍历的需求可以使用scan代替。
  • 禁用命令,禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令
  • 合理使用select,redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。
  • 使用批量操作提高效率例如mget、mset、pipeline,但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。

原生命令是原子操作,pipeline是非原子操作。pipeline可以打包不同的命令,原生命令做不到。pipeline需要客户端和服务端同时支持。

  • Redis事务功能较弱,不建议过多使用,可以用lua替代

客户端使用

  1. 避免多个应用使用一个Redis实例
  2. 使用带有连接池的数据库,可以有效控制连接,同时提高效率

有需要时可以对连接池预热

  1. 高并发下添加熔断功能

  2. 设置合理的密码,如有必要可以使用SSL加密访问

  3. Redis过期键有三种清除策略

    • 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
    • 主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key
    • 当前已用内存超过maxmemory限定时,触发主动清理策略
    1. 过期时间的key
      • volatile-ttl:在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
      • volatile-random:就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。
      • volatile-lru:会使用 LRU 算法筛选设置了过期时间的键值对删除。
      • volatile-lfu:会使用 LFU 算法筛选设置了过期时间的键值对删除。
    2. 所有的key
      • allkeys-random:从所有键值对中随机选择并删除数据。
      • allkeys-lru:使用 LRU 算法在所有数据中进行筛选删除。
      • allkeys-lfu:使用 LFU 算法在所有数据中进行筛选删除。
    3. 不处理
      • noeviction:不会剔除任何数据,在内存上限时拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。
    • LRU 算法(Least Recently Used,最近最少使用)淘汰很久没被访问过的数据,以最近一次访问时间作为参考。(默认推荐)
    • LFU 算法(Least Frequently Used,最不经常使用)淘汰最近一段时间被访问次数最少的数据,以次数作为参考。

当存在热点数据时,这时使用LFU可能更好点。

LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降。

策略 核心思想 应对场景
LRU 淘汰最近最少使用的键 应对时间敏感的缓存访问模式
LFU 淘汰历史访问频率最低的键 应对长期热点数据的缓存使用场景

配置淘汰算法

conf 复制代码
maxmemory 256mb
maxmemory-policy allkeys-lru
bash 复制代码
CONFIG SET maxmemory 256mb
CONFIG SET maxmemory-policy volatile-lru
  1. 不配置最大内存的情况,redis会无限制的使用虚拟内存(使用硬盘模拟内存),性能下降。
相关推荐
R-sz6 小时前
redis缓存工具类【当缓存未命中时,自动调用数据加载函数从数据库获取数据并存入Redis】
redis·spring·缓存
hunjinYang6 小时前
缓存常见问题:缓存穿透、缓存雪崩以及缓存击穿
redis·缓存
极客智谷8 小时前
缓存架构方案:Caffeine + Redis 双层缓存架构深度解析
redis·缓存·架构
深栈8 小时前
MySQL进阶篇(存储引擎、索引、视图、SQL性能优化、存储过程、触发器、锁)
sql·mysql·性能优化
码上飞扬9 小时前
JavaScript性能优化实战
开发语言·javascript·性能优化
意识成长9 小时前
国产化redis 替代产品tendis 安装
redis
John_ToDebug10 小时前
Chrome 开发中的任务调度与线程模型实战指南
c++·chrome·性能优化
国际云,接待10 小时前
阿里云CDN和腾讯云CDN综合对比
运维·服务器·阿里云·性能优化·云计算·腾讯云
stormsha10 小时前
深入解析Kafka JVM堆内存:优化策略与监控实践
jvm·缓存·kafka·linq
Chasing__Dreams10 小时前
Redis--基础知识点--28--慢查询相关
数据库·redis·缓存