楼层: 首页/ 软件技术/ 中间件全景/ 缓存中间件:Redis 与 Memcached
05

缓存中间件:Redis 与 Memcached

Cache Middleware · Redis / Memcached

Redis 就是内存里的百货商店——内存键值数据库,C 语言写的,单线程事件驱动,性能极高,缓存/会话/排行榜/分布式锁全能选手。读一次数据库要 10ms,读一次 Redis 只要 0.1ms,差 100 倍。热点数据放 Redis,数据库就不用被反复敲。Memcached 是纯内存 KV,多线程,简单轻量,但功能远不如 Redis 丰富。

4.1 Redis 是什么

论Redis 的脾气

内存键值数据库,C 语言写的,单线程事件驱动,性能极高。Redis 把数据全放内存,读写都是内存操作,每秒十万次读写轻轻松松。它不只是一个缓存——它支持 9 种数据结构(String/List/Hash/Set/ZSet/Bitmap/HyperLogLog/Geo/Stream),能做排行榜、分布式锁、消息队列、限流,简直是"瑞士军刀"。

Redis 8.x 新特性:向量搜索(RediSearch 整合)、JSON 模块增强、时序数据支持。但核心 set/get 命令没变,老配置文件基本兼容。

4.2 安装:Docker 最省事

docker run -d --name redis \ -p 6379:6379 \ redis:8.0 --requirepass yourpassword # 用 redis-cli 连进去 docker exec -it redis redis-cli -a yourpassword
127.0.0.1:6379> SET name "redis" OK 127.0.0.1:6379> GET name "redis"

4.3 核心配置 redis.conf

# redis.conf 关键配置 bind 0.0.0.0 # 监听所有网卡(生产要限制) port 6379 daemonize yes # 后台运行 requirepass yourpassword # 设置密码(生产必设!) # 内存管理 maxmemory 2gb # 最多用 2G 内存 maxmemory-policy allkeys-lru # 内存满了按 LRU 淘汰最久不用的 key # 持久化 appendonly yes # 开启 AOF 持久化 appendfsync everysec # 每秒刷盘(性能和安全的平衡) dir /var/lib/redis # 数据存放目录

4.4 九大数据结构(每个配命令 + 应用场景)

Redis 九种数据结构及应用场景,面试必背
结构常用命令解释应用场景
StringSET/GET/INCR/EXPIRE最基本的 KV,值可以是字符串/数字/二进制缓存对象、计数器、分布式锁
HashHSET/HGET/HGETALL键值对集合,像一个小字典存用户信息(user:1 → name/age/email)
ListLPUSH/RPUSH/LPOP/RPOP/LRANGE双向链表,两端进出消息队列、最新动态列表
SetSADD/SMEMBERS/SISMEMBER/SINTER无序去重集合标签、共同好友、去重
ZSetZADD/ZRANGE/ZRANK有序集合,每个元素带 score 排序排行榜、热度榜、延迟队列
BitmapSETBIT/GETBIT/BITCOUNT位操作,一个 bit 存一个状态签到打卡、用户在线状态
HyperLogLogPFADD/PFCOUNT/PFMERGE基数统计(去重计数),省内存UV 统计(百万级只需 12KB)
GeoGEOADD/GEORADIUS地理位置(经纬度)附近的人/店
StreamXADD/XREAD/XGROUP消息流(Kafka 轻量版)轻量级消息队列

4.5 持久化:RDB 和 AOF

RDB vs AOF 持久化对比
对比点RDB(快照)AOF(日志)
原理定时把内存数据全量快照存磁盘每写一条命令追加到日志文件
恢复速度快(直接加载快照)慢(重放所有命令)
数据安全差(两次快照之间崩了丢数据)好(everysec 最多丢 1 秒)
文件大小小(压缩二进制)大(文本日志)
生产建议做备份开 everysec,和 RDB 混合

Redis 4.0+ 支持 RDB+AOF 混合持久化:重启时先加载 RDB 快速恢复,再重放 AOF 增量。生产推荐这个模式。

4.6 过期策略与淘汰策略

论key 过期了怎么办

主动过期:Redis 每秒随机抽几个设了 TTL 的 key 检查,过期了就删。惰性过期:你访问一个 key 时才检查它过没过期。两种结合,省 CPU 又省内存。

内存满了怎么办?六种淘汰策略:

maxmemory-policy 六种淘汰策略
策略淘汰谁
noeviction不淘汰,写直接报错(默认)
allkeys-lru所有 key 里淘汰最久没用的(最常用)
volatile-lru只在设了 TTL 的 key 里 LRU 淘汰
allkeys-lfu所有 key 里淘汰最少用的(8.x 推荐)
volatile-lfu只在设了 TTL 的 key 里 LFU 淘汰
volatile-ttl淘汰最快过期的

4.7 缓存三大问题:穿透、击穿、雪崩

缓存三大经典问题,面试必考
问题原因解决方案一句话
缓存穿透查一个根本不存在的 key,每次都打到数据库布隆过滤器(Bloom Filter)拦住;缓存空值坏人查不存在的数据
缓存击穿某个热点 key 突然过期,瞬间大量请求打到数据库热点 key 不过期;互斥锁(只放一个请求去查库)热点 key 过期瞬间
缓存雪崩大量 key 同时过期,或 Redis 挂了,请求全打数据库过期时间加随机值;Redis 集群;熔断降级大片 key 同时过期
布隆过滤器是什么

布隆过滤器是一个巨大的 bit 数组 + 多个哈希函数。把数据哈希到位数组里,查的时候看对应位是不是 1。它说"不存在"就一定不存在,说"存在"可能误判。所以:先用布隆过滤器挡一道,说不存在的直接返回,不用去查 Redis 和数据库。Redis 自己就支持布隆过滤器模块(RedisBloom)。

4.8 分布式锁:SET NX PX

多台应用同时操作同一个资源(比如扣库存),要加分布式锁。最简单的实现:SET lock_key unique_value NX PX 30000——NX 表示"key 不存在才设",PX 表示"30 毫秒后自动过期"。用完了 DEL 掉。

# 加锁:只有一个线程能成功 SET lock:order:1001 "uuid-1234" NX PX 30000 # 处理业务... # 释放锁:用 Lua 脚本确保"只删自己的锁" if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end

论Redisson:别自己造轮子

生产环境别手写分布式锁。Redisson(Java 客户端)封装好了:可重入锁、公平锁、读写锁、锁续期(watch dog)——你设了 30 秒过期,但业务还没跑完,它自动续期,不用怕锁提前释放。

Redlock 算法:如果单 Redis 挂了锁就丢了,Redlock 要求同时往 5 个独立 Redis 节点加锁,超过半数(3 个)成功才算拿到锁。但争议大,大多数场景用 Redisson + 单 Redis 主从就够了。

4.9 主从复制、哨兵、集群

Redis 三种高可用方案
方案原理适合
主从复制主写从读,主写从同步。主挂了要手动切换读多写少,容灾要求不高
哨兵 Sentinel独立进程监控主从,主挂了自动选新主并通知客户端中小规模,自动故障转移
集群 Cluster16384 个哈希槽分到多节点,每个节点管一部分槽,自动分片+故障转移大规模、数据量大、要水平扩展

Cluster 的哈希槽:Redis Cluster 把所有 key 分到 16384 个槽里,每个 Master 节点管一段槽。新增节点时把槽迁过去就行。客户端连任何节点,收到 MOVED 重定向就去正确节点找。

4.10 完整案例:Redis 商品秒杀

秒杀场景:100 件商品,瞬间 1 万人来抢。数据库直接打挂?用 Redis 扛:库存放 Redis,扣减用 Lua 脚本(原子操作,防超卖),抢成功的人写数据库,没抢到的直接返回。

# 1. 初始化库存(提前把库存放 Redis) SECKILL:stock:1001 100 # 商品 1001,库存 100 # 2. Lua 脚本:原子扣库存(防超卖) local stock = redis.call("get", KEYS[1]) if stock == false then return -1 # 商品不存在 end if tonumber(stock) <= 0 then return 0 # 卖完了 end redis.call("decr", KEYS[1]) return 1 # 抢成功 # 3. Java 调用(Spring Boot + Redisson) # RScript.eval() 执行 Lua 脚本,返回 1 = 抢成功,去写数据库订单

4.11 Memcached:纯内存 KV,轻量但功能少

Redis vs Memcached 选型
对比点RedisMemcached
数据结构9 种(String/Hash/List/ZSet...)只有 String
持久化RDB + AOF不支持,重启就没
线程模型单线程(事件驱动)多线程
集群原生 Cluster客户端分片
内存管理多种淘汰策略Slab 分配
适合复杂业务、排行榜、分布式锁纯缓存、简单 KV

现在 90% 的场景用 Redis。Memcached 只剩一些老项目和纯缓存场景。它唯一的优势是多线程、简单,但 Redis 单线程已经够快了。

4.12 Redis 面试重点

Q1:Redis 为什么快?

查看答案

纯内存操作;单线程避免上下文切换;IO 多路复用(epoll);高效数据结构(跳表、压缩列表)。

Q2:缓存穿透/击穿/雪崩怎么区别和解决?

查看答案

穿透=查不存在的数据→布隆过滤器;击穿=热点key过期→互斥锁/永不过期;雪崩=大量key同时过期→加随机TTL+集群+熔断。

Q3:Redis 分布式锁怎么实现?

查看答案

SET key value NX PX 过期时间,value 设唯一标识(UUID),释放锁用 Lua 脚本先比较再删。生产用 Redisson(自动续期+可重入)。

记
第四章小结

① Redis = 内存百货商店,9 种数据结构全能选手。生产开 AOF everysec + 混合持久化。

② 缓存三兄弟:穿透(布隆过滤器)、击穿(互斥锁)、雪崩(随机 TTL)。

③ 分布式锁用 Redisson,别手写。秒杀用 Lua 脚本原子扣减库存。