Redis
Redis 是以内存数据结构为核心的键值数据库。本文以 Redis 6、7 为基线,负责执行模型、事务和客户端调用。
1. 面试核心
先记住这几条,再读正文- 低延迟机制。 Redis 通过内存访问、专用数据结构和事件循环降低命令延迟。卡 · 适用场景
- 命令执行线程。 Redis 6、7 的主要命令路径串行执行,I/O 线程可分担网络工作。
- 批处理与原子执行。 Pipeline 批量发送命令以减少往返,Lua 脚本在服务端原子执行。卡 · Pipeline 与 Lua
- 事务错误。 EXEC 执行阶段的命令错误只影响该命令,其他命令继续执行。卡 · 原子命令与事务
- 乐观并发控制。 WATCH 监视的键被修改时,EXEC 放弃执行整个事务队列。卡 · WATCH 与乐观并发控制
2. 适用场景
Redis 提供直接操作数据结构的命令。递增、集合更新和队列操作可在服务端完成。
- 缓存保存查询或计算结果,减少重复回源。
- 计数与限流维护短期窗口状态。
- 排行榜按分数保存成员顺序。
- 会话与验证码保存有期限的状态。
- Stream 保存带消费确认的消息记录。
复杂关联查询和外键约束更适合关系型数据库。选择存储还需比较数据规模、恢复目标和一致性要求。
3. 数据类型
String、Hash、List、Set、ZSet 和 Stream 提供不同操作。选择规则与内部编码见 Redis 数据结构。
4. 低延迟的原因
Redis 主要在内存中访问数据,省去常规查询的磁盘定位。专用数据结构缩短计数、成员查找和排名等操作路径。事件循环通过 I/O 多路复用处理许多连接。
Redis 6、7 的主要命令执行路径由主线程串行处理。多个命令无需同时争用同一数据结构的写锁。网络 I/O、异步释放和持久化还使用线程或子进程。
io-threads 可配置 I/O 线程数量。读取分担还由 io-threads-do-reads 控制。I/O 线程数增加后,主要命令仍串行执行。
KEYS 全量扫描、大集合操作和长脚本会拖慢其他请求。渐进遍历与对象大小控制见 SCAN 游标遍历。
5. 过期与淘汰
TTL 控制键何时失效,具体规则见 Redis 过期。内存压力下的候选选择见 Redis 内存淘汰。
6. 原子命令与事务
MULTI 将后续命令加入事务队列。EXEC 按序执行队列,执行期间其他客户端命令不会插入。DISCARD 放弃尚未执行的队列。
SET demo:number abc
MULTI
INCR demo:number
SET demo:flag done
EXEC
GET demo:flag
INCR 因内容无法转为整数而报错。SET 仍执行成功,最后 GET 返回 done。EXEC 返回数组,客户端应逐项检查结果。
入队阶段的错误有不同处理。例如参数数量错误会标记事务失败。Redis 6、7 的 EXEC 随后返回 EXECABORT,整队命令不执行。
Redis 事务与 Lua 都没有自动撤销已完成写入的机制。脚本应先校验输入,再执行写入,减少中途出错。
7. WATCH 与乐观并发控制
WATCH 将键纳入乐观并发检查。应在读取前执行 WATCH,并在同一连接完成事务。
SET demo:balance 100
WATCH demo:balance
GET demo:balance
MULTI
SET demo:balance 90
EXEC
本例读取到 100,客户端计算扣减 10。期间键被其他客户端修改时,EXEC 返回空结果。事务队列整体放弃,客户端需重新读取并有限重试。
Redis 6.0.9 起,被监视键过期也会导致事务放弃。淘汰同样会触发冲突。EXEC、DISCARD 或 UNWATCH 会清除监视状态。连接复用时,应确保状态在归还前清理。
8. 客户端配置
连接可由客户端复用,或通过连接池管理。应分别设置建连、命令与借用连接的超时。
Pipeline 一次发送多条命令,减少网络往返。批次过大时,待回复数据会占用更多服务端内存。客户端也需限制单批命令数和返回体积。
诊断延迟时,对照客户端耗时、SLOWLOG 与网络指标。SLOWLOG 记录命令执行时间,排除客户端通信等 I/O。慢日志很短时,网络和客户端排队仍能拉高总延迟。
9. 常见问题
9.1 Redis 的存储边界
存储选型还需判断查询能力、一致性和恢复目标。Redis 保存权威状态时,要配置持久化、复制与独立备份。可重建缓存与权威数据应分别规划恢复方式。
9.2 Pipeline 与 Lua
Pipeline 减少网络往返,各命令之间仍可穿插其他请求。Lua 在服务端执行完整逻辑,执行期间阻塞其他命令。脚本适合有界的原子读改写,需限制输入和循环规模。
10 · 面试题卡片
本页题目与参考回答
10.1 Redis 为什么快,使用时有哪些边界
考察重点
- 内存访问、数据结构与命令执行模型。
- 阻塞命令、大 key、网络和持久化成本。
- 过期、淘汰、缓存一致性与数据边界。
参考回答
Redis 主要通过内存访问、专用数据结构和事件循环降低延迟。
Redis 6、7 的主要命令路径串行执行,减少共享结构的锁竞争。I/O 线程与后台进程承担部分其他工作。
命令复杂度、对象大小、网络和持久化仍影响总延迟。长脚本和全量扫描会占用执行线程。诊断时对照客户端时间、SLOWLOG、内存和回源指标。
10.2 Redis 事务中一条命令报错,其他命令会执行或回滚吗?
考察重点:入队错误、执行错误和结果数组。
相关内容:原子命令与事务。
参考回答
入队阶段错误会让 EXEC 放弃整个队列。
执行阶段某条命令报错时,其他命令继续执行。此前成功写入也保留结果。客户端应逐项检查 EXEC 返回数组,并设计业务恢复。
10.3 WATCH 怎样避免并发覆盖,键过期时会发生什么?
考察重点:同连接监视、冲突放弃和版本边界。
相关内容:WATCH 与乐观并发控制。
参考回答
WATCH 在读取前监视键,EXEC 检查期间是否发生修改。
发生冲突时,整个队列放弃,客户端需重新读取。Redis 6.0.9 起,过期也会触发该检查失败。流程需在同一连接执行,并限制重试次数。
10.4 Pipeline、MULTI/EXEC 和 Lua 各提供什么保证?
考察重点:网络批处理、顺序执行和服务端逻辑。
相关内容:Pipeline 与 Lua、原子命令与事务。
参考回答
Pipeline 减少往返,命令间允许其他请求插入。
MULTI/EXEC 将队列作为一段连续命令执行。Lua 还支持在服务端读取后进行条件判断。后两者均需处理运行错误,已完成写入不会自动撤销。