Skip to content
← 返回Redis列表
数据与原子性

Redis 知识复盘 · 数据结构与使用边界至事务与条件更新 ​

返回分篇目录

先按业务选择数据结构,再理解内存、持久化与复制,最后处理事务、锁和缓存异常。每节集中回答一类问题,避免把同一概念拆成多组问答。

Redis 知识复盘:数据、可靠性与缓存设计 ​

先按业务选择数据结构,再理解内存、持久化与复制,最后处理事务、锁和缓存异常。每节集中回答一类问题,避免把同一概念拆成多组问答。

数据结构与使用边界 ​

根据访问方式选择类型 ​

需求类型需要考虑的边界
缓存对象、验证码、计数器String序列化成本、过期时间、单值大小
按字段读写对象Hash小对象可能使用紧凑编码,不能假定永远比多个键省内存
两端入队出队、最近记录List消费确认、失败重试需要另行设计
去重、交并差、共同好友Set大集合运算可能占用执行时间
排行榜、按时间取到期任务Sorted Set分数范围、并列排序、取出与删除的原子性

这些是常见基础类型,不代表 Redis 只有五种类型。队列还可以考虑 Stream;选型时应比较消费组、确认与重放需求,不能把 Pub/Sub 当成可靠消息存储。

Redis、本地缓存与 Memcached ​

本地缓存访问不经过网络,但各应用实例拥有独立副本;Redis 让多个实例访问同一份远程数据,也引入网络、故障和容量管理成本。共享缓存不等于缓存与数据库自动一致。

Memcached 主要提供对象缓存;Redis 还提供集合操作和可选持久化等能力。两者的延迟与吞吐取决于数据、命令、硬件和拓扑,不使用固定 QPS 或“必然快很多”作为结论。

缓存适合读多、可复用且允许明确陈旧窗口的数据。不能仅凭“更新前读取两次”判断是否值得缓存,应比较命中率、回源成本、更新成本与一致性要求。

执行模型与慢请求 ​

为什么快,什么操作会阻塞 ​

常见原因是内存访问、适合业务的数据结构、事件循环与 I/O 多路复用。命令执行路径与后台持久化、释放内存、网络 I/O 等线程不能混为一谈;具体线程行为取决于版本和配置。

大键、长 Lua 脚本、全量遍历和大集合运算仍可能阻塞其他请求。性能排查先看慢日志、命令复杂度、键大小、网络往返和持久化开销,不把“单线程”解释为进程只能使用一个 CPU 核。

批量写入与扫描键空间 ​

  • Pipeline 减少多次网络往返,不提供事务隔离;客户端需要限制单批数量和响应内存。
  • redis-cli --pipe 适合按 Redis 协议批量导入;导入前仍需确认目标、容量和错误统计。
  • 在线遍历优先使用游标式 SCAN,以返回游标再次为 0 为一轮结束;MATCH 是过滤条件,COUNT 是工作量提示,不保证一次返回指定条数。
  • 扫描结果可能重复,应去重;扫描期间变更的数据不能当作一致快照。每次扫描也有开销,不能称为完全无阻塞。
  • KEYS 一次扫描整个键空间,不宜直接用于大规模在线实例。

过期与内存淘汰 ​

两种机制分别解决什么问题 ​

过期根据 TTL 判断数据是否失效,Redis 通过访问时检查及主动过期等机制处理过期键。EXPIRE 设置有效期,PERSIST 取消有效期。

淘汰在内存达到 maxmemory 限制时按配置选择可移除的键;它不替代业务 TTL,也不会自动识别哪些数据对业务更重要。

策略族候选范围选择方式
allkeys-lru / allkeys-lfu所有键近似最近使用 / 使用频率
volatile-lru / volatile-lfu设置过期时间的键近似最近使用 / 使用频率
allkeys-random / volatile-random所有键 / 有过期时间的键随机
volatile-ttl有过期时间的键优先更接近过期的键
noeviction不主动淘汰需要额外内存的写入可能失败

容量与热点优化 ​

先确定哪些数据可以丢失,再选择策略。记录键数量、值大小分布、命中率、淘汰量与复制/持久化峰值;为缓冲、fork 后写时复制和系统开销预留空间,不把 maxmemory 当成进程 RSS 的硬上限。

对象可按字段合并到 Hash,但也要防止形成大键和热点。分片能分散多个键,不能自动把一个超大 Sorted Set 拆成多份。不要在没有容量与流量证据时预先部署几十个实例。

持久化与恢复选择 ​

方式保存什么主要取舍
RDB某个时点的数据快照便于备份和恢复;可能丢失快照后的更新,生成快照也有资源开销
AOF写操作日志丢失窗口与刷盘策略有关;需要考虑日志增长、重写和恢复成本
两者结合日志与快照可同时满足恢复与备份需求,仍须验证实际恢复过程

后台生成 RDB 不等于主线程完全没有停顿或 I/O 成本;fork、写时复制和磁盘竞争都可能影响延迟。AOF 的 always、everysec、no 刷盘策略在延迟与丢失窗口之间取舍,不能把任何模式称为绝对零丢失。

两种持久化都启用时,重启恢复通常使用 AOF。AOF 的布局与恢复流程有版本差异,不能照旧教程直接删除日志中的某行来“撤销误操作”。先保全文件,在隔离环境按版本工具检查与恢复。

备份与主从复制用途不同:副本会同步错误写入和删除。至少保留独立备份并做恢复演练;不能为了性能笼统关闭所有主库持久化,再把副本视作唯一保障。

复制、故障转移与分片 ​

主从复制先解决副本问题 ​

复制通常是异步的,副本读可能落后。首次同步或无法增量续传时需要全量同步;连接恢复后能否部分同步,取决于复制标识及积压缓冲区是否覆盖缺口。

全量同步涉及生成/传输快照、装载快照和追赶增量,不保证复制期间任何操作都毫无停顿。空主库重启也可能把空数据传播给副本,因此应结合持久化和故障恢复流程设计启动行为。

Sentinel 与 Cluster 各负责什么 ​

方案解决的问题没有自动解决的问题
主从复制数据副本、可选的读扩展自动选主、写入分片、零数据丢失
Sentinel监控主从、选举和故障转移、发现新主库数据分片与强一致性
Redis Cluster哈希槽分片、节点间故障处理与副本接管跨槽任意多键事务与异步复制丢失窗口

Sentinel 部署需要考虑 quorum、选举多数票与故障域,通常至少部署三个相互独立的 Sentinel。不能只凭“每主一从”就断言所有网络分区下都能继续服务。

Cluster 哈希槽与客户端重定向 ​

Cluster 使用 16384 个槽,键通过 CRC16 与槽数取模确定槽;{...} hash tag 可让有关联的键落到同槽。客户端收到 MOVED 或迁移中的 ASK 后访问正确节点,不是任意服务端替客户端透明转发命令。

扩缩容需要迁移槽与数据;副本数量独立配置,不是每个节点都天然有 N−1 个副本。Cluster 只使用数据库 0;多键操作、事务和脚本通常要求有关联的键位于同槽。支持 Cluster 的客户端可以按节点处理 Pipeline,不能笼统说集群“不支持 Pipeline”。

客户端分片将路由放在应用侧,代理分片增加中转层;两者都要明确拓扑更新、跨分片操作与备份恢复边界。副本扩容主要增加可用副本或读能力,不能替代写入分片。

事务与条件更新 ​

MULTI、EXEC、WATCH 的执行语义 ​

  1. 必要时 WATCH 观察要读取并修改的键。
  2. 读取并在应用端计算新值。
  3. MULTI 开始排队,发送写命令。
  4. EXEC 执行;若被观察的键已变化,整个事务不执行,客户端决定是否重试。

DISCARD 放弃尚未执行的队列,UNWATCH 取消监视。成功执行 EXEC 时,队列中的命令按顺序执行,其他客户端命令不会插入其中。

出错不等于关系数据库回滚 ​

失败位置结果
排队阶段发现命令错误当前支持版本会在 EXEC 时拒绝执行该事务
WATCH 条件冲突EXEC 返回空结果,队列不执行
执行时发生类型等运行错误其他命令仍会执行,不撤销已成功的写入

Lua 脚本也不自动回滚已发生的写入;运行错误会终止后续脚本执行。先验证输入与键类型,再执行写入。事务、脚本的持久性仍受持久化设置影响,不把“执行不被插入”直接等同完整 ACID 保证。

继续阅读