一、Redis缓存问题
- 缓存和数据库一致性问题
分布式环境下非常容易出现缓存和数据库一致性问题,如果想要缓存跟数据库强一致性,那就不要使用缓存。
我们只能通过采取合适的策略来降低缓存与数据库一致性问题的概率。
合适的策略包括合适的缓存更新策略、更新数据库后及时更新缓存、缓存失败时增加重试机制等。
二、缓存穿透、缓存击穿、缓存雪崩
(1)缓存穿透:指用户(黑客)不断请求缓存和数据库中都没有的数据。这样不断的攻击会导致数据库压力非常大,严重会击垮数据库。
解决办法:
1.在接口层增加校验,比如用户鉴权、参数做校验、不合法的校验直接return,比如id做基础校验,id<=0直接拦截。
2.高级用布隆过滤器(Bloom Filter):利用高效的数据结构和算法快速判断出你这个key是否在数据库中存在,不存在你return就好了,存在你就去查DB刷KV再return。
(2)缓存击穿:指一个Key非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个key的点上击穿了缓存。
解决办法:
设置热点数据永不过期,或者加上互斥锁就搞定了。
public static String getData(String key)throws InterruptedException {
//从Redis查询数据
String result = getDataByKV(key);
//参数校验
if (StringUtils.isBlank(result)) {
try {
//获得锁
if (reenLock.tryLock()) {
//去数据库查询
result = getDataByDB(key);
//校验
if (StringUtils.isNotBlank(result)) {
//插进缓存
setDataToKV(key, result);
}
} else {
//睡一会再拿
Thread.sleep(100L);
result = getData(key);
}
}finally {
//释放锁
reenLock.unlock();
}
}
return result;
}
(3)缓存雪崩:同一时间大面积的缓存失效,瞬间Redis跟没有一样,而且大并发的请求直接打到数据库上,数据库几乎是灾难性的。如果没有做熔断等策略,基本上就是瞬间挂一片的节奏。
解决办法:
1.在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效。
2.如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效。
3.或者设置热点数据永不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷新一下缓存就好了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。
三、Redis的内存回收机制
-
Redis的内存回收主要分为过期删除策略和内存淘汰策略两部分。
四、Redis的过期策略
策略 | 描述 |
定时过期 | 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。 该策略可以立即清除过期的数据,对内存很友好; 但是会占⽤用大量的CPU资源去处理理过期的数据,从而影响缓存的响应时间和吞吐量。 |
惰性过期 | 只有当访问一个key时,才会判断该key是否已过期,过期则清除。 该策略略可以最大化地节省CPU资源,却对内存非常不友好。 极端情况可能出现⼤量的过期key没有再次被访问,从而不会被清除,占⽤用大量内存。 |
定期过期 | 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。 该策略是前两者的一个折中⽅方案。 通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。) |
五、Redis淘汰策略
策略 | 描述 |
volatile-lru | 从已设置过期时间的KV集合中优先对最近最少使用(less recently used)的数据淘汰 |
volatile-ttl | 从已设置过期时间的KV集合中优先对剩余时间短(time to live)的数据淘汰 |
volatile-random | 从已设置过期时间的KV集合中随机选择数据淘汰 |
allkeys-lru | 从所有KV集合中优先对最近最少使用(less recently used)的数据淘汰 |
allkeys-random | 从所有KV集合中随便选择数据淘汰 |
noeviction | 不淘汰策略,若超过最大内存,返回错误信息(默认策略) |
- 备注:Redis 5默认使用noeviction淘汰策略,Redis 4.0加入了LFU(least frequency use)淘汰策略,包括volatile-lfu和allkeys-lfu,通过统计访问频率,将访问频率最少,即最不经常使用的KV淘汰。
六、Redis持久化
- Redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。
- Redis的持久策略有两种,一种是RDB,另一种是AOF。
(1)RDB:快照的形式直接把内存中的数据保存到一个dump文件中,定时保存,保存策略。
- RDB持久化策略原理:
1.Redis会生成一个子进程,子进程将内存中的数据写到磁盘上一个临时RDB文件。
2.当子进程完成写临时文件后,将原来在磁盘中的二进制文件dump.rdb替换掉。
这样的好处是可以copy-on-write(写时复制)。
- RDB优缺点:
优点:
非常适合于备份(比如:每个小时备份一次数据),即使遇上问题,也可以随时将数据还原到不同的版本。RDB非常适合灾难恢复。
缺点:
如果需要尽量避免在服务器故障时丢失数据,那么RDB无法全面恢复。
(2)AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。
- AOF原理:
每一个写命令都通过write函数追加到appendonly.aof中。
- AOF优缺点:
优点:
让Redis变成非常持久,可以设置不同的Fsync策略,AOF默认策略是每秒钟Fsync一次。(在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据)。
缺点:
AOF文件体积通常要大于RDB文件的体积。根据所使用的Fsync策略,AOF的速度可能会慢于RDB。
备注:
1)Redis默认是快照RDB的持久化。
2)当Redis重启的时候,他会优先使用AOF文件还原数据集,因为AOF文件保存的数据通常比RDB文件保存的数据集更加完整。(前提是开启了AOF配置)
3)关于如何选择哪种持久化策略,可依据以下情景:
3-1)如果你可以承受数分钟内的数据丢失,那么可以只使用RDB持久化。定时生成RDB快照非常便于进行数据备份,并且RDB恢复数据集的速度要比AOF恢复的速度快。
3-2)如果不能承受数分钟内的数据丢失,可以使用AOF,AOF将Redis执行的每一条命令追加到磁盘中,处理巨大的写入会降低Redis的性能。
3-3)建议是将两者一起用,用AOF保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的备份,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。
4)访问redis的线程太多导致崩溃,重启需要注意哪些地方
4-1)重启前的准备工作
4-1-1)数据备份与恢复
立即备份:重启前先检查持久化文件(dump.rdb、appendonly.aof)是否完整,避免数据丢失。
验证持久化配置:检查 redis.conf 中的 save 规则和 appendonly 配置,确保重启后数据能正确加载。
4-1-2)分析崩溃原因
查看日志:通过 redis-server.log 定位崩溃原因(如 OOM 错误、连接数超限、高延迟等)。
监控历史数据:使用 INFO 命令的历史记录或监控工具(如 Prometheus+Grafana)分析崩溃前的内存、连接数、QPS 等指标。
4-1-3)调整系统资源
操作系统限制:检查并调整 Linux 的文件描述符限制(ulimit -n)和 TCP 连接参数(如 net.core.somaxconn)。
资源隔离:确保 Redis 独占服务器资源,避免与其他服务竞争 CPU、内存或网络带宽。
4-2)调整 Redis 配置
4-2-1)连接数控制
maxclients
:设置合理的最大客户端连接数(默认 10000),需根据服务器内存和负载能力调整。例如:
maxclients 20000
客户端超时:通过 timeout
参数关闭闲置连接(默认 0,建议设置为 300 秒):
timeout 300
4-2-2)内存管理
maxmemory
:根据服务器内存设置上限,避免 OOM(如 16GB 服务器保留 2GB 给系统):
maxmemory 14gb
淘汰策略:根据场景选择 volatile-lru
或 allkeys-lru
,优先淘汰旧数据:
maxmemory-policy allkeys-lru
4-2-3)多线程优化(Redis 6.0+)
启用 I/O 多线程:通过多线程处理网络 I/O(CPU 密集型操作仍为单线程):
io-threads 4 # 建议设置为 CPU 核心数的 70%
4-3)重启后的监控与优化
4-3-1)实时监控关键指标
连接数:redis-cli info clients 查看 connected_clients。
内存使用:redis-cli info memory 关注 used_memory 和 maxmemory。
延迟:redis-cli --latency 或 redis-cli --latency-history 检测响应时间。
4-3-2)客户端优化
连接池:客户端使用连接池复用连接(如 Jedis、Lettuce 的池化配置)。
批量操作:使用 Pipeline 或 MGET/MSET 减少网络往返次数。
异步处理:对非实时任务改用消息队列(如 Kafka、RabbitMQ)削峰填谷。
4-3-3)架构扩展
集群模式:拆分数据到多个节点,使用 Redis Cluster 或 Codis。
读写分离:通过主从复制将读请求分发到从节点。
缓存穿透/雪崩防护:添加本地缓存(如 Caffeine)或限流器(如 Sentinel)。
5)Redis内存为什么会超,已经超了的数据怎么处理
5-1)Redis内存超限的原因分析:
5-1-1)数据量超过配置上限:Redis设置了最大内存限制(maxmemory
),若数据持续写入且未配置合理淘汰策略,内存会超限。
5-1-2)内存碎片率高:频繁增删数据可能导致内存碎片,降低内存利用率(可通过 info memory
查看碎片率)。
5-1-3)大Key或热Key占用:单个大Key(如几百MB的String)或高频访问的Key可能导致内存集中消耗。
5-1-4)客户端连接与缓冲区:大量客户端连接或输出缓冲区占用内存(如慢查询、Pub/Sub场景)。
5-1-5)淘汰策略未启用或配置不当:默认策略为 noeviction
(不淘汰数据),内存满时拒绝写入。
5-2)处理已超限数据的方案
5-2-1)配置内存淘汰策略
动态调整策略(无需重启):
CONFIG SET maxmemory-policy volatile-lru # 根据LRU淘汰有过期时间的Key
# 或
CONFIG SET maxmemory-policy allkeys-lru # 淘汰所有Key中的最近最少使用
可选策略:volatile-ttl
(淘汰即将过期的Key)、allkeys-random
(随机淘汰)等。
2-2)手动清理数据
删除无用Key:
DEL key_name # 同步删除,可能阻塞
UNLINK key_name # 异步删除(Redis 4.0+)
按模式批量删除:
redis-cli --scan --pattern "user:*" | xargs redis-cli unlink
2-3)处理大Key与优化数据结构
扫描大Key:
redis-cli --bigkeys # 快速定位大Key
优化方案:
- 拆分大Key:将Hash拆分为多个小Key。
- 使用更高效的数据结构:如用
ZSTREAM
(Redis 5.0+)替代大List存储日志。
2-4)处理内存碎片
查看碎片率:
redis-cli info memory | grep fragmentation
解决方案:
- 重启Redis:强制内存整理(需主从切换避免服务中断)。
开启自动碎片整理(Redis 4.0+):
CONFIG SET activedefrag yes
Redis讲解目录: