redis的优化思路
优化思路
CPU 相关
使用复杂度过高命令、数据的持久化,都与耗费过多的 CPU 资源有关
内存相关
bigkey 内存的申请和释放、数据过期、数据淘汰、碎片整理、内存大页、内存写时复制都与内存息息相关
磁盘相关
数据持久化、AOF 刷盘策略,也会受到磁盘的影响
网络相关
短连接、实例流量过载、网络流量过载,也会降低 Redis 性能
计算机系统
CPU 结构、内存分配,都属于最基础的计算机系统知识
操作系统
写时复制、内存大页、Swap、CPU 绑定,都属于操作系统层面的知识
常见问题
使用复杂度过高的命令
- 原因
- 经常使用 O(N) 以上复杂度的命令。Redis 在操作内存数据时,时间复杂度过高,要花费更多的 CPU 资源
- 使用 O(N) 复杂度的命令,但 N 的值非常大。Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中
- 解决方式
- 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
- 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回
操作bigkey
- 解决方式
- 尽量避免一个 key 存入过大的数据
- Redis 提供了扫描 bigkey 的命令,执行以下命令就可以扫描出,一个实例中 bigkey 的分布情况
- 如果你使用的 Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响
- 注意事项
- 对线上实例进行 bigkey 扫描时,Redis 的 OPS 会突增,为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒
- 扫描结果中,对于容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多,你还需要根据业务情况,进一步评估内存占用情况
集中过期
- 解决方式
- 检查你的业务代码,是否存在集中过期 key 的逻辑。一般集中过期使用的是 expireat / pexpireat 命令,你需要在代码中搜索这个关键字
- 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力
- 如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
实例内存达到上限
- 避免存储 bigkey,降低释放内存的耗时
- 淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整)
- 拆分实例,把淘汰 key 的压力分摊到多个实例上
- 如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)
fork耗时严重
- 控制 Redis 实例的内存:尽量在 10G 以下,执行 fork 的耗时与实例大小有关,实例越大,耗时越久
- 合理配置数据持久化策略:在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用),可以关闭 AOF 和 AOF rewrite
- Redis 实例不要部署在虚拟机上:fork 的耗时也与系统也有关,虚拟机比物理机耗时更久
- 降低主从库全量同步的概率:适当调大 repl-backlog-size 参数,避免主从全量同步
开启内存大页
- 关闭内存大页机制就可以了
开启AOF
碎片整理
网络带宽过载
其它
-
频繁短连接
-
运维监控
-
使用Swap
-
绑定CPU
缓存常见问题
缓存穿透/数据穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求
通常数据缓存的机制是:缓存未命中时,从数据库读取数据,并放入缓存中。而穿透场景,会导致频繁且无意义(空的查询结果集)的数据库访问,由于空值无法进行缓存,失去了缓存的
解决方案
- 接口层增加数据校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
- 在穿透场景中,可以将无效的Key键查询,在缓存中设置空值缓存,即【Key,Null】,用来避免穿透而频繁的访问数据库。空值缓存设置短有效期,如30秒(设置太长会导致正常情况也没法使用)
- 也可以通过布隆过滤器,来进行数据访问拦截。bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小
缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期)。这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案
- 设置热点数据永远不过期
- 接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制
- 加互斥锁
缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
解决方案
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中
- 设置热点数据永远不过期
缓存污染(或者满了)
缓存污染问题说的是缓存中一些只会被访问一次或者几次的的数据,被访问完后,再也不会被访问到,但这部分数据依然留存在缓存中,消耗缓存空间
解决方案
- 指定最大缓存设置更大
- 建议把缓存容量设置为总数据量的 15% 到 30%,兼顾访问性能和内存空间开销
- 指定缓存淘汰策略
持久化相关问题
Redis启动加载流程
存在AOF
存在
加载AOF日志
启动Redis
不存在
存在RDB快照
存在
加载RDB快照
启动Redis
不存在
启动Redis
解决方式
- 降低RDB fork的频率,比如可以手动来触发RDB生成快照、与AOF重写
- 控制Redis最大使用内存(小于10G),防止fork耗时过长
- 使用更牛逼的硬件
- 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败
实践经验
- Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回
- 自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据
- 单机如果部署多个实例,要防止多个机器同时运行持久化、重写操作,防止出现内存、CPU、IO资源竞争,让持久化变为串行
- 可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令
- RDB持久化与AOF持久化可以同时存在,配合使用