Redis
Redis 是一个以内存数据结构为核心的键值数据库,常用于缓存、计数、限流、排行榜和短期状态存储。
1. Redis 适合解决哪些问题
Redis 的主要优势是低延迟,以及服务端直接提供的数据结构操作。应用不必每次读取完整对象再修改,可以让 Redis 原子地完成递增、集合更新或队列写入。
常见场景包括:
- 缓存:保存数据库查询结果或计算结果,降低源站压力。
- 计数与限流:通过
INCR、过期时间或 Lua 脚本维护窗口状态。 - 排行榜:使用 Sorted Set 按分值排序,并查询名次和区间。
- 会话与临时状态:保存登录会话、验证码和短期任务状态。
- 消息传递:使用 Stream 保存可消费、可确认的消息记录。
Redis 不是关系型数据库的通用替代品。需要复杂关联查询、严格外键约束或长事务时,关系型数据库通常更合适。
2. 数据类型决定操作方式
Redis 的 key 是字符串,value 可以使用多种数据类型:
| 数据类型 | 常见操作 | 适用场景 |
|---|---|---|
| String | GET、SET、INCR | 缓存、计数器、分布式状态 |
| Hash | HGET、HSET | 字段较多的小对象 |
| List | LPUSH、BRPOP | 简单队列、时间顺序数据 |
| Set | SADD、SINTER | 去重、集合关系 |
| Sorted Set | ZADD、ZRANGE | 排行榜、延迟任务索引 |
| Stream | XADD、XREADGROUP | 消费组、可确认消息 |
选择类型时先看需要执行的操作,再看内存成本。把所有内容序列化为一个 String 虽然简单,但会失去字段级更新和服务端原子操作;把对象拆成大量小 key,又会增加 key、过期时间和元数据的开销。
3. Redis 为什么延迟较低
Redis 的数据主要保存在内存中,常用数据结构的操作路径较短。命令执行默认由主线程串行处理,因此单条命令不需要为共享数据加锁,也不会出现多个命令同时修改同一结构的竞态。
“单线程”只描述主要命令执行路径。网络读取、持久化、异步释放内存等工作可以由其他线程或子进程完成。串行执行也不代表不会阻塞:大 key 操作、复杂 Lua 脚本、慢磁盘和过量连接都可能拉高延迟。
KEYS *、对超大集合执行全量读取,以及一次删除大 key,都可能长期占用命令执行线程。线上查询优先使用渐进式扫描,并限制单次返回数量。
4. 过期与淘汰负责不同事情
为 key 设置 TTL 后,Redis 会通过定期抽样和访问时检查清理过期数据。过期策略回答“这条数据什么时候失效”。
内存达到 maxmemory 后,Redis 按配置的淘汰策略决定是否以及删除哪些 key。淘汰策略回答“内存不够时牺牲哪些数据”。两者需要分别设计:缓存可以接受淘汰,作为事实来源的数据则不能依赖淘汰策略保证完整性。
排查内存问题时,应同时检查 key 数量、value 大小、TTL 分布、内存碎片和淘汰次数,不能只看进程 RSS。
5. 原子命令不等于完整业务事务
单条 Redis 命令在服务端串行执行,具备原子性。多个步骤需要一起判断和更新时,可以使用 Lua 脚本,或按场景使用 WATCH、MULTI、EXEC。
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 之外设计幂等与补偿。