07.01 访问模式与状态角色:哪些数据可以丢
返回第七卷 · 数据库业务前置:06.12 · 一致性前置:08.04
07.01 访问模式与状态角色:哪些数据可以丢
DeepTutor 八节初稿经技术与教学审阅后的静态课程。所有消息、命中表与缓存故障均为虚构教学情景;没有运行 Redis、数据库、Go、IM 服务、压测或站点,也不声称 OpenIM 实际用下面的数据结构。当前 S2
200 accepted_in_memory与未来 S3 v2stored_in_teaching_db提议严格分开。
一、先问“丢了会怎样”,再问“放在哪里”
06.12 已把 c-a 的历史延长到 seq=9(新消息 m-9),08.01–08.04 又说明跨节点失败、时间和副本可见性的边界。现在业务想加快会话列表、显示在线用户、限制重复请求,常听到一句“放缓存里”。这句话缺少最重要的条件:那份数据是不是权威事实?丢失后能否从别处正确重建?多旧仍可用?
在当前 S2 v1,成功 200 accepted_in_memory 只确认本进程的易失内存受理;若进程崩溃,已受理消息可能消失,不能凭“加了缓存”变成持久历史。未来 S3 v2 若按纸上提议让数据库事务提交后返回 stored_in_teaching_db,权威消息才在所选数据库提交/恢复合同下保存在数据库。这两个阶段的状态角色不同,不得用 S3 假设为 S2 事后补承诺。06.12 数据库业务案例
| 学习轮次 | 关键问题 | 交付物 |
|---|---|---|
| 第一遍 | 权威、派生、临时;消息、权限、预览、在线和计数各属哪类 | 一张按状态划分的丢失/重建表 |
| 第二遍 | 访问模式、热点、命中/陈旧、TTL/逐出和缓存故障 | 一张 10 次读的成本/新鲜度表与降级决定 |
二、权威、派生、临时是业务角色,不是某个产品的标签
权威状态是争议时用来裁决业务事实的来源。在未来教学 S3 中,messages 中已提交的 m-9 和 members.left_at 的当前资格是权威;06.12 额外提出的 conversation_counters.next_seq 是分配会话序号的受控事务状态,不能因为名字叫 counter 就当成可清空的临时计数。若要从消息行重建 next_seq,也必须先停止/围住写入、核对所有消息与预留策略,再按合同重建;不能重启缓存时直接把它归零。06.01 四表 · 06.12 教学计数行
派生状态能由明确的权威输入和规则重新计算,例如 c-a 会话列表预览“最近已提交消息 m-9、seq9”。但“可以重建”有前提:消息历史、编辑/撤回版本、会话成员可见范围和摘要规则必须完整且有可用的回放/查询路径。若预览存了唯一一份未保存的人工标注,它就不再完全是派生值。临时状态服务当前短期判断,如连接/心跳产生的近似在线提示,或允许重置的速率计数;它们丢了以后未必能从消息历史推回,必须有重新观察或保守降级办法。
| 状态 | 本题角色 | 丢失后正确动作 | 允许多旧? |
|---|---|---|---|
| S2 进程内受理的消息 | 当前 S2 受理边界,易失 | 不得冒充有 S3 持久来源;按 S2 合同报告未知/可能丢失 | 进程重启后无持久保证 |
未来 S3 已提交 messages | 权威消息事实 | 按数据库恢复/备份合同恢复,不能靠预览缓存补造 | 按持久版合同,不用旧缓存替代 |
members.left_at | 当前权限权威 | 查权威/受控版本,退群后旧许可不能放行 | 写入资格不能用过期旧值 |
conversation_counters.next_seq | 教学事务序号权威扩展 | 受控恢复和约束核对,不能任意重置 | 发送分配必须受控 |
| 会话预览/可重算未读提示 | 派生视图 | 由消息、读水位与可见规则重建并对账 | 产品须定义陈旧窗口 |
| 当前连接/心跳在线提示 | 临时近似状态 | 进入未知或等待新心跳/连接重新确认 | 可短暂不准,但须定义口径 |
| 临时速率计数 | 条件性临时状态 | 按 fail-open/fail-closed 等已审风险政策处理 | 窗口与丢失政策须明确 |
这张表按本题合同分类,不是说 Redis 天生只能放临时数据、数据库天生总权威。若某产品把唯一的业务事实放在 Redis,便须按权威数据的持久、复制、恢复和确认要求来设计,不能套“缓存可随时逐出”。Redis 官方也把典型缓存视为持久数据的可再生成副本,按内存上限可能逐出键;正因如此,读者必须先认角色再决定是否允许逐出。Redis Key Eviction
三、访问模式先于数据结构:谁按什么键、多久读写一次?
缓存设计应从操作出发,而非先挑 String、Hash 或 Sorted Set。对 IM 需求逐项列查询键、返回大小、读写/失效频率、热点与正确性条件:
| 业务操作 | 访问形状 | 写入/失效原因 | 首先要问的边界 |
|---|---|---|---|
| 历史更旧三条 | (conversation_id,seq) 范围,倒序游标 | 新消息、编辑、撤回、权限变化 | 缓存的是哪一页、哪个版本和哪位读者可见 |
| 单条消息核对 | 稳定 message_id 精确查 | 提交/撤回/删除 | 查不到是“不存在”还是缓存没这键/副本落后 |
| A 的会话列表预览 | 按用户会话集合取多条摘要/排序 | 新消息、退群、已读推进 | 可陈旧多久、能否重建与批量回源成本 |
| 在线设备提示 | (user,device) 或连接身份查询 | 建连、断开、心跳过期 | “在线”定义是最近心跳还是设备已可接收 |
| 临时限流 | 主体/会话与时间窗口计数 | 窗口推进或过期 | 重置后是否放宽风险、是否要持久配额 |
会话 next_seq | 每次写消息锁同一会话行 | 成功提交/事务回滚 | 是否权威、争用如何测量 |
例如 c-g 大群会话的预览可能被大量成员重复读取,出现热点;c-a 老历史可能呈现连续范围局部性。热点是请求分布在少数键上高度集中,局部性是近期访问更可能再次访问相同/相邻数据。二者只提示可研究缓存,不证明某个具体 Redis 结构或 TTL 必有收益。还需记录对象大小、活跃工作集、更新率、跨副本读一致性与权限成本;07.02 才按访问形状选择数据类型。
四、十次预览读取:命中率 70% 仍可能让用户看到旧消息
纸上假设 c-a 会话预览在十次独立读取中有 7 次键命中、3 次键未命中,则命中率是 7/(7+3)=70%。再假设最简单的“每次未命中就查权威并回填”、没有并发、没有其他授权查询,那么这十次预览按该模型是 3 次预览回源。真实每个请求可能还需查权限、多个键或遭遇并发 miss,所以不能把 3 当作整条接口的数据库总请求数。Redis INFO 的 keyspace hit/miss 口径
更重要的反例:数据库已经有 m-9/seq9,缓存里仍保存“最新 seq8”;第八次读取命中缓存键并返回旧预览。对 Redis 的 keyspace 统计它可能是 hit,对用户“刚发的消息应在会话列表里出现”的承诺却可能是不合格旧读。命中不等于新鲜,不等于有权,不等于正确。 若 7 次命中里有 2 次这种旧值,仅报 70% 命中率会掩盖体验错误。需要把陈旧比例、延迟、回源量与具体业务合同一起看,不能以“命中率高”替代一致性评审。08.04 一致性模型
缓存 miss 也不等于数据库里没有消息:可能是键从未加载、TTL 到期、内存逐出、缓存服务重启或读错命名空间。只有按受控权限访问权威源并核对版本后,才能回答业务上的“消息不存在”。这个区分会在 07.03 的负缓存与迟到回填中变得更重要。
五、四种“键没了”:TTL、主动失效、逐出、故障
TTL 过期是键的设定生存时间到期;主动失效是应用因源数据变化显式删除/更新旧副本;逐出是缓存受内存政策等条件淘汰键;故障丢失是缓存进程/节点出问题后状态不可用或重建。这四种原因都可能在应用层出现“没有缓存值”,恢复动作与风险却不同。Redis EXPIRE 提供键过期机制,maxmemory 与策略决定内存压力下是否逐出;本章只用官方文档核对这些机制,不把任一具体配置冒充本项目真实部署。Redis EXPIRE · Redis Key Eviction
TTL 只是键的生命周期边界,不是“在 TTL 内始终新鲜”的证明。数据库在缓存写入一毫秒后就更新,若没有对应失效或版本校验,缓存可在剩余 TTL 中一直旧。反过来,TTL 很短虽能缩短某些陈旧窗口,却可能让更多请求同时回源;内存逐出也与 TTL 到期无关。07.03 会把 cache-aside 的读/写竞争画成具体时间线,07.04 才处理集中过期和热点冲击。
| 缓存观察 | 可下的局部结论 | 还不能下的业务结论 |
|---|---|---|
| hit,值为 seq8 | 键存在、读到值 | 权威最新仍是 8;当前 A 有权看 |
| miss | 此次缓存无可用值 | 权威没有 m-9 |
| TTL 到期 | 当前缓存值不再按 TTL 返回 | 数据库已更新或必须返回 404 |
| eviction 增加 | 缓存按政策淘汰了键 | 用户数据已丢或数据库也没了 |
六、在线状态和权限缓存:错误类别比延迟更要紧
在线提示常由连接或近期心跳估算:设备刚连上也可能立即断开,最后一次心跳不表示用户正看着屏幕,更不等于消息已到达设备应用。若承载在线状态的缓存重启,服务暂时没有键,正确选项是按产品合同显示未知、等待新心跳/重连或保守降级;直接把所有用户标成“已离线”可能造成误导。若业务还要显示可审计 last_seen,那条历史事实须另有权威保存,不能靠易失 presence 推断过去。
权限更敏感。u-a 退群后,members.left_at 的权威值已变化;旧缓存若仍说“活跃”,用它放行发送可能违反 06.07–06.08 的受控先后,读私有历史还可能泄露正文。缓存成员列表可用于候选展示或预取,但真正授权须按设计取得足够新的权威/版本证据,并处理失效、复制滞后与请求并发。把“查缓存更快”当作安全性理由没有意义。07.03 再细化版本与失效策略,09.07 的对象级授权规则在此仍适用。06.08 锁与 MVCC · 09.07 认证与授权
临时速率计数同样要先写风险选择:缓存丢失后计数重置,可能让攻击者短时多发;若合同允许短暂 fail-open,应标注影响与后续监控;若必须 fail-closed,则缓存不可用时拒绝相应请求并接受可用性代价。精确业务配额或 conversation_counters.next_seq 不能被一句“限流计数也是 counter”归成可丢临时状态。
七、假设缓存重启:五种状态该怎样恢复?
固定未来教学 S3 情景,缓存节点重启、其中键全部不可用,而权威数据库仍可读。逐行做失效演练;再把第一行换回 S2,提醒两个阶段不能混淆:
| 状态 | S3 缓存重启后 | 恢复来源与用户可见处理 |
|---|---|---|
messages 历史 | 数据库已提交事实不因缓存重启消失 | 权威查询/索引读取;缓存页可重建,但须授权 |
| 会话预览 | 旧副本丢失,列表可能变慢/暂缺摘要 | 从权威消息及完整规则重算;核对版本、允许陈旧窗 |
| 在线 presence | 连接/心跳索引可能暂缺 | 标未知,依新连接/心跳恢复;不要从历史造“在线” |
| 临时限流计数 | 窗口可能归零 | 按已审 fail-open/fail-closed 风险政策处理 |
members 权限与 next_seq | 不应只存在可丢缓存 | 查权威并保持事务/约束,必要时暂停受影响写入 |
若仍在当前 S2,消息本来只被进程内存受理,进程重启可能连消息本身都失去;不能因为未来 S3 表格里写“数据库仍在”就宣布 S2 数据已安全。这也是为什么同一个 Redis/缓存故障表必须附上系统阶段和权威定义。恢复后,A 的首页若从权威读到 seq9、预览缓存尚未重建为 seq8,要按 08.04 的业务承诺决定是否暂时返回旧预览或等待修复;不能在不同视图间无说明地互相矛盾。
八、先交状态角色表,再学 Redis 命令与结构
交付一份“状态→权威/派生/临时→访问键→允许陈旧→故障恢复来源→权限边界”的矩阵,以及十次读的 7 hit/3 miss、旧预览 seq8 的反例和缓存重启表。每个候选缓存都先有可核对的损失政策,再问 07.02 该用哪种数据类型。没有真实负载、容量和故障记录时,本章不声称 Redis 一定加速或某个 TTL 是最优值。
分层练习与反馈
1–8 先辨角色,9–16 推演命中、陈旧和失效,17–22 评审权限、恢复与选型。先答,再展开反馈。
1. 当前 S2 的 `accepted_in_memory` 等于数据库历史权威已持久吗?
不等于。它只确认当前进程受理,进程故障后可能丢失。
2. 未来 S3 已提交消息在本题属于哪类状态?
权威消息事实,具体可恢复范围仍依数据库提交与故障合同。
3. 会话预览是派生状态,需要哪些重建输入?
至少权威消息及编辑/撤回、会话可见规则;缺少唯一输入的标注不能凭空重建。
4. `members.left_at` 的过期缓存能独立裁决退群者可否发送吗?
不能。要按受控权威/版本边界判断,旧许可不得放行。
5. `conversation_counters.next_seq` 只因名字叫 counter 就可随意清空吗?
不能。它是 06.12 的事务序号权威扩展,恢复须有受控写入和约束核对。
6. 在线键没了就证明用户真的离线吗?
不证明。可能是 TTL、逐出或缓存故障;当前状态应按合同未知/重建。
7. Redis 实例是否天生只能存派生和临时数据?
不是。角色取决于业务合同;若它承载唯一权威事实,就须设计相应持久与恢复,不可按普通可丢缓存逐出。
8. 历史范围查询的基本键形状是什么?
先以 conversation_id 限定会话,再以 seq 范围/游标按业务顺序取页。
9. 十次预览读 7 hit、3 miss,命中率是多少?
7/(7+3)=70%。
10. 在最简单无并发 cache-aside 假设下,三次 miss 对应多少次预览回源?
在“每次 miss 查一次权威”的本题假设下是 3 次预览回源;不包含额外授权、其他键和并发工作。
11. 命中缓存 seq8,权威已到 seq9,hit 证明正确吗?
不证明。键存在但值旧,若合同要求刚发后预览含 9,这个 hit 不合格。
12. 缓存 miss 可直接返回“消息不存在”吗?
不能。需要按权限查询权威源,并区分负缓存、落后副本和依赖失败。
13. TTL 还剩 30 秒,能保证这 30 秒内值一直最新吗?
不能。源可能立即变化,若未失效/校验,缓存可一直旧到 TTL 到期。
14. TTL 到期、主动失效和内存逐出是一回事吗?
不是。分别是生命周期结束、应用响应源变化、容量政策淘汰,需记录不同原因。
15. `keyspace_hits` 高就证明权限安全、用户满意吗?
不能。hit 可能旧或未经授权,还要看陈旧率、业务结果、延迟和错误。
16. 缓存重启后在线键缺失,如何避免谎报?
先按产品合同标未知或保守降级,再用新连接/心跳重新建立提示。
17. 临时限流计数故障重置,为什么也要评审?
短时可能放宽速率限制;应明确 fail-open/fail-closed、影响范围与监控。
18. 精确业务配额可和易失速率计数共用“可丢”政策吗?
不可。精确配额若决定受理/收费等权威事实,丢失与重置有业务后果,须另设持久边界。
19. S3 缓存重启后消息历史一定随着缓存一起消失吗?
在本题 S3 数据库仍可读假设下不会;缓存页可从权威重建。
20. 同一问题换成当前 S2 进程崩溃,能套第 19 题答案吗?
不能。S2 消息仅进程内存受理,尚无数据库持久承诺。
21. 成员已退群、缓存仍显示活跃,最快返回旧值可接受吗?
不可据此放行发送或泄露私有历史。权限必须走足够新的受控权威/版本证据。
22. 选择 Redis 结构前,至少写出哪四项访问证据?
例如访问键/范围、读写与失效频率、对象/工作集大小、热点分布;还需损失与陈旧政策。
本章完成标准与下一步
不看答案时,能把消息、成员资格、next_seq、预览、在线提示和临时限流逐一分类,并说清缓存重启后的恢复来源;还能手算 70% 命中且解释为何旧 seq8 的命中不合格,才算完成第一轮。第二轮由学习者在隔离环境测真实访问分布、陈旧与故障结果,本章没有代替运行。
按学习路线,下一章07.02 Redis 数据模型才选择 Redis 字符串、哈希、集合、有序集合与流等结构;07.03 再把读取、更新和失效的竞争画成时间线。