系统进入只读模式适合什么场景?
简化版
只读模式适用于写入无法安全完成、但已有数据仍可可信读取的场景,例如主库失去多数派、磁盘逼近满载、跨地域写权不明确或高风险迁移期间。 它要在统一写入口强制执行,明确哪些“看似读取”会产生副作用,并向用户返回可识别的只读状态;不能仅隐藏前端按钮。
详细版
进入条件来自数据安全而非普通流量高峰:无法确认唯一写主、WAL 空间不足、Schema 迁移保护或人工应急。所有 API、后台任务、MQ Consumer、定时任务和管理工具都要遵守同一 epoch/开关,避免旁路写。
读取也要说明一致性与陈旧度;依赖写入的登录续期、已读标记、库存锁定等应禁用或转入经设计的可靠待办。退出前确认写主、复制、磁盘、积压和数据对账正常,再逐步恢复写流量。
完整版教学
一、只读模式保护的是什么
当系统不能保证写入只有一个权威结果时,继续提供写服务可能产生数据分叉或损坏。只读牺牲业务完整性,换取已有数据可访问和后续可恢复性。它是数据保护状态,不是通用的性能优化开关。
记忆钩子:宁可明确“不让改”,也不要在写权不清时让两个世界同时改。
二、适合进入只读的典型场景
| 场景 | 风险 | 只读价值 |
|---|---|---|
| 共识丢多数 | 无法安全提交 | 避免少数派写 |
| 磁盘紧急水位 | WAL/Compaction 失败 | 保留恢复空间 |
| 跨站脑裂风险 | 双主冲突 | 冻结写时间线 |
| 高风险迁移 | 新旧 Schema 不确定 | 防止状态继续变化 |
依赖短暂慢但写仍安全时,限流或排队可能更合适,不能一有异常就全站只读。
三、哪些操作其实是写
登录刷新 token、查询后更新最后访问时间、领取优惠前锁库存、消费消息提交 offset 都会改变状态。只关闭 POST/PUT 不足以构成只读。需要维护操作分类表,并在数据访问层或写权 token 上强制校验。
if request.writeEpoch != currentWritableEpoch:
reject READ_ONLY / STALE_EPOCH
前端隐藏按钮只能改善体验,后端仍必须拒绝脚本、旧客户端和内部任务。
四、只读期间的用户与任务语义
响应应使用稳定错误码、重试建议和状态页信息。对可延后操作,可以返回 requestId 并写入独立可靠介质;如果该介质也依赖故障库,就不能承诺“稍后处理”。资金与库存操作通常明确失败,不应用内存队列假装已受理。
读取若来自异步副本,要标注 dataAsOf 或最大陈旧度。只读不自动保证读到最新,甚至可能需要固定到故障前安全快照。
五、触发、传播和时限
控制面发布带 version、scope、reason、TTL 的只读状态,数据面缓存最后有效快照。最关键的存储写入口还应通过租约/epoch 自主拒绝,避免控制面消息丢失。状态传播覆盖 API、MQ、Job 和运维工具。
假设 100 个实例每 5 秒轮询,最坏传播接近 5 秒;高风险脑裂不能接受这一窗口,就必须由存储层 fencing 即时保护,而非只靠应用配置。
六、如何安全退出
先确认唯一主和复制水位,释放磁盘余量,处理冲突并校验数据;再恢复内部小流量写,随后 1%、10%、50% 放量。只读期间若积累待办,要单独限速回放,避免实时写和历史写同时冲击数据库。
监控只读命中、旁路写拒绝、状态版本覆盖率、读取陈旧度、待办积压和退出阶段。每次进入都要审计原因与持续时间,长期只读说明灾备目标未达标。
七、常见误区与追问
- 误区:前端禁用提交按钮就实现了只读。 API、任务和旧客户端仍可写。
- 误区:GET 请求一定无副作用。 某些系统会更新会话、访问时间或缓存状态。
- 误区:只读模式下所有读都可信。 副本可能陈旧或损坏,仍需一致性说明。
- 误区:故障解除后直接打开写开关。 先确认数据、复制和积压,再阶梯恢复。
- 追问:如何防内部脚本绕过? 在统一数据访问层和存储写权上校验 epoch。
- 追问:只读期间能否接收订单? 只有写入独立可靠系统并提供可查询状态时才能受理。
- 追问:控制面挂了怎么办? 数据面使用最后状态,关键安全由存储 fencing 保证。
八、加强记忆
只读模式按“安全触发—全写面封锁—读语义说明—待办有事实—数据验证后阶梯退出”设计。真正边界在后端和存储写权,不在页面按钮。它为恢复争取一致时间线,但不能把未知写伪装成排队成功。
只读演练应覆盖网页、开放 API、定时任务和运维脚本四类入口,并验证客户端收到的状态码不会触发无界重试。