NoSQL的最终一致性:真的可靠吗?

在学习数据库的时候,我们经常会接触到两个概念:

  • 关系型数据库强调 ACID

  • NoSQL / 分布式数据库经常强调 BASE

很多人第一次看到 BASE 时,会产生一个疑问:

如果 NoSQL 不保证强一致性,而只是保证"最终一致性",那数据岂不是可能不一致?这样的数据库还能用吗?

答案当然是:能用,而且很多互联网系统恰恰非常依赖这种设计。

关键在于,BASE 并不是"不要一致性",而是在分布式、高并发场景下,对一致性、可用性和性能进行权衡。


一、先简单回顾一下 ACID

传统关系型数据库中的事务,一般强调 ACID:

  • Atomicity(原子性)

  • Consistency(一致性)

  • Isolation(隔离性)

  • Durability(持久性)

例如用户进行一次银行转账:

复制代码
A 账户扣除 100 元
B 账户增加 100 元

这两步应该作为一个整体执行。

要么全部成功:

复制代码
A:1000 → 900
B:500  → 600

要么全部失败。

不能出现:

复制代码
A:1000 → 900
B:仍然是 500

否则钱就"消失"了。

因此,在银行余额、支付、账务等场景中,强一致性通常非常重要。


二、为什么分布式系统不能一直追求强一致性?

假设我们的系统只有一台数据库服务器:

复制代码
用户
 ↓
数据库

所有数据都在一个地方,一致性相对容易保证。

但随着业务规模越来越大,单机数据库撑不住之后,系统可能变成:

复制代码
           ┌── Node A
用户 ──────┼── Node B
           └── Node C

数据会分布在多个节点上。

这时问题就出现了。

假设 Node A 更新了一条数据:

复制代码
库存:100 → 99

然后需要同步给 Node B 和 Node C。

但是网络通信是有延迟的。

于是短时间内可能出现:

复制代码
Node A:99
Node B:100
Node C:100

如果我们要求:

所有节点必须在同一时刻看到完全一样的数据

那么系统就必须等待所有节点完成同步以后,才能返回请求。

这样虽然一致性很强,但会带来几个问题:

  • 请求延迟增加

  • 某个节点故障可能拖慢整个系统

  • 网络抖动时系统可用性下降

  • 高并发场景吞吐量下降

因此很多分布式系统会选择:

允许短时间不一致,但保证最终会一致。

这就是 BASE 思想产生的背景。


三、什么是 BASE?

BASE 主要包含三个部分:

复制代码
Basically Available
Soft State
Eventually Consistent

也就是:

1. Basically Available:基本可用

意思是:

即使系统发生部分故障,也尽可能继续提供服务。

比如一个电商系统有 10 台数据库节点,其中 1 台发生故障。

系统不应该因为这一台服务器挂掉,就导致整个网站无法访问。

可以通过:

复制代码
节点 A ❌

节点 B ✅
节点 C ✅
节点 D ✅

让其他节点继续处理请求。

因此 BASE 更强调:

系统先活着。


四、Soft State:软状态

Soft State 可以理解成:

系统允许数据暂时处于一个中间状态。

例如商品库存原本是:

复制代码
库存 = 100

用户购买一件商品之后,Node A 已经更新为:

复制代码
Node A:99

但 Node B 还没同步:

复制代码
Node B:100

那么此时系统暂时存在:

复制代码
Node A:99
Node B:100

这就是一种"软状态"。

数据暂时不完全一致。

但这种状态只存在于数据同步的过程中。


五、Eventually Consistent:最终一致性

最终一致性是 BASE 最重要的一个概念。

意思是:

系统允许短时间不一致,但经过一段时间以后,所有节点最终会达到一致状态。

例如:

复制代码
T1 时刻

Node A:99
Node B:100
Node C:100

经过数据同步:

复制代码
T2 时刻

Node A:99
Node B:99
Node C:99

最终三个节点的数据一致。

这就是:

复制代码
短时间不一致
      ↓
数据传播 / 同步
      ↓
最终一致

六、那 NoSQL 数据库岂不是"不可靠"?

这里很容易产生一个误区:

最终一致性 = 数据不可靠

实际上并不是。

最终一致性只是说:

数据不一定"瞬间一致"。

而不是说:

数据最终会错。

比如微博点赞。

某篇文章真实点赞数是:

复制代码
10001

由于不同节点同步存在几十毫秒的延迟:

用户 A 可能看到:

复制代码
10000

用户 B 可能看到:

复制代码
10001

这个问题严重吗?

通常并不严重。

几十毫秒之后:

复制代码
用户 A:10001
用户 B:10001

最终所有人看到的数据都会一致。

但如果为了让点赞数绝对实时一致,要求所有点赞请求必须同步多个节点以后才能返回:

复制代码
用户点赞
   ↓
Node A 写入
   ↓
等待 Node B
   ↓
等待 Node C
   ↓
全部完成
   ↓
返回成功

那么点赞这种高并发操作的性能会大幅下降。

这显然不划算。


七、哪些业务适合 BASE?

很多互联网业务其实并不要求强一致性。

例如:

点赞数

复制代码
1000 → 1001

稍微晚一点同步影响不大。

浏览量

文章浏览量:

复制代码
PV = 100000

变成:

复制代码
PV = 100001

晚几十毫秒甚至几秒更新通常也没有问题。

Feed 流

比如微博、朋友圈、社区推荐:

复制代码
用户 A 发了一条动态

用户 B 晚 1 秒看到,一般可以接受。

搜索索引

例如商品更新:

复制代码
价格:100 → 90

数据库已经更新,但 Elasticsearch 可能晚几百毫秒同步。

很多系统也能接受这种短暂的不一致。

缓存

经典结构:

复制代码
Redis
  ↓
MySQL

Redis 和 MySQL 在某个极短时间窗口内,也可能存在数据不一致。

通常通过:

  • 缓存删除

  • 延迟双删

  • TTL

  • MQ 同步

  • 重试机制

等方式保证最终一致性。


八、哪些业务不能轻易使用最终一致性?

有些业务对数据一致性非常敏感。

例如银行转账:

复制代码
A → B 转账 100 元

不能允许:

复制代码
A 已经扣款
B 很久以后才到账

更不能出现账务永久错误。

类似场景还有:

  • 银行账户余额

  • 支付系统

  • 清结算系统

  • 核心账务

  • 某些严格库存系统

这些场景通常需要更强的事务和一致性保证。

因此实际系统中经常会:

复制代码
核心数据 → 强一致

非核心数据 → 最终一致

而不是所有业务都使用同一种一致性策略。


九、BASE 和 CAP 有什么关系?

BASE 经常和 CAP 一起出现。

CAP 指的是分布式系统中的三个性质:

复制代码
C:Consistency       一致性
A:Availability      可用性
P:Partition Tolerance 分区容错

在发生网络分区的情况下,系统通常无法同时完美保证:

复制代码
Consistency
+
Availability

因为分布式系统基本无法避免网络问题,所以 P 通常必须保证。

系统就需要在:

复制代码
CP

或者

AP

之间进行权衡。

很多强调 BASE 的系统,会更倾向于:

复制代码
AP

也就是:

在发生网络问题时,优先保证系统还能提供服务,然后通过异步同步让数据最终一致。

这也是 BASE 的核心思想之一。


十、NoSQL 一定只能提供最终一致性吗?

并不是。

这是一个很常见的误区:

复制代码
SQL = ACID
NoSQL = BASE

实际上现代数据库并没有这么绝对。

很多 NoSQL 数据库都允许用户在:

复制代码
一致性
性能
可用性

之间进行调整。

例如 Cassandra 可以通过配置:

复制代码
ONE
QUORUM
ALL

决定一个请求需要多少副本确认。

MongoDB 也可以通过:

复制代码
Read Concern
Write Concern

调整读写的一致性级别。

Redis 也可以通过:

  • 主从复制

  • WAIT

  • Sentinel

  • Cluster

等机制提高数据可靠性。

因此更准确的说法应该是:

NoSQL 通常更强调可扩展性和高可用性,在某些场景下愿意使用最终一致性换取性能。

而不是:

NoSQL 不保证数据正确。


十一、一个实际的电商例子

假设一个秒杀系统:

复制代码
用户下单
   ↓
扣库存
   ↓
创建订单
   ↓
写数据库

如果每一步都要求跨数据库强事务:

复制代码
库存数据库
     ↕
分布式事务
     ↕
订单数据库

可能需要使用:

  • 2PC

  • TCC

  • XA

这些方案虽然能够提供较强的一致性,但实现复杂,而且高并发下性能压力较大。

另一种方案是:

复制代码
用户请求
   ↓
Redis 原子扣库存
   ↓
Redis Stream / MQ
   ↓
立即返回
   ↓
消费者异步创建订单
   ↓
失败重试
   ↓
补偿

这种设计允许:

复制代码
某个短暂时刻:

Redis:库存已经减少

MySQL:订单还没落库

也就是说:

复制代码
暂时不一致

但是消费者最终会处理消息:

复制代码
Redis
   ↓
Stream / MQ
   ↓
订单数据库

最终变成:

复制代码
库存正确
订单正确

这就是一个非常典型的 BASE / 最终一致性思想。


十二、总结

ACID 和 BASE 并不是谁先进、谁落后。

它们解决的是不同场景下的问题。

可以简单理解成:

复制代码
ACID:

我要数据现在就一致
即使性能低一些也可以

BASE:

我要系统高可用、高吞吐
允许数据短时间不一致
但最终必须一致

因此:

场景 更适合的思想
银行转账 ACID / 强一致
支付账务 ACID / 强一致
点赞 BASE / 最终一致
浏览量 BASE / 最终一致
Feed 流 BASE / 最终一致
搜索索引 BASE / 最终一致
秒杀异步落库 BASE / 最终一致

所以对于:

"NoSQL 使用 BASE,那 NoSQL 还能用吗?"

答案是:

当然可以。

BASE 并不是放弃一致性,而是接受:

在大型分布式系统中,没有必要让所有数据在任何时刻都保持绝对强一致。

对于大量互联网业务而言,使用"短暂不一致 + 最终一致"的方案,反而可以获得更好的:

  • 可用性

  • 扩展性

  • 性能

  • 容错能力

这也是 NoSQL 和分布式数据库能够支撑大规模互联网业务的重要原因。

相关推荐
敲代码的嘎仔1 小时前
用 Redis 合并写 + DelayQueue 延迟检测,把视频播放进度写库频率降到 1/60
java·开发语言·数据库·redis·缓存·音视频·高并发
承渊政道2 小时前
PostgreSQL 鸿蒙 PC 适配全记录:从原生交叉编译到 HNP 数据库服务闭环
数据库·postgresql·harmonyos·鸿蒙系统·pc端
风哥2号2 小时前
数据库教程FGMT26‑2-GoldenGate数据库容灾迁移02(OGG同构异构、数据库迁移、数据同步、容灾复制)
数据库·goldengate
牛油果子哥q2 小时前
向量数据库原理与工程选型:FAISS深度剖析、Milvus基础、检索优化、分片与持久化落地
数据库·milvus·faiss
海浪仙人掌2 小时前
流动比率有哪些分析陷阱?流动比率怎么避开这些陷阱?
大数据·数据库·人工智能
庆登登登3 小时前
MySQL Workbench 鸿蒙 PC 适配全记录:从 GTK_X11 桌面程序到 ArkUI 原生数据库工作台
数据库·mysql·harmonyos
2601_963282773 小时前
政企无线对讲系统落地:从设备采购到长期运维的完整工程实践
大数据·运维·数据库
熊文豪3 小时前
OceanBaseVS金仓:一条 SQL 的两条路——KingbaseES 的性能竞争力从哪来
数据库·sql·电科金仓
大模型码小白3 小时前
数据可视化:AI 生成 HTML5 动态交互式数据图表
前端·数据库·人工智能·深度学习·机器学习·信息可视化·html5