Redis 基础特性
- 定义:Redis(Remote Dictionary Server)是一款开源的高性能键值对数据库,基于内存存储,支持持久化,采用单线程模型(核心网络 IO 模块)。
- 核心优势:
-
速度快:内存操作 + 单线程避免上下文切换,QPS 可达 10 万 +;
-
支持多数据结构:不止字符串,还包含哈希、列表、集合等;
-
功能丰富:支持持久化、过期淘汰、事务、发布订阅、Lua 脚本等;
-
高可用:提供主从复制、哨兵、集群方案;
-
跨平台:兼容 Linux、Windows 等,且支持多种客户端语言。
-
Redis 核心数据结构
五大基础数据结构及应用场景
数据结构 |
底层实现(关键版本) |
核心命令 |
典型应用场景 |
String |
简单动态字符串(SDS) |
SET、GET、INCR、APPEND |
计数器(文章阅读量)、缓存用户信息 |
Hash |
哈希表(拉链法解决冲突) |
HSET、HGET、HMGET、HDEL |
存储对象(用户资料:id→{name,age}) |
List |
双向链表(3.2 前)/ 压缩列表(3.2 后) |
LPUSH、LPOP、LRANGE |
消息队列、最新消息排行(如朋友圈) |
Set |
哈希表 / 整数集合 |
SADD、SISMEMBER、SUNION |
标签(用户兴趣标签)、共同好友计算 |
ZSet |
跳表 + 哈希表 |
ZADD、ZRANGE、ZSCORE |
排行榜(游戏积分排行、商品销量榜) |
高级数据结构
-
HyperLogLog:基于概率统计的基数计数结构,占用内存极小(约 12KB),适用于 “统计 UV”(如网站独立访客),误差率约 0.81%。
-
GeoHash:将经纬度编码为字符串,支持附近地点查询(如 “外卖 App 附近商家”),核心命令GEOADD(添加坐标)、GEORADIUS(搜索半径内地点)。
-
BitMap:按位存储数据,1 字节可存 8 个状态,适用于 “签到统计”(如用户月度签到)、“活跃用户标记”,核心命令SETBIT(设定位)、BITCOUNT(统计 1 的个数)。
Redis 持久化机制
持久化的核心目的是防止内存数据丢失,Redis 提供两种持久化方案,可单独使用或结合使用。
RDB(Redis Database)
-
原理:在指定时间间隔内,将内存中当前数据集快照写入磁盘(生成.rdb文件),恢复时直接加载该文件到内存。
- 触发方式:
-
手动触发:SAVE(阻塞 Redis 进程,不推荐)、BGSAVE(fork 子进程执行快照,主进程不阻塞);
-
自动触发:配置文件中save <秒数> <修改次数>(如save 60 1000表示 60 秒内修改 1000 次则触发 BGSAVE)。
-
- 优缺点:
-
优点:文件体积小、恢复速度快,适合全量备份(如每日凌晨备份);
-
缺点:快照间隔内数据易丢失(如 1 分钟内宕机,该分钟数据丢失),fork 子进程时会占用额外内存(Copy-On-Write 机制)。
-
AOF(Append Only File)
- 原理:将 Redis 的每一条写命令(如 SET、HSET)以日志形式追加到.aof文件,恢复时重新执行文件中的命令以重建数据。
- 触发方式:依赖配置文件中的appendfsync参数:
-
always:每执行一条命令就刷盘,数据零丢失,但性能最差;
-
everysec:每秒刷盘一次,最多丢失 1 秒数据,性能与安全性平衡(默认值);
-
no:由操作系统决定刷盘时机,数据丢失风险高。
-
- AOF 重写:
-
问题:AOF 文件会随命令增多而变大,恢复速度变慢;
-
解决:通过BGREWRITEAOF命令(或自动触发),合并相同 key 的命令(如SET a 1→SET a 2合并为SET a 2),生成更小的 AOF 文件。
-
- 优缺点:
-
优点:数据安全性高(可配置每秒刷盘),文件可读性强(文本格式);
-
缺点:文件体积大、恢复速度慢,写命令追加会消耗额外 IO。
-
RDB 与 AOF 的选择
- 追求高性能 + 快速恢复:使用 RDB(如缓存场景,可接受少量数据丢失);
- 追求数据安全性:使用 AOF(如金融场景,需避免数据丢失);
- 生产环境推荐:RDB+AOF 结合(AOF 保证数据不丢失,RDB 用于快速恢复)。
Redis 过期键删除与内存淘汰
过期键删除策略
Redis 同时使用三种策略平衡 “数据准确性” 与 “性能消耗”:
- 惰性删除:只有当访问某个 key 时,才判断是否过期,过期则删除。优点:不占用额外 CPU;缺点:过期 key 未访问时会占用内存(内存泄漏风险)。
- 定期删除:每隔一段时间(默认 100ms),随机抽取部分过期 key 进行删除。优点:避免内存泄漏;缺点:可能有少量过期 key 未及时删除。
- 内存淘汰:当内存达到maxmemory阈值时,触发内存淘汰策略,删除部分 key 释放内存(即使 key 未过期)。
内存淘汰策略
配置参数:maxmemory-policy <策略名>,Redis 4.0 后新增两种 LFU 策略:
策略名 |
含义 |
volatile-lru |
从设置了过期时间的 key 中,删除最近最少使用(LRU)的 key |
allkeys-lru |
从所有 key中,删除最近最少使用的 key(推荐,通用缓存场景) |
volatile-lfu |
从设置了过期时间的 key 中,删除最近最少频率使用(LFU)的 key |
allkeys-lfu |
从所有 key 中,删除最近最少频率使用的 key(适合访问频率差异大的场景) |
volatile-random |
从设置了过期时间的 key 中,随机删除 key |
allkeys-random |
从所有 key 中,随机删除 key |
volatile-ttl |
从设置了过期时间的 key 中,删除剩余生存时间(TTL)最短的 key |
noeviction |
不删除任何 key,内存满时拒绝新写命令(默认策略,不推荐生产使用) |
Redis 高可用方案
主从复制(基础高可用)
- 核心作用:实现数据备份(从节点复制主节点数据)、负载均衡(读请求分发到从节点)。
- 复制流程:
-
从节点执行SLAVEOF <主节点IP> <端口>,发送 SYNC 命令;
-
主节点执行 BGSAVE 生成 RDB 文件,同时记录后续写命令到复制缓冲区;
-
主节点发送 RDB 文件给从节点,从节点加载 RDB 恢复数据;
-
主节点发送复制缓冲区的写命令,从节点执行命令,最终与主节点数据一致。
-
- 注意点:默认是 “只读从节点”(slave-read-only yes),避免从节点写入数据导致数据不一致;支持 “链式复制”(从节点可作为其他从节点的主节点),减轻主节点压力。
哨兵(Sentinel)
- 核心作用:解决主从复制的 “主节点故障自动切换” 问题,实现高可用。
- 核心功能:
-
监控:实时检查主节点、从节点是否存活;
-
自动故障转移:主节点宕机后,从哨兵中选举新主节点,并重配置其他从节点指向新主;
-
通知:通过 API 向客户端或管理员发送故障通知。
-
- 部署建议:哨兵节点至少 3 个(奇数,避免脑裂),且与主从节点分开部署(避免单点故障)。
Redis Cluster(分布式集群)
- 核心作用:解决 “单机内存上限” 问题,实现数据分片存储(横向扩展),同时支持高可用。
- 核心特性:
-
数据分片:将所有 key 通过 CRC16 算法计算哈希值,映射到 16384 个哈希槽(slot),每个节点负责部分槽;
-
高可用:每个主节点至少有一个从节点,主节点宕机后,从节点自动升为主节点;
-
去中心化:无中心节点,客户端可连接任意节点发送请求,节点间通过 Gossip 协议通信。
-
- 关键命令:CLUSTER MEET(添加节点到集群)、CLUSTER ADDSLOTS(为节点分配槽)、CLUSTER REPLICATE(设置从节点)。
Redis 常见问题与优化
缓存穿透
- 问题:请求查询 “不存在的 key”(如恶意查询 id=-1 的用户),Redis 无法命中,请求直接穿透到数据库,导致数据库压力过大。
- 解决方案:
-
缓存空值:对不存在的 key,缓存一个空值(如SET -1 "" EX 300),并设置较短过期时间;
-
布隆过滤器:在 Redis 前部署布隆过滤器,不存在的 key 直接被过滤(如用 Redis 的 HyperLogLog 或单独布隆过滤器组件)。
-
缓存击穿
- 问题:某个 “热点 key” 过期瞬间,大量请求同时穿透到数据库(如秒杀商品的库存 key 过期)。
- 解决方案:
-
互斥锁:当 key 过期时,只有一个线程能获取锁去数据库查询,其他线程等待重试(如用 Redis 的SETNX命令实现锁);
-
热点 key 永不过期:对热点 key 不设置过期时间,通过后台线程定期更新数据(需注意内存占用)。
-
缓存雪崩
- 问题:大量 key 在同一时间过期,或 Redis 集群宕机,导致所有请求穿透到数据库,引发数据库雪崩。
- 解决方案:
-
过期时间随机化:为 key 设置过期时间时,添加随机值(如EX 3600 + rand(0, 600)),避免集中过期;
-
服务熔断 / 降级:当数据库压力过大时,拒绝部分非核心请求,或返回默认值(如用 Sentinel 或 Hystrix 实现);
-
Redis 集群高可用:部署哨兵或 Redis Cluster,避免单点故障导致整个缓存不可用。
-
Redis 单线程模型为什么快?
- 核心原因:Redis 的单线程仅负责 “网络 IO” 和 “命令执行”,避免了多线程的上下文切换和锁竞争;
- 辅助原因:
-
内存操作:数据都在内存中,读写速度远快于磁盘;
-
IO 多路复用:使用 epoll(Linux)、kqueue(BSD)等 IO 多路复用模型,可同时处理大量客户端连接,且无阻塞;
-
高效数据结构:如跳表、压缩列表等,减少数据操作的时间复杂度。
-
Redis 与 Memcached 的区别
特性维度 | Redis | Memcached |
---|---|---|
数据类型 | 丰富:String, Hash, List, Set, Sorted Set, Stream, Geo 等 | 单一:只支持 String 类型的 Key-Value |
持久化 | 支持:提供 RDB(快照)和 AOF(日志)两种方式,数据可恢复 | 不支持:纯内存缓存,重启数据即丢失 |
分布式 | 支持 (Redis Cluster, 主从复制, 哨兵模式) | 支持 (客户端分片,或通过代理如 Twemproxy) |
性能 | 极高 (单机10万+ QPS,依赖网络IO) (但复杂命令如 ZRANGE 会慢一些) | 极高 (单机10万+ QPS,依赖网络IO) (模型简单,性能略高于 Redis 简单命令) |
内存管理 | 多种策略:LRU, LFU, TTL, 最大内存限制等 | 类似:LRU, TTL, 最大内存限制 |
事务 | 支持 (MULTI/EXEC) | 不支持 (但提供 CAS 命令保证原子性) |
应用场景 | 全能型:缓存、会话共享、消息队列、排行榜、分布式锁、地理位置等 | 单一型:简单的键值缓存,用于减轻数据库负载 |
语言支持 | 极广:官方支持 C/C++,社区有几乎所有语言的客户端 (Java, Python, Go, ...) | 广泛:多种语言客户端 |
附加功能 | 发布订阅、Lua 脚本、模块化扩展 | 多线程、高并发 |
典型部署 | 独立服务器/集群部署 | 独立服务器/集群部署 |
优点 | 功能强大、数据类型丰富、持久化、高可用、社区活跃 | 超简单、超快(对于简单KV)、多线程性能高、内存利用率稍好 |
缺点 | 消耗更多内存(存储元数据)、单线程模型(6.0+多线程IO)复杂命令可能阻塞 | 功能单一、无法持久化、需要客户端实现分布式 |