在分布式数据库领域,Cassandra 一直以极高的写入吞吐量(AP 系统的典范)著称。然而,一旦业务场景要求强一致性(Linearizability),比如余额扣减、唯一性约束,大家的第一反应往往是使用 Cassandra 的轻量级事务(LWT,Lightweight Transactions)。
但用过 LWT 的人大多会痛苦不堪。因为 Cassandra 的 LWT 基于 Paxos 协议实现,在 4.0 之前需要 4 个 RTT(往返时延),即便优化后也至少需要 2 个 RTT。在高并发下,频繁的 Paxos 冲突会导致严重的性能抖动。
那么,在 Cassandra 中,有没有办法不走慢腾腾的 Paxos,同时实现高性能的“无锁”强一致性写入?
答案是肯定的,但我们需要根据具体的业务场景,采用不同的架构设计和最新的技术栈。
误区澄清:单纯的 $R + W > N$ 无法解决并发写冲突
很多开发者认为,Cassandra 支持配置读写一致性级别(Consistency Level)。只要满足:
$$\text{Read Level} + \text{Write Level} > \text{Replication Factor}$$
(例如 $QUORUM + QUORUM > 3$),就能实现强一致性。
这是一个经典的认知误区。
这个公式只能保证读写强一致性(Read-Your-Writes Consistency),即“写完一定能读到最新数据”。但在并发写场景下,它无法保证顺序。
Cassandra 默认采用 LWW(Last-Write-Wins,最后写入者胜) 策略,依赖客户端或协调节点的时间戳来解决冲突。因为分布式环境下存在时钟漂移,如果两个客户端同时修改同一行数据,Cassandra 会根据时钟大小覆盖掉其中一个,这会导致:
- 脏写(Dirty Write):先发起的请求因为时钟快,覆盖了后发起的请求。
- 无法实现 CAS(Compare-And-Swap):无法基于“当前值为 A 时才更新为 B”的条件进行安全写入。
因此,要实现无锁的强一致性写入,我们必须引入更高级的机制。
方案一:终极解法 —— 拥抱 Cassandra 5.0 的 Accord 协议
如果你正在规划未来的技术栈,或者可以使用 Cassandra 5.0(及以上版本),那么你将迎来彻底干掉 Paxos 的终极武器:Accord 协议。
什么是 Accord 协议?
Accord 是 Cassandra 社区为了彻底解决 Paxos 事务性能差而引入的新一代共识协议。它是世界上第一个实现**单 RTT(1 Round-Trip)**的全局通用共识协议,能够提供全分区的 ACID 事务支持。
【传统 Paxos LWT】
Client -> Coordinator -> [Prepare/Promise] -> [Propose/Accept] -> [Commit] -> Success (2~4 RTTs)
【Accord 协议】
Client -> Coordinator -> [Fast Path Consensus & Execution] -> Success (1 RTT)
为什么 Accord 能做到“无锁”且高性能?
- 无 Leader 架构(Leaderless):不像 Raft 必须经过 Leader 节点,Accord 任意节点都可以发起事务,避免了单点瓶颈。
- 依赖图分析(Dependency Graph):Accord 不去强行对所有事务进行全局排序(这非常慢),而是通过分析事务之间的读写冲突,建立依赖图。对于不冲突的事务,并发执行;对于冲突的事务,通过一轮 RTT(Fast Path)快速达成共识并解决依赖。
- 单 RTT 完成提交:在没有严重冲突的情况下,Accord 只需要一次往返即可完成事务的共识与执行,性能逼近普通的非事务写入。
实践建议:
这是 Cassandra 官方推荐的未来方向。如果你需要跨行、跨表的强一致性事务,且对延迟要求极高,升级到 Cassandra 5.0 并启用基于 Accord 的 TRANSACTION 语法是最佳选择。
方案二:应用层协同 —— 基于 Redis/etcd 侧路分布式锁
如果你的 Cassandra 还停留在 3.x 或 4.x 版本,且无法承受 Paxos LWT 的延迟,最成熟的工程实践是将一致性协调与数据存储解耦。
既然 Cassandra 本身不擅长做高并发下的复杂共识,那就把“锁”的部分交给更擅长的组件(如 Redis 或 etcd),而 Cassandra 只负责无锁的高速写入。
架构设计:
+------------------+
| API Client |
+------------------+
/ \
1. 获取分布式锁 2. 极速写入 (Consistency Level: LOCAL_QUORUM)
v v
+-------------+ +-------------+
| Redis/etcd | | Cassandra |
+-------------+ +-------------+
执行步骤:
- 加锁:客户端在写入 Cassandra 前,先去 Redis(使用 Redisson)或 etcd 申请针对该行主键的分布式锁。
- 执行:获取锁成功后,客户端直接向 Cassandra 发起普通的
INSERT/UPDATE写入(使用LOCAL_QUORUM保证副本同步,不要带IF EXISTS等 LWT 语法)。 - 释放锁:写入完成后,释放锁。
为什么这个方案性能更好?
- Redis 的全内存操作和 etcd 的高效 Raft 实现,处理锁竞争的吞吐量远高于 Cassandra 的磁盘/网络混杂的 Paxos。
- Cassandra 端执行的是纯粹的无锁写入,延迟通常在几个毫秒以内,极大地释放了 Cassandra 的吞吐潜力。
避坑指南:
该方案需要处理锁超时和异地多活的问题。如果业务是跨地域多活部署,Redis 锁的同步延迟可能会抵消其性能优势。
方案三:数据结构化繁为简 —— 使用 CRDT(无冲突复制数据类型)
很多时候,我们追求“强一致性写入”,只是为了解决“并发计数”或“集合求并集”时的数据丢失问题。
例如:点赞数累加。如果用 Read-Modify-Write,必须加锁。
但在 Cassandra 中,我们可以利用 CRDT(Conflict-Free Replicated Data Types) 来实现数学意义上的强一致性,而完全不需要任何锁和共识协议。
Cassandra 内置支持两种典型的 CRDT 思想:
- Counter 列(计数器):
Cassandra 的 Counter 列在底层是自愈的,并发写入时不会因为时序问题丢失增量,它通过特定的合并算法保证所有副本最终对齐。UPDATE user_stats SET likes = likes + 1 WHERE user_id = 'A'; - 集合类型(List, Set, Map)的追加/删除:
使用SET = SET + {'new_item'}时,Cassandra 实际上是以类似日志追加的方式处理的,不同节点的并发追加最终会合并成一个完整的集合。
为什么这能算“强一致性”?
虽然它在物理上是最终一致的(Eventually Consistent),但由于其操作满足结合律、交换律和幂等律(ACI Properties),无论网络丢包、乱序还是延迟,最终所有节点计算出的结果都是绝对一致的。在业务层面上,这达到了与强一致性相同的效果,且写入性能与普通写入完全一致(0 附加延迟)。
方案四:乐观锁变种 —— 使用具有确定性时间戳的 MVCC
如果你无法引入外部锁,又必须在现有 Cassandra 版本中实现类似 CAS(检查再更新)的操作,可以尝试应用端控制时间戳。
Cassandra 允许客户端在写入时显式指定 USING TIMESTAMP:
UPDATE orders USING TIMESTAMP 1680000000000
SET status = 'PAID'
WHERE order_id = '123';
设计思路:
- 版本号机制:在表中引入一个
version字段。 - 读取:读取当前数据,获取当前
version值为 $V$,获取当前系统高精度时间戳 $T_1$。 - 写入:更新数据,同时设置
version = V + 1,并在 SQL 后面加上USING TIMESTAMP T_1。 - 冲突检测:如果另一个并发请求使用了更旧的 $T_0$(但在你之后到达),Cassandra 会自动根据 LWW 规则丢弃旧的写入。
注:此方案属于乐观锁的变种,不能完全替代标准的 CAS,因为无法在数据库端原子性地返回“更新失败”。它更适合对写入冲突率较低、但对最终时序有严格要求的场景。
总结:如何选择?
在 Cassandra 中实现高性能写入,关键在于摆脱传统的关系型数据库“行锁”思维。
| 解决方案 | 强一致性级别 | 性能表现 | 适用场景 | 实施成本 |
|---|---|---|---|---|
| Accord 协议 (5.0+) | 严格线性一致 (ACID) | 极高 (1 RTT) | 跨行跨表分布式事务、替代 Paxos LWT | 中(需升级 Cassandra) |
| Redis/etcd 分布式锁 | 强一致(视锁实现而定) | 高(依赖内存锁) | 存量 Cassandra 版本的强一致控制 | 高(需维护额外组件) |
| 内置 CRDT (Counter/Set) | 收敛一致性 (业务等价强一致) | 极高 (与普通写无异) | 计数器、状态累加、日志追加 | 极低 (内置支持) |
| 客户端控制 Timestamp | 确定性最终一致 | 极高 | 严格依赖操作时序的覆盖写入 | 低 |
黄金法则:
- 能用 CRDT 解决的业务场景(如计数、日志),永远不要用锁和事务。
- 追求极致事务体验,首选 Cassandra 5.0 Accord。
- 遗留系统且并发冲突极高,采用 Redis 侧路锁 保护 Cassandra 写入路径。