目录
1、主从复制(Master-Slave Replication)
Redis(Remote Dictionary Server) 是一款基于内存的高性能键值对存储数据库,同时也是一个 数据结构服务器。它支持多种复杂的数据结构,并提供高读写性能、持久化、集群、事务等功能,被广泛应用于缓存、实时数据处理、消息队列、计数器、排行榜等场景。
一、Redis 的核心特点
-
基于内存(In-Memory)
- 数据默认存储在内存中,读写速度极快(官方测试读速度可达 11 万次 / 秒,写速度可达 8 万次 / 秒)。
- 缺点:内存空间有限,数据易失性(断电丢失),需通过持久化机制(RDB/AOF)保证数据可靠性。
-
丰富的数据结构
Redis 支持多种数据结构,每种结构对应不同的应用场景:- 字符串(String):最简单的数据类型,用于存储单个值(如计数器、缓存用户信息)。
- 哈希(Hash):键值对的集合,适合存储对象(如用户详情:
user:1 {name: "Alice", age: 25}
)。 - 列表(List):有序可重复的链表,支持两端插入 / 删除,用于消息队列(如任务队列)、最新列表(如朋友圈动态)。
- 集合(Set):无序唯一的元素集合,用于去重(如统计用户唯一登录数)、交集 / 并集运算(如共同关注)。
- 有序集合(Sorted Set):带分数的有序集合,用于排行榜(如按分数排序的用户排名)。
- 其他:位图(Bitmap)、HyperLogLog(基数统计)、地理空间(Geospatial)等。
-
持久化机制
- RDB(Redis Database):定时将内存数据快照写入磁盘,适合大规模数据的备份。
- AOF(Append Only File):记录每一条写命令,重启时重新执行命令恢复数据,数据安全性更高。
- 可同时开启 RDB 和 AOF,优先使用 AOF 恢复数据。
-
单线程与高性能
- Redis 基于单线程模型处理请求(基于非阻塞 I/O 多路复用技术),避免了多线程的上下文切换开销,性能卓越。
- 单线程意味着 Redis 自身不会出现线程安全问题,但需注意多个客户端并发访问时的竞争条件(可通过事务或分布式锁解决)。
-
原生支持集群(Cluster)
- 可通过分片(Sharding)将数据分布到多个节点,支持动态扩缩容,解决单节点内存限制和高并发问题。
- 集群模式下自动实现数据分片和故障转移,保证高可用性。
-
其他功能
- 发布订阅(Pub/Sub):实现消息传递,用于实时通信(如实时聊天、通知系统)。
- 事务(Transaction):支持简单的事务操作,确保命令序列的原子性执行。
- Lua 脚本:支持在服务端执行 Lua 脚本,减少客户端与服务端的交互次数。
- 过期键(TTL):可为键设置过期时间,自动清理无效数据(如缓存的有效期)。
二、Redis 常见数据类型
数据类型 | 特点 | 典型命令 | 应用场景 | 底层实现 | 内存优化建议 |
---|---|---|---|---|---|
字符串 (String) | - 最基础类型 - 二进制安全(可存储图片、JSON 等) - 最大支持 512MB | SET key value GET key INCR key MSET k1 v1 k2 v2 | - 缓存(如用户信息) - 计数器(点赞数、访问量) - 分布式锁( SETNX ) | - 短字符串:embstr 编码(一次内存分配) - 长字符串:raw 编码(两次内存分配) | - 避免大字符串(>10KB) - 批量操作使用 MSET /MGET |
哈希 (Hash) | - 键值对集合(field→value) - 类似 Java 的 HashMap | HSET hash field value HGET hash field HGETALL hash HINCRBY | - 存储对象(如用户详情) - 存储配置项(如应用配置) | - 小哈希:压缩列表(ziplist) - 大哈希:哈希表(hashtable) | - 控制元素数量(默认 < 512) - 值长度 < 64 字节(触发压缩列表) |
列表 (List) | - 有序可重复 - 支持双向插入 / 删除 - 底层为双向链表 | LPUSH list value1 value2 RPOP list LRANGE list 0 -1 BLPOP | - 消息队列(FIFO/LIFO) - 最新动态列表(如微博时间线) | - 小列表:压缩列表(ziplist) - 大列表:快速列表(quicklist) | - 避免一次性获取全量数据(LRANGE 0 -1 )- 使用 BLPOP 实现阻塞队列 |
集合 (Set) | - 无序唯一 - 支持交集 / 并集 / 差集运算 | SADD set member1 member2 SMEMBERS set SINTER set1 set2 SCARD | - 去重(如用户标签) - 社交关系(共同好友) - 抽奖系统(随机取元素) | - 整数集合(intset):全整数元素 - 哈希表(hashtable):含字符串元素 | - 控制元素数量(>512 自动转为哈希表) - 批量操作使用 SUNIONSTORE |
有序集合 (Sorted Set) | - 每个元素关联分数(score) - 按分数排序(支持范围查询) | ZADD zset score1 member1 score2 member2 ZRANGE zset 0 -1 WITHSCORES ZINCRBY | - 排行榜(如游戏积分) - 时间线(按时间戳排序) - 带权重的任务调度 | - 跳表(skiplist)+ 哈希表 - 小数据:压缩列表(ziplist) | - 避免大范围查询(ZRANGE 参数过大)- 定期清理过期数据 |
位图 (Bitmap) | - 基于位操作的字符串 - 每个位存储 0/1 | SETBIT key offset value GETBIT key offset BITCOUNT key | - 签到统计(每日 1 位) - 用户活跃状态(亿级用户仅需 12.5MB) - 布隆过滤器 | - 基于字符串实现(本质是二进制数组) | - 按位操作,避免整键读取 - 大位图可分段存储 |
补充说明
-
数据类型选择原则
- 简单键值对:字符串(String)
- 对象存储:哈希(Hash)
- 有序列表:列表(List)或有序集合(Sorted Set)
- 去重与集合运算:集合(Set)
- 统计基数:HyperLogLog
- 位操作:位图(Bitmap)
- 地理位置:地理空间(Geospatial)
-
内存优化关键
- 压缩列表(ziplist):当哈希、列表元素较少且值较小时自动触发,内存占用更低。
- 批量操作:使用
MSET
/MGET
、HMSET
/HMGET
减少网络开销。 - 过期策略:合理设置
EXPIRE
时间,避免冷数据长期占用内存。
-
性能注意事项
- O (N) 命令:如
KEYS *
、LRANGE list 0 -1
、SMEMBERS
,可能阻塞主线程,生产环境慎用。 - 大对象:单个键值超过 10KB 时需谨慎,可能导致内存碎片或网络传输瓶颈。
- O (N) 命令:如
-
底层编码转换
Redis 会根据数据量和类型自动转换编码(如小哈希从 ziplist 转为 hashtable),可通过OBJECT ENCODING key
查看。
三、持久化机制
Redis 提供了两种持久化机制:RDB(快照持久化) 和 AOF(日志追加持久化),用于将内存中的数据持久化到磁盘,避免因进程退出或服务器宕机导致数据丢失。此外,Redis 4.0 引入了 混合持久化 模式,结合两者优势。以下是详细解析:
1、RDB(Redis Database)
核心原理
- 全量快照:将某一时刻的内存数据完整写入磁盘,生成
.rdb
文件。 - 触发方式:
- 自动触发:根据
redis.conf
中的配置规则(如save 900 1
表示 900 秒内至少 1 次写操作则触发)。 - 手动触发:
SAVE
:阻塞主线程,直到快照完成(生产环境慎用)。BGSAVE
:fork 子进程执行快照,主线程继续处理请求(推荐方式)。
- 自动触发:根据
- 执行流程:
- 主线程 fork 子进程,子进程负责写入快照(copy-on-write 机制,不影响主线程读写)。
- 子进程将内存数据序列化到临时文件。
- 替换旧的
.rdb
文件,完成持久化。
优缺点
优点 | 缺点 |
---|---|
1. 文件体积小,恢复速度快(全量加载)。 | 1. 无法实时持久化,可能丢失最近写入的数据。 |
2. 适合备份(如定时同步到远程服务器)。 | 2. fork 子进程时可能占用大量内存和 CPU。 |
3. 跨版本兼容性好(不同 Redis 版本可读取)。 | 3. 大内存实例 fork 时可能导致短暂阻塞。 |
配置参数(redis.conf)
# 自动触发规则(可配置多个条件,用空格分隔)
save 900 1 # 900秒内至少1次写操作
save 300 10 # 300秒内至少10次写操作
save 60 10000 # 60秒内至少10000次写操作
stop-writes-on-bgsave-error yes # 快照失败时禁止写入(避免数据不一致)
rdbcompression yes # 是否压缩数据(牺牲CPU换取空间)
rdbchecksum yes # 写入时校验数据(增加文件生成时间)
dbfilename dump.rdb # 快照文件名
dir ./ # 快照存储目录(需提前创建,权限可写)
适用场景
- 对数据完整性要求不高,允许丢失最近几分钟数据。
- 需定期备份(如每天一次),用于灾难恢复。
- 主从复制场景(主节点通过 RDB 同步数据到从节点)。
2、AOF(Append Only File)
核心原理
- 增量日志:将每个写命令(如
SET
/HSET
等)按协议格式追加到 AOF 文件末尾,读命令不记录。 - 重写机制(AOF Rewrite):
- 随着时间推移,AOF 文件会变庞大(如多次
INCR
同一个键),通过BGREWRITEAOF
命令优化:- 子进程重新构建键值对的最小命令集合(如用最终的
SET
替代多次INCR
)。 - 生成新的 AOF 文件,替换旧文件(不阻塞主线程)。
- 子进程重新构建键值对的最小命令集合(如用最终的
- 随着时间推移,AOF 文件会变庞大(如多次
- 触发重写方式:
- 自动触发:根据
auto-aof-rewrite-percentage
(默认 100%,即文件大小较上次重写后增长 100%)和auto-aof-rewrite-min-size
(默认 64MB,文件最小体积)。 - 手动触发:
BGREWRITEAOF
。
- 自动触发:根据
优缺点
优点 | 缺点 |
---|---|
1. 数据安全性高(可配置每秒或实时刷盘)。 | 1. 文件体积通常大于 RDB(记录所有写命令)。 |
2. 支持实时持久化(根据 fsync 策略)。 | 2. 恢复速度较慢(需重放所有命令)。 |
3. 日志文件可读(便于排查问题)。 | 3. 重写时可能消耗大量 CPU 和 I/O。 |
配置参数(redis.conf)
appendonly yes # 启用 AOF(默认关闭)
appendfilename "appendonly.aof" # AOF 文件名
appendfsync always # 刷盘策略:
# always:每个写命令都刷盘(最慢,数据安全性最高)
# everysec:每秒刷盘(推荐,兼顾性能与安全)
# no:由操作系统决定(最快,风险高)
no-appendfsync-on-rewrite yes # 重写时是否停止刷盘(避免阻塞)
auto-aof-rewrite-percentage 100 # 触发重写的文件增长百分比
auto-aof-rewrite-min-size 64mb # 触发重写的最小文件大小
aof-load-truncated yes # 加载时忽略损坏的日志(避免启动失败)
aof-use-rdb-preamble yes # 混合持久化时,在 AOF 开头添加 RDB 内容
数据恢复流程
- 读取 AOF 文件,解析并执行所有写命令,重建内存数据。
- 若 AOF 文件损坏,可通过
redis-check-aof --fix
工具修复。
3、混合持久化(Hybrid Persistence)
核心原理
- Redis 4.0+ 新增:在 AOF 重写时,先将当前内存数据以 RDB 格式写入 AOF 文件开头,再追加增量的写命令。
- 优势:
- 结合 RDB 的快速恢复能力(加载时先读 RDB 部分)和 AOF 的增量日志(仅重放最近命令)。
- 减少 AOF 文件体积,提升恢复效率。
配置方式
aof-use-rdb-preamble yes # 开启混合持久化(默认开启)
4、两种持久化机制对比
维度 | RDB | AOF |
---|---|---|
数据安全 | 低(可能丢失最后一次快照后的数据) | 高(取决于 fsync 策略) |
文件体积 | 小(压缩后) | 大(原始命令日志) |
恢复速度 | 快(全量加载) | 慢(重放所有命令) |
性能影响 | fork 时可能阻塞主线程 | 每秒刷盘几乎无阻塞,always 会降低写入性能 |
适用场景 | 备份、主从同步、允许少量数据丢失 | 金融交易、实时数据要求高的场景 |
四、Redis的过期键的删除策略
Redis 的过期键删除策略采用 被动删除(惰性删除) 和 主动删除(定期删除) 结合的方式,并在内存不足时触发 内存淘汰机制。以下是具体策略的详细说明:
1、惰性删除
策略 | 触发条件 | 执行逻辑 | 优点 | 缺点 |
---|---|---|---|---|
惰性删除 | 客户端访问键时 | 检查键是否过期: - 过期则删除并返回 nil - 未过期则正常处理请求 | 不主动消耗 CPU 资源 | 可能导致过期键长期占用内存 |
适用场景 | 读多写少、过期键易被访问 | — | — | — |
2、定期删除
策略 | 触发条件 | 执行逻辑 | 优点 | 缺点 |
---|---|---|---|---|
定期删除 | 周期性任务(默认每 100ms) | 1. 从每个数据库随机抽样少量键(默认 20 个) 2. 删除其中过期的键 3. 若过期键比例 > 25%,重复抽样(最多循环 ACTIVE_EXPIRE_CYCLE_LOOP 次,单次耗时 ≤ 25ms) | 主动清理过期键,避免内存泄漏 | 无法保证即时删除所有过期键 |
配置参数 | hz (默认 10,即每秒 10 次) | — | 增大 hz 提升清理频率 | 增大 hz 会增加 CPU 消耗 |
3、内存淘汰机制
当内存使用达到 maxmemory
限制时,强制删除键(包括未过期键),优先淘汰过期键或符合策略的键:
策略 | 作用范围 | 淘汰逻辑 |
---|---|---|
volatile-ttl | 仅设置过期时间的键 | 优先删除剩余存活时间(TTL)最短的键 |
volatile-random | 仅设置过期时间的键 | 随机删除设置过期时间的键 |
volatile-lru | 仅设置过期时间的键 | 淘汰最近最少使用(LRU)的键 |
volatile-lfu | 仅设置过期时间的键 | 淘汰最不常用(LFU)的键 |
allkeys-random | 所有键(无论是否过期) | 随机删除任意键 |
allkeys-lru | 所有键(无论是否过期) | 淘汰最近最少使用(LRU)的键 |
allkeys-lfu | 所有键(无论是否过期) | 淘汰最不常用(LFU)的键 |
noeviction (默认) | 所有键 | 禁止删除任何键,写入操作报错 |
五、redis事务
Redis 的事务(Transaction)提供了一种将多个命令组合执行的机制,确保多个操作按顺序执行,但不支持回滚(Rollback),主要用于简化批量操作和处理并发场景。以下是 Redis 事务的核心概念、命令、流程及特性的详细说明:
1、事务相关命令
命令 | 作用 |
---|---|
MULTI | 开启事务,标记后续命令进入队列(排队阶段),返回 OK 。 |
EXEC | 执行事务队列中的所有命令,返回各命令的结果列表;若中途监控键被修改,返回 nil 。 |
DISCARD | 放弃事务,清空队列并退出事务,返回 OK 。 |
WATCH key [key ...] | 监控一个或多个键,若事务执行前(EXEC 前)键被修改,则事务自动失败(EXEC 返回 nil )。 |
UNWATCH | 取消所有监控,用于主动放弃监控机制。 |
2、事务执行流程
2.1.开启事务
127.0.0.1:6379> MULTI
OK # 进入事务模式,后续命令入队
2.2. 命令入队(Queue Commands)
- 调用任意命令(如
SET
、GET
等),命令不会立即执行,而是加入事务队列,返回QUEUED
。 - 语法错误检查:命令入队时会检查语法,若有错误(如命令不存在),
EXEC
时会拒绝执行整个事务。127.0.0.1:6379> SET key1 "value1" # 入队成功,返回 QUEUED QUEUED 127.0.0.1:6379> INVALID_COMMAND # 语法错误,入队失败,标记事务为 discard (error) ERR unknown command `INVALID_COMMAND`
2.3. 执行事务(EXEC)或放弃事务(DISCARD)
- 正常执行:若队列中所有命令语法正确且监控键未被修改,
EXEC
按顺序执行所有命令,返回结果列表。127.0.0.1:6379> EXEC # 执行事务 1) OK # SET key1 "value1" 的结果 2) (error) ERR unknown command `INVALID_COMMAND` # 语法错误的命令导致执行失败
- 放弃事务:调用
DISCARD
会清空队列,退出事务模式。127.0.0.1:6379> DISCARD OK
3、事务的特性(与传统数据库的对比)
特性 | Redis 事务 | 传统数据库事务 |
---|---|---|
原子性(Atomicity) | 队列中的命令要么全部执行,要么全部不执行(仅在入队阶段有语法错误时); 执行阶段单命令失败不影响其他命令。 | 整个事务要么全部成功,要么回滚。 |
一致性(Consistency) | 保证事务执行前后数据状态合法(依赖应用层逻辑)。 | 事务执行前后数据满足完整性约束。 |
隔离性(Isolation) | 命令按入队顺序串行执行,无并发问题(单线程模型)。 | 支持多种隔离级别(如读未提交、可重复读)。 |
持久性(Durability) | 取决于持久化策略(RDB/AOF),与事务无关。 | 由数据库持久化机制保证(如 WAL 日志)。 |
4、乐观锁机制:WATCH 命令
作用
- 监控键的变化,确保事务执行前键未被其他客户端修改,用于解决并发场景下的竞争条件(Race Condition)。
流程示例
- 监控键
balance
:127.0.0.1:6379> WATCH balance # 监控账户余额 OK
- 获取当前余额并开启事务:
127.0.0.1:6379> GET balance "100" 127.0.0.1:6379> MULTI OK
- 模拟扣款操作(入队):
127.0.0.1:6379> DECR balance # 扣款 1 元,入队 QUEUED
- 其他客户端修改
balance
(模拟并发修改):127.0.0.1:6379> SET balance "90" # 其他操作修改了余额 OK
- 执行事务(失败):
127.0.0.1:6379> EXEC # 发现 balance 被修改,事务失败,返回 nil (nil)
- 处理失败(重试或放弃):
127.0.0.1:6379> UNWATCH # 取消监控,重新开始流程 OK
5、事务的局限性
- 不支持回滚:
- 若命令在执行阶段失败(如类型错误),Redis 会跳过该命令,继续执行后续命令,不会回滚已执行的命令。
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET key "value" # 入队成功 QUEUED 127.0.0.1:6379> LPUSH key "element" # 类型错误(key 是字符串,非列表) QUEUED 127.0.0.1:6379> EXEC 1) OK # SET 成功 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value # LPUSH 失败
- 若命令在执行阶段失败(如类型错误),Redis 会跳过该命令,继续执行后续命令,不会回滚已执行的命令。
- 无法解决所有并发问题:
WATCH
仅能监控键是否被修改,无法处理复杂的业务逻辑冲突(如金额计算错误),需应用层处理。
- 性能限制:
- 事务本质是批量执行命令,若包含大量操作,可能阻塞 Redis 主线程,需合理控制事务规模。
5、适用场景
- 批量操作:简化多个命令的顺序执行(如先查询再更新)。
- 乐观锁场景:通过
WATCH
防止并发修改(如库存扣减、账户余额调整)。 - 简单事务逻辑:无需回滚的场景(如缓存更新、计数器批量操作)。
六、集群方案
Redis 提供了多种集群方式以满足不同的业务需求,主要包括 主从复制(Master-Slave Replication)、哨兵模式(Sentinel)和 集群模式(Cluster)。以下是各模式的详细对比和说明:
1、主从复制(Master-Slave Replication)
1. 架构特点
- 单主多从:一个主节点(Master)负责写入,多个从节点(Slave)复制主节点数据,从节点支持读操作。
- 数据同步:主节点通过
RDB
快照和增量日志(AOF
重写)向从节点同步数据。 - 读写分离:读请求由从节点处理,写请求集中在主节点,提升读性能。
2. 核心功能
- 数据备份:从节点作为主节点的副本,用于容灾备份。
- 扩展读能力:通过增加从节点数量分担读压力。
- 手动故障转移:主节点故障时需手动将从节点提升为新主节点。
3. 优缺点
优点 | 缺点 |
---|---|
- 配置简单,易于实现 | - 主节点单点故障,需手动切换 |
- 支持读写分离 | - 无法自动恢复,存在服务中断风险 |
- 适合读多写少场景 | - 从节点数据可能存在延迟(异步复制) |
4. 适用场景
- 简单数据备份和读写分离场景(如缓存层读扩展)。
- 对高可用性要求不高的业务(如非核心系统)。
2、哨兵模式(Sentinel)
1. 架构特点
- 主从 + 哨兵节点:在主从复制基础上,引入独立的哨兵节点(Sentinel)组成集群。
- 自动监控:哨兵节点定期监控主从节点状态,检测主节点故障。
- 自动故障转移:主节点故障时,哨兵自动选举从节点为新主节点,并重新配置其他从节点。
2. 核心流程
- 监控:哨兵节点通过
PING
命令检查主从节点存活状态。 - 选举:当主节点被多数哨兵认定为下线(
ODOWN
),触发选举机制,选择最优从节点(如复制偏移量最大、优先级最高)。 - 故障转移:将选中的从节点提升为新主节点,其他从节点指向新主节点,通知客户端更新连接。
3. 优缺点
优点 | 缺点 |
---|---|
- 自动故障转移,高可用 | - 不支持数据分片,单机存储限制 |
- 多哨兵节点避免单点故障 | - 主节点写压力大,无法横向扩展数据量 |
- 兼容主从复制的读写分离 | - 故障转移期间存在短暂服务不可用 |
4. 适用场景
- 对高可用性要求较高的场景(如生产环境缓存)。
- 数据量较小、写请求不密集的业务(如中小型应用)。
3、集群模式(Cluster)
1. 架构特点(Redis Cluster)
- 分布式架构:节点分为 主节点(Master) 和 从节点(Slave),主节点负责数据读写,从节点作为备份。
- 数据分片(Sharding):通过 哈希槽(Hash Slot) 机制将数据分散到不同主节点。Redis 集群共有
16384
个哈希槽,每个主节点负责部分槽位。 - 节点间通信:节点通过
Gossip 协议
交换集群状态,自动发现和维护节点关系。
2. 核心机制
- 哈希槽分配:
数据键通过CRC16(key) % 16384
计算槽位,映射到对应主节点。例如:槽位 0-5460 → 节点 A 槽位 5461-10922 → 节点 B 槽位 10923-16383 → 节点 C
- 自动故障转移:与哨兵模式类似,主节点故障时,从节点自动提升为新主节点,并重新分配槽位。
- 横向扩展:可通过添加节点组(主从对)动态调整槽位分布,无需停机。
3. 优缺点
优点 | 缺点 |
---|---|
- 支持数据分片,突破单机存储限制 | - 配置和管理复杂(需手动分配槽位) |
- 高可用性(主从 + 自动故障转移) | - 客户端需支持集群模式(如 Redis-Py Cluster) |
- 读写性能可线性扩展 | - 跨节点操作需通过路由,存在延迟 |
- 适合大数据量和高并发场景 | - 不支持多键事务(仅支持单槽操作) |
4. 适用场景
- 数据量较大(GB 级以上)或访问流量高的场景(如大型电商、社交平台)。
- 需要水平扩展读写能力的业务(如实时统计、分布式缓存)。
4、三种模式对比表格
维度 | 主从复制 | 哨兵模式 | 集群模式 |
---|---|---|---|
高可用性 | 手动切换,低 | 自动故障转移,高 | 自动故障转移,高 |
数据分片 | 不支持 | 不支持 | 支持(16384 哈希槽) |
读写扩展 | 读可扩展(从节点) | 读可扩展(从节点) | 读写均可扩展(节点) |
单机瓶颈 | 主节点存在 | 主节点存在 | 无(数据分散存储) |
部署复杂度 | 简单 | 中等(需配置哨兵) | 复杂(槽位管理) |
典型场景 | 备份、读写分离 | 高可用中小规模数据 | 大规模数据 / 高并发 |
Redis 版本 | 2.0+ | 2.8+ | 3.0+ |
5、选择建议
- 小规模场景:
- 若仅需备份和读写分离,选 主从复制。
- 若需自动故障转移,选 哨兵模式。
- 大规模场景:
- 数据量超过单机限制或需高并发,选 集群模式。
- 特殊需求:
- 需多数据中心容灾,可结合 主从复制 + 异步复制。
- 需强一致性,谨慎使用(Redis 基于异步复制,存在短暂数据丢失可能)。
七、redis缓存三兄弟
在 Redis 缓存体系中,“缓存三兄弟” 通常指的是缓存穿透、缓存击穿和缓存雪崩,这三个是缓存场景中常见的典型问题,会严重影响系统性能甚至导致服务崩溃。以下是对它们的详细解析:
1、缓存穿透
问题描述
- 场景:客户端请求不存在于缓存和数据库中的数据,导致请求直接穿透缓存层,每次都访问数据库。
- 危害:大量无效请求会压垮数据库,甚至引发DDoS 攻击。
成因
- 恶意攻击:攻击者故意请求不存在的键(如随机字符串)。
- 业务逻辑漏洞:如用户输入非法参数,查询不存在的数据。
解决方案
-
布隆过滤器(Bloom Filter)
- 原理:在请求进入数据库前,用布隆过滤器快速判断数据是否存在。若不存在,直接拒绝请求,避免穿透到数据库。
- 适用场景:数据基数大、更新频率低的场景(布隆过滤器更新成本较高)。
-
空值缓存
- 原理:当查询结果为 null 时,仍将空值存入缓存(设置较短过期时间,如 5 分钟),避免相同请求反复查询数据库。
- 注意:需设置合理过期时间,防止缓存大量无效数据。
-
参数校验
- 对请求参数进行合法性校验(如 ID 格式、范围限制),提前拦截无效请求。
2、缓存击穿
问题描述
- 场景:热点数据的缓存过期瞬间,大量请求同时访问数据库,导致数据库压力激增。
- 关键词:单个热点键、缓存过期、并发请求。
成因
- 热点数据(如秒杀商品、热门新闻)的缓存设置了固定过期时间,到期时大量用户请求同时到达。
解决方案
-
热点数据永不过期
- 不设置过期时间,通过其他机制(如异步线程)主动更新缓存(需注意数据一致性)。
-
加互斥锁(Mutex)
- 原理:当缓存失效时,仅允许单个请求访问数据库并重建缓存,其他请求等待锁释放后从缓存获取数据。
- 实现方式:
- 使用 Redis 的
SET NX
命令实现分布式锁(如set lock_key unique_value nx px 10000
)。 - 注意设置锁的过期时间,避免死锁。
- 使用 Redis 的
-
提前更新缓存
- 在缓存过期前(如剩余 10% 时间),异步提前刷新缓存,避免到期时被动重建。
3、缓存雪崩
问题描述
- 场景:大量缓存键同时过期或Redis 服务宕机,导致大量请求直接涌入数据库,造成数据库负载过高甚至崩溃。
- 关键词:大量键过期、缓存不可用、请求激增。
成因
- 批量缓存设置了相同或相近的过期时间,到期时集体失效。
- Redis 集群故障(如主从切换、节点宕机),导致缓存服务整体不可用。
解决方案
-
分散过期时间
- 在设置缓存过期时间时,添加随机时间偏移(如
TTL = 3600 + random(600)
),避免大量键同时过期。
- 在设置缓存过期时间时,添加随机时间偏移(如
-
提升缓存高可用性
- 采用 Redis 集群(Cluster)或主从复制(Master-Slave)架构,结合 Sentinel 实现自动故障转移,减少单点故障影响。
-
熔断降级
- 通过熔断器(如 Hystrix、Resilience4j)监控数据库负载,当压力超过阈值时,直接返回预设的默认值或提示信息,保护数据库。
-
限流
- 通过限流组件(如 Guava RateLimiter、Redis 限流)限制单位时间内的请求量,避免突发流量压垮系统。
-
二级缓存
- 使用本地缓存(如 Caffeine、Ehcache)作为一级缓存,Redis 作为二级缓存,降低对 Redis 的依赖。
4. 三兄弟对比总结
问题 | 核心原因 | 典型场景 | 解决方案核心思路 |
---|---|---|---|
缓存穿透 | 请求不存在的数据 | 恶意攻击、非法参数查询 | 过滤无效请求、缓存空值 |
缓存击穿 | 单个热点键过期,并发请求 | 热点商品秒杀、突发流量 | 避免并发重建、延长热点键存活 |
缓存雪崩 | 大量键过期或缓存服务不可用 | 批量缓存到期、Redis 宕机 | 分散过期时间、增强系统容错 |