会话保证:弱一致系统的实用契约
03-一致性与复制
核心
约 15 分钟
#会话保证#读己之写#单调读#路由
更新 2026-10-02
当前状态:未学
本文基于模型知识整理(生成时未联网核对),关键结论建议对照经典文献复核。
一句话定义
会话保证把一致性承诺从"全体用户全体请求"收缩到"单个用户的会话之内"——读己之写、单调读、单调写、一致前缀读——用远低于线性一致的成本,消除用户可直接感知的异常。
为什么重要
AP 系统(缓存、读从库、无主存储)默认最终一致,直接暴露给用户会产生"我刚改的头像又变回去了"这类体验事故。会话保证是以会话为界的分级承诺:用路由与元数据小成本堵住最刺眼的坑,是弱一致系统落地的关键一层。
前置知识
kp-011(复制滞后异常)、kp-013(一致性谱系位置)。
核心概念
- 读己之写(read-your-writes):会话内写后必能读到。
- 单调读(monotonic reads):会话内不会读到"比之前更旧"的值(不许时光倒流)。
- 单调写(monotonic writes):会话内写按发起顺序生效(不许乱序覆盖)。
- 一致前缀读(consistent prefix read):因果链条(先问后答)不拆散、不颠倒。
- 实现工具:会话粘性路由(sticky session)、复制位点/版本令牌(token)随会话携带、分区因果元数据(分区顺序戳)。
原理与机制
为什么按会话承诺可行:用户直接感知的异常几乎都发生在自己的操作链路内;其他用户的数据"晚几秒可见"通常无害。因此把强保证收敛到会话内,会话外维持最终一致——性价比极高。
实现机制对照:
- 读己之写:会话记录上次写的版本/时间戳,读时路由到"副本位点 ≥ 该版本"的节点(等待或换节点)。
- 单调读:会话固定路由到同一副本,或记录已读最大版本、只接受更新的版本。
- 单调写:会话内写都送同一 leader/同一分区(按用户 key 路由)。
- 一致前缀读:记录各分区的写入序号,读时等待所有相关分区的序号都覆盖已见最大值(如 Kafka 分区 offset 的会话追踪)。
代价:粘性路由损害负载均衡;等待位点增加尾延迟;会话服务器重启需恢复令牌。这是"一致性预算"在用户体验上的精确投放。
图示
会话内: 写 v3 → 主库 读请求携带 token=v3
副本A 位点 v2 ✕ → 副本B 位点 v3 ✓ → 读到 v3(读己之写)
会话外: 其他用户的旧值可见(最终一致),用户无感
实例或案例
- Cassandra 的 consistency level + Lightweight Transactions:写后读配 LOCAL_QUORUM 模拟会话保证。
- MongoDB 的 Causal Consistency Sessions:显式 session token 传递因果。
- 前端实践:修改资料后跳详情页强制走写库/主缓存并加版本参数,规避"改完看旧值"工单。
常见误区
- 误区一:"最终一致系统加个会话保证就不弱了"。会话保证只覆盖单用户链路,跨用户因果(如 B 回复 A 后 C 看到回复却看不到原帖)需要一致前缀读,往往被遗漏。
- 误区二:"粘住主库就完事"。粘性路由会在主库故障切换时失效,切换瞬间会话 token 逻辑必须能跟着新主重建。
- 误区三:"会话令牌可以不传"。保证只在令牌持续传递时成立;移动端多设备、多进程并发会话会打破"单会话"假设,需明确会话边界。
与其他知识点的关系
自测题
- 单调读失效的典型用户观感?
答:"时光倒流":第一次看到评论已有,刷新后评论消失——两次读落到滞后程度不同的副本。
- 一致前缀读解决什么跨分区问题?
答:问题与回答写入不同分区且延迟不同,导致"答案先于问题可见";追踪分区序号,读时等齐因果前缀。
- 会话保证与线性一致的本质差别?
答:前者只对单个会话的操作序列承诺可见性规则,其他会话可见性无约束;后者对全体操作给出统一实时全序。
延伸阅读
- Martin Kleppmann《DDIA》第 5 章"Problems with Replication Lag"。
- MongoDB Causal Consistency 文档(会话 token 设计实例)。
- Mahajan 等, COPS(causal+ 语义,会话保证的系统化版本)。