Redis 基础回顾:数据结构概览、常用命令、单线程模型与性能优势

在深入缓存读写分离架构之前,必须夯实 Redis 核心基础。本章将系统梳理 Redis 的五大基础数据结构、对应的常用命令、单线程事件驱动模型及其性能优势,为后续的接口契约设计提供底层认知支撑。

2.1 五大基础数据结构概览

Redis 并非简单的键值存储,其值类型支持多种数据结构。理解每种结构的底层实现与适用场景,是正确设计缓存接口的前提。

数据结构 底层编码(3.2+) 最大元素数 典型应用场景
String int / embstr / raw 512 MB 缓存对象、计数器、分布式锁
List quicklist 2³² - 1 消息队列、时间线、最新列表
Hash ziplist / hashtable 2³² - 1 对象字段缓存、用户属性
Set intset / hashtable 2³² - 1 标签系统、去重、共同好友
Sorted Set ziplist / skiplist 2³² - 1 排行榜、延时队列、限流窗口
编码转换要点:当 Hash 或 Sorted Set 的元素数量较少且元素体积较小时,Redis 自动使用压缩的 ziplist 编码以节省内存;超过阈值后自动切换为 hashtable 或 skiplist。接口契约中应避免依赖特定编码,但了解编码有助于预估内存开销。

2.2 常用命令分类速查

命令是接口契约的最小单元。以下按数据结构分类列出最常用的命令,并标注时间复杂度,帮助你在设计接口时预判性能瓶颈。

String 命令

SET key value [EX seconds|PX milliseconds] [NX|XX]   # O(1)
GET key                                                # O(1)
INCR key                                               # O(1)
MSET key1 val1 key2 val2                               # O(N)
MGET key1 key2                                         # O(N)
STRLEN key                                             # O(1)

List 命令

LPUSH key value [value ...]   # O(1) 每个元素
RPUSH key value [value ...]   # O(1)
LPOP key                      # O(1)
RPOP key                      # O(1)
LRANGE key start stop         # O(S+N),S为偏移量,N为返回元素数
LLEN key                      # O(1)

Hash 命令

HSET key field value          # O(1)
HGET key field                # O(1)
HMSET key f1 v1 f2 v2         # O(N)
HMGET key f1 f2               # O(N)
HDEL key field [field ...]    # O(N)
HGETALL key                   # O(N),N为字段数
HLEN key                      # O(1)

Set 命令

SADD key member [member ...]  # O(1) 每个元素
SREM key member [member ...]  # O(1)
SMEMBERS key                  # O(N)
SISMEMBER key member          # O(1)
SCARD key                     # O(1)
SINTER key1 key2              # O(N*M)

Sorted Set 命令

ZADD key score member [score member ...]   # O(log N) 每个元素
ZRANGE key start stop [WITHSCORES]         # O(log N + M)
ZREVRANGE key start stop [WITHSCORES]      # O(log N + M)
ZREM key member [member ...]               # O(log N) 每个元素
ZSCORE key member                          # O(1)
ZCARD key                                  # O(1)
ZRANK key member                           # O(log N)
接口设计警示:KEYS、FLUSHALL、SMEMBERS 等 O(N) 命令在生产环境应严格禁止在接口路径中直接暴露。如需遍历,应使用 SCAN、SSCAN 等游标命令替代。

2.3 单线程模型与性能优势

Redis 的核心处理线程是单线程的,但这并非性能瓶颈,反而是其高性能的关键设计。理解单线程模型有助于在接口契约中合理控制并发与阻塞操作。

Redis 单线程事件驱动模型架构 Client 1 Client 2 Client N I/O 多路复用 (epoll / kqueue) 事件队列 读就绪 / 写就绪 非阻塞轮询 单线程事件处理器 命令解析 命令执行 数据操作 结果返回 串行处理,无锁竞争 内 存 数 据 库 核心设计要点: 1. 所有客户端请求通过 I/O 多路复用进入事件队列,单线程依次处理。 2. 避免上下文切换和锁竞争,纯内存操作使单线程足以支撑 10万+ QPS。

单线程模型的核心优势

优势维度 说明 对接口契约的影响
无锁竞争 所有操作串行执行,无需加锁,不会出现死锁或数据竞争 接口天然线程安全,无需在应用层加分布式锁(除非跨实例)
低上下文切换 单线程避免了 CPU 频繁切换线程的开销 高并发下延迟稳定,适合对 p99 延迟敏感的接口
纯内存访问 数据全部在内存中,读写速度在纳秒级 接口响应时间主要受网络开销影响,而非磁盘 I/O
I/O 多路复用 epoll/kqueue 高效管理大量连接,单线程可处理数万并发 连接数不再是瓶颈,但需注意慢查询会阻塞后续请求
原子性保证 单条命令天然原子,无需事务即可保证简单操作的原子性 INCR、SETNX 等命令可直接用于计数器、分布式锁接口
性能基准参考:在普通物理机(Intel Xeon E5-2650 v3,64GB RAM)上,Redis 单实例 GET/SET 操作可达 10万+ QPS,Pipeline 批量操作可达 100万+ QPS。接口契约设计时应充分利用 Pipeline 减少网络往返。

单线程模型的注意事项

  • 慢查询阻塞:单个耗时命令(如 KEYS *、SMEMBERS 大集合、ZRANGE 大范围)会阻塞整个事件循环,导致后续所有请求排队等待。接口契约中必须对这类操作设置超时或改用 SCAN 系列命令。
  • 大 Key 风险:单个 Key 存储超过 10 MB 的数据(如大 String、大 Hash)会导致读写延迟飙升。接口契约应限制单 Key 大小,或采用分片策略。
  • CPU 密集型操作:虽然 Redis 操作主要是内存访问,但 SORT、ZUNIONSTORE 等命令会消耗较多 CPU。接口设计时应评估复杂度,避免在高峰期执行。

2.4 数据结构选择与接口契约的映射关系

在缓存读写分离架构中,接口契约的数据结构选择直接影响读写性能与一致性维护成本。以下给出常见业务场景与推荐数据结构的映射关系。

业务场景 推荐数据结构 接口契约示例 理由
用户会话缓存 String (JSON) 或 Hash GET session:{token} Hash 支持部分字段更新,减少序列化开销
商品详情缓存 Hash HGETALL product:{id} 字段独立,可按需返回部分字段
最新文章列表 List (LPUSH + LTRIM) LRANGE articles:latest 0 19 天然支持分页,LTRIM 控制列表长度
文章标签去重 Set SISMEMBER tags:article:{id} "redis" O(1) 判断成员是否存在
实时排行榜 Sorted Set ZREVRANGE leaderboard 0 9 WITHSCORES 按分数排序,支持范围查询
接口限流计数器 String (INCR + EXPIRE) INCR rate:limit:{userId} 原子自增,配合过期时间实现滑动窗口
接口契约设计原则:优先使用 Hash 和 Sorted Set 替代 String 序列化存储对象,前者支持部分字段更新,减少网络传输量;后者支持范围查询,避免全量拉取。在读写分离架构中,写接口应尽量使用 HSET、ZADD 等部分更新命令,减少主库写入压力。