跳到主要内容

Redis

Redis 是一个以内存数据结构为核心的键值数据库,常用于缓存、计数、限流、排行榜和短期状态存储。

1. Redis 适合解决哪些问题

Redis 的主要优势是低延迟,以及服务端直接提供的数据结构操作。应用不必每次读取完整对象再修改,可以让 Redis 原子地完成递增、集合更新或队列写入。

常见场景包括:

  • 缓存:保存数据库查询结果或计算结果,降低源站压力。
  • 计数与限流:通过 INCR、过期时间或 Lua 脚本维护窗口状态。
  • 排行榜:使用 Sorted Set 按分值排序,并查询名次和区间。
  • 会话与临时状态:保存登录会话、验证码和短期任务状态。
  • 消息传递:使用 Stream 保存可消费、可确认的消息记录。

Redis 不是关系型数据库的通用替代品。需要复杂关联查询、严格外键约束或长事务时,关系型数据库通常更合适。

2. 数据类型决定操作方式

Redis 的 key 是字符串,value 可以使用多种数据类型:

数据类型常见操作适用场景
StringGETSETINCR缓存、计数器、分布式状态
HashHGETHSET字段较多的小对象
ListLPUSHBRPOP简单队列、时间顺序数据
SetSADDSINTER去重、集合关系
Sorted SetZADDZRANGE排行榜、延迟任务索引
StreamXADDXREADGROUP消费组、可确认消息

选择类型时先看需要执行的操作,再看内存成本。把所有内容序列化为一个 String 虽然简单,但会失去字段级更新和服务端原子操作;把对象拆成大量小 key,又会增加 key、过期时间和元数据的开销。

3. Redis 为什么延迟较低

Redis 的数据主要保存在内存中,常用数据结构的操作路径较短。命令执行默认由主线程串行处理,因此单条命令不需要为共享数据加锁,也不会出现多个命令同时修改同一结构的竞态。

“单线程”只描述主要命令执行路径。网络读取、持久化、异步释放内存等工作可以由其他线程或子进程完成。串行执行也不代表不会阻塞:大 key 操作、复杂 Lua 脚本、慢磁盘和过量连接都可能拉高延迟。

先检查命令复杂度

KEYS *、对超大集合执行全量读取,以及一次删除大 key,都可能长期占用命令执行线程。线上查询优先使用渐进式扫描,并限制单次返回数量。

4. 过期与淘汰负责不同事情

为 key 设置 TTL 后,Redis 会通过定期抽样和访问时检查清理过期数据。过期策略回答“这条数据什么时候失效”。

内存达到 maxmemory 后,Redis 按配置的淘汰策略决定是否以及删除哪些 key。淘汰策略回答“内存不够时牺牲哪些数据”。两者需要分别设计:缓存可以接受淘汰,作为事实来源的数据则不能依赖淘汰策略保证完整性。

排查内存问题时,应同时检查 key 数量、value 大小、TTL 分布、内存碎片和淘汰次数,不能只看进程 RSS。

5. 原子命令不等于完整业务事务

单条 Redis 命令在服务端串行执行,具备原子性。多个步骤需要一起判断和更新时,可以使用 Lua 脚本,或按场景使用 WATCHMULTIEXEC

Redis 事务不会提供关系型数据库式的回滚。EXEC 执行期间某条命令报错,其他已经执行的命令不会自动撤销。只要业务还要同时修改数据库、发送消息或调用外部服务,就需要额外处理幂等、重试和一致性边界。

6. 客户端配置会影响最终延迟

应用侧通常使用连接池或复用连接,并设置明确的建连、命令和借用超时。Pipeline 可以减少多条独立命令的网络往返,但不会让这些命令成为一个事务;批次过大会增加服务端排队时间和客户端内存占用。

生产环境至少观察以下指标:

  • 命令延迟分位数与慢日志。
  • 命中率、过期量与淘汰量。
  • 内存使用、碎片率与大 key。
  • 连接数、阻塞客户端和复制延迟。
  • 缓存回源量,以及源数据库是否被突发流量压垮。

7. 常见问题

7.1 Redis 快,所以可以保存所有业务数据吗

不能只按延迟选择存储。还要判断持久化、故障恢复、查询能力、数据规模和一致性要求。Redis 可以作为事实来源,但这需要明确配置持久化、复制、备份与恢复目标,不能沿用普通缓存的运维方式。

7.2 Pipeline 和 Lua 脚本有什么区别

Pipeline 主要减少网络往返,命令之间仍可能穿插其他客户端的操作。Lua 脚本在服务端作为一个整体执行,适合短小的原子读改写;脚本运行期间会阻塞其他命令,因此要限制复杂度和输入规模。

8. 面试题

8.1 Redis 为什么快,使用时有哪些边界

出现公司:美团、字节跳动

考察重点

  • 内存访问、数据结构与命令执行模型。
  • 阻塞命令、大 key、网络和持久化成本。
  • 过期、淘汰、缓存一致性与数据边界。

相关内容:第 1 节“Redis 适合解决哪些问题”至第 6 节“客户端配置会影响最终延迟”。

参考回答

Redis 的数据主要位于内存,针对 String、Hash、Set 和 Sorted Set 等结构提供直接操作;主要命令执行路径串行,省去了同一数据结构上的锁竞争。事件驱动网络模型还能让一个实例同时处理大量连接。

低延迟仍受命令复杂度、value 大小、网络往返、持久化和内存压力影响。大 key、长 Lua 脚本或全量扫描会阻塞后续命令。设计时要选择合适的数据类型与 TTL,区分过期和内存淘汰,限制批次,并观察慢日志、P99、淘汰量和回源流量。需要跨数据库一致性或复杂事务时,还要在 Redis 之外设计幂等与补偿。