Redis核心知识点(突出重点+深度细化)
一、高性能底层逻辑(Redis核心竞争力)
1. 纯内存存储(性能基石)
- 核心优势:内存读写速度(约100ns级)远高于磁盘(ms级),Redis将所有数据驻留内存,彻底规避磁盘I/O瓶颈。
- 细节 :内存数据并非无限制存储,需配合内存淘汰策略 (如LRU)和持久化机制(RDB/AOF)平衡性能与数据安全。
- 场景:高频访问数据(如商品详情、用户会话)全内存存储,支撑每秒10万+TPS。
2. 单线程模型(并发处理精髓)
- 核心设计 :主线程串行处理所有命令(网络I/O+数据操作),避免多线程的锁竞争 和线程切换开销(上下文切换成本约1-10μs)。
- 关键细节 :
- 仅核心命令执行单线程,Redis 6.0+引入多线程I/O (默认4线程)处理网络读写,进一步提升并发连接能力(通过
io-threads
配置)。 - 单线程不卡顿的前提:命令执行时间极短(微秒级),若存在耗时命令(如
KEYS *
)会阻塞全库,需禁用或用SCAN
替代。
- 仅核心命令执行单线程,Redis 6.0+引入多线程I/O (默认4线程)处理网络读写,进一步提升并发连接能力(通过
- 场景:高并发读场景(如秒杀库存查询),单线程无锁机制可高效处理。
3. IO多路复用(高并发连接支撑)
-
核心机制 :通过epoll模型 (Linux默认)实现单线程同时监听数万客户端连接,事件驱动式处理I/O请求。
-
细节对比 :
模型 缺陷 epoll优势 select 最多监听1024个连接,轮询效率低 无连接数限制,事件驱动(O(1)复杂度) poll 无连接数限制,但轮询效率低 仅处理触发事件的连接,无需遍历全量 -
流程 :
epoll_create
创建实例 →epoll_ctl
注册连接事件 →epoll_wait
阻塞等待事件 → 处理触发的读写请求。
4. 优化的数据结构(操作效率保障)
Redis每种数据结构均为"场景化设计",底层优化直击性能痛点:
数据结构 | 底层实现 | 核心优化点 | 典型命令效率 |
---|---|---|---|
字符串(String) | 简单动态字符串(SDS) | 预分配空间(减少内存重分配)、惰性释放(避免频繁回收)、O(1)获取长度 | SET /GET :O(1) |
哈希(Hash) | 压缩列表→哈希表 | 小数据用压缩列表(紧凑存储),大数据自动转哈希表(O(1)查找) | HGET :O(1) |
有序集合(ZSet) | 压缩列表→跳跃表 | 跳跃表通过多层指针实现O(logN)查找,支持范围查询(如排行榜Top10) | ZADD /ZRANGE :O(logN) |
列表(List) | 双端链表→压缩列表 | 两端插入/删除O(1),支持阻塞操作(如消息队列BLPOP ) |
LPUSH /RPOP :O(1) |
集合(Set) | 整数集合→哈希表 | 整数元素用整数集合(紧凑存储),其他用哈希表(去重+O(1)查找) | SADD /SISMEMBER :O(1) |
- 示例 :SDS预分配机制
执行APPEND key "xyz"
时,若当前剩余空间足够,直接拼接(O(1));否则按"当前长度*2"扩容,减少后续分配次数。
二、缓存三大核心问题(生产必避坑)
1. 缓存穿透(查不到的数据攻击)
- 定义:请求查询"缓存+数据库均不存在的数据",导致每次请求穿透到数据库,可能压垮DB。
- 原因:恶意攻击(伪造不存在的ID)、业务逻辑漏洞(如误查无效参数)。
- 解决方案(细化实现) :
-
缓存空值 :数据库查不到时,缓存
key→null
(过期时间设短,如5分钟),避免重复穿透。java// 伪代码 Object data = db.query(id); if (data == null) { redis.set(id, null, 300, TimeUnit.SECONDS); // 缓存空值,5分钟过期 } else { redis.set(id, data, 3600, TimeUnit.SECONDS); }
-
布隆过滤器 :提前将所有有效ID存入布隆过滤器,请求先过过滤器,不存在则直接返回。
- 优点:空间效率极高(100万ID约占12KB),查询O(1)。
- 注意:存在误判率(可通过调整位数和哈希函数降低,如0.1%误判),需配合缓存空值兜底。
-
2. 缓存击穿(热点key失效瞬间雪崩)
- 定义:某个热点key(如热门商品ID)过期瞬间,大量并发请求直接冲击数据库。
- 原因:热点数据缓存过期,且并发量极高(如秒杀商品库存查询)。
- 解决方案(细化实现) :
-
互斥锁 :缓存失效时,仅允许一个线程查DB并重建缓存,其他线程等待重试。
java// 伪代码:用SETNX实现互斥锁 String lockKey = "lock:" + id; Boolean locked = redis.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); // 锁10秒过期 if (locked) { try { Object data = db.query(id); // 查DB redis.set(id, data, 3600, TimeUnit.SECONDS); // 重建缓存 } finally { redis.delete(lockKey); // 释放锁 } } else { Thread.sleep(100); // 等待100ms后重试 return queryCache(id); // 重新查缓存 }
-
热点数据永不过期 :缓存不设过期时间,通过异步更新(如消息队列)同步DB变更,避免过期瞬间冲击。
-
3. 缓存雪崩(大量key同时失效)
- 定义:大量缓存key在同一时间过期,导致请求批量穿透到数据库,引发DB崩溃。
- 原因:缓存过期时间设置过于集中(如全量设置1小时过期)。
- 解决方案(细化实现) :
-
随机过期时间 :给每个key的过期时间加随机偏移(如1小时±10分钟),分散过期时间。
javaint baseExpire = 3600; // 基础3600秒 int random = new Random().nextInt(600); // 0-600秒随机 redis.set(key, value, baseExpire + random, TimeUnit.SECONDS);
-
二级缓存:本地缓存(如Caffeine)+ Redis,Redis失效时先查本地缓存,兜底防穿透。
-
三、持久化机制(数据不丢失的保障)
Redis是内存数据库,需通过持久化将数据落地磁盘,避免宕机丢失。核心有两种机制:
1. RDB(快照持久化)
- 原理 :按配置时机(如
save 60 10000
:60秒内1万次写操作)触发,通过bgsave
fork子进程生成全量数据快照(.rdb文件)。 - 优点:文件小、恢复快(适合备份),fork子进程不阻塞主线程。
- 缺点:快照间隔内数据可能丢失(如宕机),fork子进程时会短暂阻塞(内存越大阻塞越久)。
- 适用场景:冷备(每天凌晨生成RDB备份)、数据容忍分钟级丢失的场景。
2. AOF(日志持久化)
- 原理 :记录所有写命令(如
SET key value
)到.aof文件,重启时重放命令恢复数据。 - 关键配置 :
appendfsync always
:每条命令刷盘(最安全,性能最差);appendfsync everysec
:每秒刷盘(平衡安全与性能,默认);appendfsync no
:由OS决定刷盘(最快,数据丢失风险高)。
- 优化 :AOF重写(
bgrewriteaof
)→ 合并冗余命令(如INCR key
100次→SET key 100
),减少文件体积。 - 优点:数据丢失少(最多丢1秒),适合对数据安全性要求高的场景(如金融交易)。
- 缺点:文件大、恢复慢。
3. 混合持久化(Redis 4.0+默认)
- 原理:RDB+AOF结合,AOF文件头部存RDB快照,尾部存增量命令,兼顾恢复速度(RDB)和数据安全性(AOF)。
四、主从复制与高可用(集群核心)
1. 主从复制(读写分离+容灾)
- 核心作用:主库(Master)写数据,从库(Slave)读数据,分担主库压力;主库宕机时从库可切换为主库。
- 同步流程 :
- 全量同步:从库首次连接主库,主库生成RDB并发送,从库加载RDB初始化数据;
- 增量同步 :主库后续写命令通过复制缓冲区实时发送给从库,从库执行命令保持一致。
- 关键细节 :
- 主库用复制偏移量 (offset)记录同步进度,从库通过
PSYNC
命令请求增量数据; - 网络中断后,从库重连时通过偏移量+复制缓冲区(默认1MB)实现部分同步,避免全量重传。
- 主库用复制偏移量 (offset)记录同步进度,从库通过
2. 哨兵(Sentinel):自动故障转移
- 功能:监控主从库状态,主库宕机时自动从从库中选新主库,并更新其他从库的主库地址。
- 核心流程 :
- 监控:每秒向所有节点发送
PING
,判断节点是否存活; - 选主:通过"主观下线+客观下线"判断主库故障,再按"从库优先级→复制偏移量→runid"选新主库;
- 通知:更新客户端连接的主库地址。
- 监控:每秒向所有节点发送
3. 集群(Cluster):水平扩容
- 核心设计 :将16384个哈希槽(slot)分配给多个节点,每个key通过
CRC16(key) % 16384
映射到对应槽,实现数据分片。 - 扩容缩容 :通过
cluster add-node
/cluster del-node
操作,迁移槽和数据(自动平衡),不中断服务。 - 高可用:每个主节点至少1个从节点,主节点宕机时从节点自动切换为主节点。
五、事务与原子操作(数据一致性)
1. 事务
- 命令 :
MULTI
(开启事务)→ 批量命令 →EXEC
(执行)/DISCARD
(放弃)。 - 特性:批量命令串行执行(不被其他命令打断),但不支持回滚(错误命令仅当前命令失败,其他继续执行)。
- 乐观锁 :
WATCH key
监控key,若事务执行前key被修改,事务取消(防止并发修改)。
2. 原子命令与Lua脚本
-
原子命令 :如
INCR
(自增)、HSETNX
(哈希表不存在时设置),单命令天然原子性,适合简单场景。 -
Lua脚本 :复杂逻辑(如"查+改+写")用Lua脚本包裹,通过
EVAL
执行,保证原子性(脚本内命令不可中断)。
示例:库存扣减(防止超卖)lua-- 若库存>0则扣减1,返回1;否则返回0 local stock = redis.call('get', KEYS[1]) if stock and tonumber(stock) > 0 then return redis.call('decr', KEYS[1]) end return 0
总结
Redis的核心竞争力在于**"高性能+高可用+灵活的数据模型"**:
- 底层通过"内存存储+单线程+epoll"支撑超高并发;
- 缓存问题需针对性解决(穿透用布隆过滤器/空值,击穿用锁/永不过期,雪崩用随机过期);
- 持久化(RDB/AOF)和集群(主从/哨兵/Cluster)保障数据安全和水平扩展;
- 事务与Lua脚本确保复杂操作的原子性。
掌握这些知识点,可应对90%以上的Redis生产场景。