1. 什么是redis
1.1 Redis简介
Redis(Remote Dictionary Server)是一个开源的、基于内存的键值存储系统,常被用作数据库、缓存和消息代理。它支持多种数据结构,如字符串、哈希、列表、集合等,并提供持久化、高可用性和分布式功能。
这是redis官网: Redis中文网https://redis.net.cn/
1.2 Redis的数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
String(字符串)
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
实例
- redis 127.0.0.1:6379> SET name "redis.net.cn"
- OK
- redis 127.0.0.1:6379> GET name
- "redis.net.cn"
在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为redis.net.cn。
Hash(哈希)
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
实例
- redis 127.0.0.1:6379> HMSET user:1 username redis.net.cn password redis.net.c
n points 200- OK
- redis 127.0.0.1:6379> HGETALL user:1
- 1) "username"
- 2) "redis.net.cn"
- 3) "password"
- 4) "redis.net.cn"
- 5) "points"
- 6) "200"
- redis 127.0.0.1:6379>
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HEGTALL 命令,user:1 为键值。
每个 hash 可以存储 232 - 1 键值对(40多亿)。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。
实例
- redis 127.0.0.1:6379> lpush redis.net.cn redis
- (integer) 1
- redis 127.0.0.1:6379> lpush redis.net.cn mongodb
- (integer) 2
- redis 127.0.0.1:6379> lpush redis.net.cn rabitmq
- (integer) 3
- redis 127.0.0.1:6379> lrange redis.net.cn 0 10
- 1) "rabitmq"
- 2) "mongodb"
- 3) "redis"
- redis 127.0.0.1:6379>
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
Set(集合)
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
sadd 命令
添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误。
- sadd key member
实例
- redis 127.0.0.1:6379> sadd redis.net.cn redis
- (integer) 1
- redis 127.0.0.1:6379> sadd redis.net.cn mongodb
- (integer) 1
- redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
- (integer) 1
- redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
- (integer) 0
- redis 127.0.0.1:6379> smembers redis.net.cn
- 1) "rabitmq"
- 2) "mongodb"
- 3) "redis"
注意:以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zadd 命令
添加元素到集合,元素在集合中存在则更新对应score
- zadd key score member
实例
- redis 127.0.0.1:6379> zadd redis.net.cn 0 redis
- (integer) 1
- redis 127.0.0.1:6379> zadd redis.net.cn 0 mongodb
- (integer) 1
- redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
- (integer) 1
- redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
- (integer) 0
- redis 127.0.0.1:6379> ZRANGEBYSCORE redis.net.cn 0 1000
- 1) "redis"
- 2) "mongodb"
- 3) "rabitmq"
2.redis缓存穿透
缓存穿透:
查询一个不存在的数据,mysal查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库
这种情况可能由恶意攻击或频繁查询无效数据引发,给数据库带来巨大压力。
缓存穿透的典型场景
- 恶意攻击:攻击者故意请求不存在的数据(如无效ID或随机键)。
- 业务错误:用户输入非法参数(如负数ID),而系统未校验。
缓存穿透的解决方案
布隆过滤器(Bloom Filter)
在缓存层前加布隆过滤器,快速判断数据是否存在。若布隆过滤器返回“不存在”,则直接拒绝请求;若返回“可能存在”,才查询缓存或数据库。布隆过滤器需定期同步数据库中的合法键。
优点:内存占用少,没有多余的key
缺点:实现复杂,存在误判率(误差)
缓存空对象
即使数据库查询结果为空,仍将空结果(如NULL
或特殊标记)写入缓存,并设置较短过期时间。后续请求会命中空对象,避免重复穿透。需注意内存占用和数据一致性问题。
优点:简单易操作
缺点:消耗内存,可能发生不一致问题
3.redis缓存击穿
缓存击穿:
给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮
缓存击穿的核心问题
- 热点数据集中访问:高并发请求同时查询同一个不存在/已失效的缓存键。
- 瞬时数据库压力:大量请求直接落到数据库,可能引发连接池耗尽或性能瓶颈。
- 恶性循环:数据库响应变慢进一步加剧请求阻塞,形成连锁反应。
缓存击穿的解决方案:
互斥锁(Mutex Lock)
当缓存失效时,通过分布式锁(如Redis的
SETNX
)确保只有一个请求能访问数据库并重建缓存,其他请求等待或轮询缓存。
优点:保证数据强一致性
缺点:可能产生等待延迟,性能差
逻辑过期时间
缓存数据不设置物理TTL,而是嵌入逻辑过期时间字段。业务代码判断是否过期,若过期则触发异步重建,未过期直接返回旧数据。
优点:高可用性,性能比较好
缺点:实现复杂度较高,不能保证数据绝对一致
4.redis缓存雪崩
缓存雪崩:
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
缓存雪崩的常见原因
- 缓存集中过期:大量缓存键设置相同的过期时间,导致同时失效。
- 缓存服务宕机:Redis等缓存服务集群故障,无法提供服务。
- 热点数据并发:突发流量导致缓存未命中,请求直接压垮数据库。
缓存雪崩解决方案:
分散过期时间
为缓存键设置随机的过期时间(TTL),避免同时失效。例如在原定过期时间基础上增加随机值
高可用设计
对Redis等缓存服务采用集群模式(如哨兵或Cluster),避免单点故障。同时设置从库快速切换机制。
熔断与降级机制
在数据库压力过大时,通过熔断器(如Hystrix)暂时拒绝请求或返回降级内容(如默认值、缓存旧数据),保护数据库。
多级缓存架构
构建本地缓存(如Caffeine)+分布式缓存(如Redis)的多层体系。当分布式缓存失效时,本地缓存仍能缓解部分压力。
5.redis双写一致性
双写一致性:
当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致
读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间。
写操作:采用 延时双删策略
延时双删策略
写入数据库前先删除Redis缓存 写入数据库后再次删除Redis缓存(延迟一定时间) 设置延迟是为了确保读请求完成并可能将旧数据写入缓存
双写强一致性
采用分布式锁
但是由于缓存都是“读多写少”我们可以采用以下两种情况:
共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作
注意事项
强一致性方案通常影响性能,
6.redis持久化
Redis持久化的两种主要方式
Redis提供了两种主要的持久化机制:RDB(Redis Database)和AOF(Append Only File)。这两种方式各有优缺点,适用于不同的场景。
RDB持久化
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据简单来说就是把内存中的。
RDB的配置通常在redis.conf文件中进行:
save 900 1 # 900秒内至少有1个键被改动
save 300 10 # 300秒内至少有10个键被改动
save 60 10000 # 60秒内至少有10000个键被改动
RDB的执行原理
basave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write技术
- 当主进程执行读操作时,访问共享内存
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作,
RDB的优点包括:
- 紧凑的单一文件便于备份和灾难恢复
- 最大化Redis性能,因为父进程不需要参与磁盘I/O
- 重启时恢复大数据集速度比AOF快
RDB的缺点包括:
- 可能丢失最后一次快照后的数据
- 数据集较大时,fork子进程可能耗时较长
AOF持久化
AOF持久化记录服务器接收到的每个写操作命令,并在服务器启动时重新执行这些命令来恢复数据。可以吧AOF看做是 命令日志文件
AOF的配置选项:
appendonly yes # 启用AOF
appendfsync everysec # 每秒同步一次
# appendfsync always # 每次写操作都同步
# appendfsync no # 由操作系统决定同步时机
AOF的优点包括:
- 更好的持久性保障,可配置为每秒或每次操作同步
- AOF日志是仅追加的,没有寻址问题
- 当AOF文件过大时,Redis会自动重写AOF文件
AOF的缺点包括:
- AOF文件通常比RDB文件大
- 根据使用的fsync策略,AOF可能比RDB慢
- 特定命令下可能遇到AOF重写bug
混合持久化模式
Redis 4.0开始支持RDB-AOF混合持久化模式。这种模式下,AOF重写时会先生成RDB格式的数据,再将增量命令以AOF格式追加。
启用混合持久化的配置:
aof-use-rdb-preamble yes
这种模式结合了RDB的快速恢复和AOF的低数据丢失风险,是目前推荐的持久化方案。
RDB和AOF的对比
7.redis数据过期策略
Redis数据过期策略
Redis通过两种主要机制处理键的过期:被动过期和主动过期。
被动过期(惰性删除)
当客户端尝试访问一个键时,Redis会检查该键是否设置了过期时间且已过期。如果过期,Redis会立即删除该键并返回空值。
这种方式节省CPU资源,但可能导致大量过期键占用内存直到被访问。
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放
主动过期(定期删除)
Redis每隔一段时间(默认每秒10次)随机抽取一定数量的键(默认20个)检查是否过期。如果发现过期键,立即删除。
如果抽样中过期键比例超过25%,会继续抽样直到比例低于25%。
定期清理有两种模式:
- SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz 选项来调整这个次数
- FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
8.redis数据淘汰策略
数据淘汰策略
当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
Redis支持8种不同的策略来删除key
noeviction
默认策略,不淘汰任何数据。当内存不足时,新写入操作会返回错误(如OOM)。适用于不允许数据丢失的场景。volatile-lru
从已设置过期时间(TTL)的键中,淘汰最近最少使用(LRU)的键。适合对冷数据敏感的场景。allkeys-lru
从所有键中淘汰最近最少使用的键,无论是否设置过期时间。适用于缓存场景,优先保留热点数据。volatile-lfu
从已设置过期时间的键中,淘汰使用频率最低(LFU)的键。适合访问频率差异明显的场景。allkeys-lfu
从所有键中淘汰使用频率最低的键。适用于需要长期保留高频访问数据的场景。volatile-random
随机淘汰已设置过期时间的键。适合对淘汰顺序无特殊要求的场景。allkeys-random
随机淘汰任意键。适用于数据重要性均匀分布的情况。volatile-ttl
优先淘汰剩余生存时间(TTL)最短的键。适合需要快速清理过期数据的场景。
配置方法
在redis.conf
文件中设置以下参数:
maxmemory <bytes> # 指定最大内存限制(如100mb)
maxmemory-policy <策略名> # 如allkeys-lru
选择建议
- 优先使用 alkeys-lru 策略。充分利用 LRU 算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
- 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用 alkeys-random,随机选择淘汰。
- 如果业务中有置顶的需求,可以使用 volatile-lru 策略,同时置顶数据不设置过期时间,这些数据就一直不被删除会淘汰其他设置过期时间的数据。
- 如果业务中有短时高频访问的数据,可以使用 alkeys-lfu 或 volatile-lfu 策略
注意:LRU/LFU算法均为近似实现,Redis使用采样法平衡性能与精度。
9.redis分布式锁
10.面试问题
不定时更新!!!