艾体宝干货|【Redis实用技巧#16】架构解析(下):持久化、Lua、内存管理与集群设计

6 阅读4分钟

在上一篇中,我们分析了 Redis 的几个核心架构:

  • 单线程执行模型
  • I/O 多路复用
  • 高效数据结构

但真正让 Redis 在生产环境稳定运行的关键,还包括:

  • 持久化策略
  • Lua 原子脚本
  • 内存管理
  • Pipeline 优化
  • Cluster 扩展

这些机制决定了 Redis 在 ​高并发系统中的工程表现​。

Redis 持久化机制

Redis 是 ​内存数据库​。

如果完全不持久化:

Redis restart = 数据全部丢失

因此 Redis 提供两种持久化方式:

机制类型
RDB快照
AOF操作日志

RDB:快照机制

RDB 会定期生成 ​数据集快照​。

配置示例:

save 900 1
save 300 10
save 60 10000

含义:

条件触发
900 秒内1 次写入
300 秒内10 次写入
60 秒内10000 次写入

执行流程

当触发保存:

Redis main process
        │
        │ fork()
        ▼
Child Process  → 写入 dump.rdb
Parent Process → 继续服务请求

关键技术:

Copy-On-Write

写操作发生时才复制内存页

隐藏问题:fork 延迟

如果 Redis 数据量很大:

dataset = 10GB

fork 可能需要:

200ms - 500ms

在此期间:

Redis 停止响应请求

这也是很多系统出现 周期性延迟尖刺 的原因。

AOF:操作日志

AOF 记录所有写操作:

SET user:1 John
INCR counter
DEL key

Redis 重启时:

Replay log → 重建数据

AOF 同步策略

配置:

appendfsync always
appendfsync everysec
appendfsync no

含义:

策略特点
always最安全 最慢
everysec推荐
no最快

一般生产环境:

everysec

最多丢失:1 秒数据

AOF Rewrite

AOF 会不断增长。

例如:

SET key 1
SET key 2
SET key 3

其实只需要:

SET key 3

因此 Redis 会执行 ​AOF 重写​:

BGREWRITEAOF

生成更小的日志。

Lua 脚本:为什么 Redis 需要 Lua

很多开发者第一次写 Redis 代码会遇到这种逻辑:

GET counter
if counter < 100
    INCR counter
    EXPIRE counter 60

问题:

GET 和 INCR 之间可能被其他请求插入

导致:限流失效

Lua 解决方案

Lua 脚本在 Redis 中:

原子执行

示例:

local key = KEYS[1]local limit = tonumber(ARGV[1])local ttl = tonumber(ARGV[2])local current = redis.call("GET", key)if not current then
    current = 0else
    current = tonumber(current)endif current < limit then
    redis.call("INCR", key)
    redis.call("EXPIRE", key, ttl)return 1elsereturn 0end

执行:

EVALSHA script

特点:

特性说明
原子执行不会被打断
减少 RTT一次请求
服务器逻辑避免客户端协调

Redis 内存管理

Redis 完全运行在内存中。

因此 ​内存管理极其关键​。

内存限制

配置:

maxmemory 2gb

当超过限制:

触发 ​淘汰策略​。

常见策略

策略说明
noeviction拒绝写入
allkeys-lru淘汰最近最少使用
volatile-lru淘汰带 TTL 的 key
allkeys-random随机

缓存系统通常使用:

allkeys-lru

Redis LRU 不是严格 LRU

为了性能:

Redis 使用 ​采样算法​:

随机选 N 个 key
淘汰最旧的

这样避免维护完整 LRU 链表。

内存碎片

运行一段时间后可能看到:

used_memory = 2.5GB
used_memory_rss = 4.8GB

碎片率:

1.92

原因:

  • 内存分配块
  • key 删除留下空洞

解决方案:

activedefrag yes

或者:定期重启 Redis

Pipeline:Redis 性能加速

很多系统 Redis 慢的原因不是 Redis 本身。

而是:

网络 RTT

例如:

1000 次 SET

如果逐条发送:

1000 次 RTT

Pipeline

Pipeline pipeline = jedis.pipelined()

for i=0..1000
    pipeline.set(key,value)

pipeline.sync()

效果:

1000 请求 → 1 次 RTT

性能提升:

10x - 100x

Redis 事务的行为

Redis 提供:

MULTI
EXEC

但很多人误以为它类似 SQL 事务。

实际上:

Redis ​没有 rollback​。

示例:

MULTI
SET key1 value1
BADCOMMAND
SET key2 value2
EXEC

结果:

key1 成功
key2 成功

即使中间有错误。

因此:

复杂原子逻辑应该使用:

Lua Script

而不是事务。

Redis Cluster

当单机 Redis 无法满足需求时,需要:

水平扩展

Redis Cluster 使用:

16384 hash slots

计算:

CRC16(key) % 16384

分布示例:

NodeA 0-5460
NodeB 5461-10922
NodeC 10923-16383

多 Key 限制

例如:

MGET key1 key2

如果 key 在不同节点:

CROSSSLOT error

解决方案:

hash tags

示例:

{user}:1
{user}:2

同一 slot。

Redis 监控

生产环境必须监控:

SLOWLOG

SLOWLOG GET

常见慢操作:

  • KEYS *
  • 大集合扫描

INFO

关键指标:

used_memory
connected_clients
evicted_keys
keyspace_hits

建议监控:

指标阈值
内存>80%
缓存命中率<95%
evicted_keys>0

总结

Redis 的成功并不是偶然。它依赖一系列 ​极度克制的架构设计​:

设计选择目的
单线程简化并发
I/O 多路复用高连接数
数据结构优化高性能
Lua原子逻辑
Pipeline减少 RTT
Cluster水平扩展

理解这些机制之后,你会发现:

Redis 并不是“简单缓存”,而是一套 ​高性能实时数据平台​。