一句话定义:BASE 是 Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致性)三个词的缩写,它描述的是一类“先把请求答应下来、再慢慢把数据对齐”的系统取向;而最终一致性是其中最关键的一条——同一份数据的多个副本,只要不再有新的写入,给够时间,最终会收敛到同一个值。
打个比方:连锁奶茶店的库存。强一致像全国只有一台收银机,谁点单都得排队问它一句“还有没有珍珠”,答不上来就不许下单;BASE 像每家门店自己先收单,店长们事后在小群里对账。顾客当场就能拿到小票(可用性没丢),但“今天还剩几杯杨枝甘露”这个数字,在一段时间里各家显示得不一样(这就是软状态),对完账就一致了。
“过一会儿”是多久? 这是最容易被含糊过去的地方。它由三件事决定:
1. 复制方式:同步复制要等副本确认才返回,异步复制则是写完就返回、日志慢慢回放。后者延迟大致等于网络往返 + 排队 + 攒批间隔。
2. 攒批策略:为了吞吐,很多系统会攒一批再传,延迟就从毫秒级变成秒级。
3. 冲突修复:最后写入者胜(last-write-wins)、向量时钟(vector clock)、CRDT 等,收敛速度和“谁赢”的语义都不同。
关键在于:最终一致性本身不承诺时间上界。工程上必须把它变成可观测的东西——盯副本延迟指标、定收敛时间目标(SLO),否则“最终”在用户眼里就等于“永远不”。
为什么读不到自己刚写的数据? 常见四种:
- 读写分离:写进主库,读被路由到从库,从库还没回放日志(复制延迟 replication lag)。
- 多地域副本:读请求落到了另一个还没同步的区域。
- 缓存:库写成功了,缓存里的旧值还没失效。
- 异步派生:数据写入后异步建索引、异步算聚合,搜索页自然搜不到。
对应的解法叫“读己之写”(read-your-writes):写完后一小段时间内强制读主库;或写入返回一个版本号/会话令牌,读的时候带上它,让副本等到这个版本;或写成功后本地先留一份。
| ACID | BASE | |
|---|---|---|
| 关注点 | 事务的正确性 | 系统在故障下仍能服务 |
| 一致性 | 强一致,事务结束即正确 | 最终一致,允许中间态 |
| 典型代价 | 冲突时拒绝或阻塞 | 冲突时先接受,事后合并 |
| 适用 | 余额、扣库存、下单 | 点赞数、动态流、日志、会话状态 |
对从业者的意义:产品侧要接受“延迟可见”的交互,按钮文案写“已提交,正在同步”比写“立刻生效”诚实得多;涉及钱和额度的路径别只靠最终一致,得配合幂等、对账和补偿。而作为使用者,当你发出的内容“刷新一下才出现”,多半不是 bug,而是系统在拿一点点延迟,换你点下去那一刻不至于转圈。具体实现细节各家不同,以官方文档为准。
