Redis 在缓存架构中的角色
Redis 在缓存架构中并非简单的键值对存储,而是承担着加速层、缓冲层与状态协调层三重角色。其核心价值在于将热点数据从慢速的磁盘型数据库(如 MySQL、PostgreSQL)提升至内存层级,同时通过原子操作与数据结构能力,在缓存与数据库之间建立可预测的一致性边界。
Redis 作为缓存层的核心优势
| 优势维度 | 具体表现 | 技术支撑 |
|---|---|---|
| 极致延迟 | 平均读写延迟 < 1ms(P99 < 5ms) | 全内存数据存储、单线程事件循环、非阻塞 I/O |
| 高吞吐量 | 单实例可达 10万+ QPS(读场景更高) | 纯内存操作 + 多路复用 I/O 模型 |
| 丰富数据结构 | String、Hash、List、Set、Sorted Set、HyperLogLog、Bitmap、Stream | 直接支持缓存对象、计数器、排行榜、布隆过滤器等场景 |
| 原子性保证 | 单命令原子 + Lua 脚本原子 + 事务乐观锁 | 单线程执行模型保证命令级原子性 |
| 淘汰与过期 | 8 种淘汰策略(LRU、LFU、TTL、随机等)+ 惰性删除 + 定期删除 | 精确控制缓存容量与数据新鲜度 |
| 持久化兜底 | RDB 快照 + AOF 日志,重启后快速恢复缓存数据 | 降低缓存雪崩时数据库回源压力 |
Redis 与数据库的典型交互模式
缓存与数据库的交互模式决定了数据一致性的最终形态。以下是生产环境中四种最常用的模式,每种模式对应不同的一致性等级与适用场景。
模式一:Cache-Aside(旁路缓存)
这是最广泛使用的模式,应用层同时感知缓存与数据库的存在,并主动管理数据流向。
- 读流程:应用先查询 Redis → 命中则直接返回 → 未命中则查询数据库 → 将结果写入 Redis 并设置 TTL → 返回给调用方。
- 写流程:应用先更新数据库 → 然后删除 Redis 中的对应缓存(或更新缓存)。
模式二:Read-Through / Write-Through(穿透读写)
缓存层封装了与数据库的交互逻辑,应用只与缓存交互,由缓存组件(如 Redis + 代理层)负责回源与同步。
- Read-Through:缓存未命中时,缓存层自动加载数据库数据并返回,应用无感知。
- Write-Through:应用写入缓存时,缓存层同步将数据写入数据库,保证写入操作的强一致性。
模式三:Write-Behind(异步回写)
应用只写入缓存,缓存层异步批量将数据回写至数据库。适用于写吞吐极高、对瞬时一致性要求不高的场景(如日志、点击流)。
模式四:Cache-Aside + 订阅变更(近实时一致性)
在 Cache-Aside 基础上,引入数据库变更捕获(如 Canal、Debezium)监听 binlog,当数据库数据变更时,通过消息队列通知缓存层删除或更新对应缓存。
| 模式 | 一致性等级 | 写吞吐 | 实现复杂度 | 典型场景 |
|---|---|---|---|---|
| Cache-Aside | 最终一致性(秒级窗口) | 中 | 低 | 通用业务缓存 |
| Read/Write-Through | 强一致性(同步写库) | 低 | 中 | 配置、元数据 |
| Write-Behind | 最终一致性(分钟级) | 极高 | 高 | 日志、埋点、计数 |
| Cache-Aside + binlog | 近实时一致性(毫秒级) | 中 | 高 | 订单、库存、价格 |
Redis 的原子性保证
Redis 的原子性是其作为缓存一致性基石的关键能力。理解原子性的三个层次,有助于在缓存与数据库交互时设计正确的并发控制策略。
层次一:单命令原子性
由于 Redis 采用单线程事件循环模型,每个命令在执行期间不会被其他命令中断。这意味着 INCR、DECR、GETSET、HSET 等操作天然具备原子性。
# 原子递增计数器,无需加锁
INCR article:read_count:1001
# 原子设置并返回旧值,用于分布式锁续期
GETSET lock:order:12345 "new_owner"
层次二:事务原子性(MULTI/EXEC + WATCH)
Redis 事务通过 MULTI 开启,EXEC 执行。事务中的所有命令会被序列化并按顺序执行,期间不会插入其他客户端的命令。但 Redis 事务不支持回滚——如果事务中某条命令执行失败,后续命令仍会继续执行。
WATCH 命令可实现乐观锁,在事务执行前监控 key 是否被其他客户端修改。
# 使用 WATCH 实现乐观锁扣减库存
WATCH product:stock:2001
stock = GET product:stock:2001
if stock > 0
MULTI
DECR product:stock:2001
EXEC
else
UNWATCH
return "库存不足"
层次三:Lua 脚本原子性
Lua 脚本是 Redis 实现复杂原子操作的首选方案。整个脚本作为一个整体被 Redis 单线程执行,脚本执行期间不会处理任何其他命令。这使得开发者可以在缓存层实现包含条件判断、循环、多 key 操作的原子逻辑。
-- Lua 脚本:原子扣减库存并记录操作日志
local stock_key = KEYS[1]
local log_key = KEYS[2]
local quantity = tonumber(ARGV[1])
local current = redis.call('GET', stock_key)
if not current or tonumber(current) < quantity then
return 0 -- 库存不足
end
redis.call('DECRBY', stock_key, quantity)
redis.call('LPUSH', log_key, 'deduct:' .. quantity .. ' at ' .. ARGV[2])
return 1 -- 扣减成功
| 原子性层次 | 粒度 | 是否可回滚 | 适用场景 |
|---|---|---|---|
| 单命令 | 单个 key 操作 | 不适用 | 计数器、简单状态切换 |
| 事务 (MULTI/EXEC) | 多命令批量 | 否(失败继续执行) | 批量更新、乐观锁扣减 |
| Lua 脚本 | 多 key 复杂逻辑 | 否(但可脚本内判断) | 库存扣减、限流、分布式锁续期 |