HOOOS

嫌 Cassandra 的 Paxos 慢?聊聊如何实现高性能的“无锁”强一致性写入

0 3 架构探路者 Cassandra分布式系统一致性协议
Apple

在分布式数据库领域,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 能做到“无锁”且高性能?

  1. 无 Leader 架构(Leaderless):不像 Raft 必须经过 Leader 节点,Accord 任意节点都可以发起事务,避免了单点瓶颈。
  2. 依赖图分析(Dependency Graph):Accord 不去强行对所有事务进行全局排序(这非常慢),而是通过分析事务之间的读写冲突,建立依赖图。对于不冲突的事务,并发执行;对于冲突的事务,通过一轮 RTT(Fast Path)快速达成共识并解决依赖。
  3. 单 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  |
       +-------------+    +-------------+

执行步骤:

  1. 加锁:客户端在写入 Cassandra 前,先去 Redis(使用 Redisson)或 etcd 申请针对该行主键的分布式锁。
  2. 执行:获取锁成功后,客户端直接向 Cassandra 发起普通的 INSERT/UPDATE 写入(使用 LOCAL_QUORUM 保证副本同步,不要带 IF EXISTS 等 LWT 语法)。
  3. 释放锁:写入完成后,释放锁。

为什么这个方案性能更好?

  • Redis 的全内存操作和 etcd 的高效 Raft 实现,处理锁竞争的吞吐量远高于 Cassandra 的磁盘/网络混杂的 Paxos。
  • Cassandra 端执行的是纯粹的无锁写入,延迟通常在几个毫秒以内,极大地释放了 Cassandra 的吞吐潜力。

避坑指南
该方案需要处理锁超时异地多活的问题。如果业务是跨地域多活部署,Redis 锁的同步延迟可能会抵消其性能优势。


方案三:数据结构化繁为简 —— 使用 CRDT(无冲突复制数据类型)

很多时候,我们追求“强一致性写入”,只是为了解决“并发计数”或“集合求并集”时的数据丢失问题。

例如:点赞数累加。如果用 Read-Modify-Write,必须加锁。
但在 Cassandra 中,我们可以利用 CRDT(Conflict-Free Replicated Data Types) 来实现数学意义上的强一致性,而完全不需要任何锁和共识协议

Cassandra 内置支持两种典型的 CRDT 思想:

  1. Counter 列(计数器)
    UPDATE user_stats SET likes = likes + 1 WHERE user_id = 'A';
    
    Cassandra 的 Counter 列在底层是自愈的,并发写入时不会因为时序问题丢失增量,它通过特定的合并算法保证所有副本最终对齐。
  2. 集合类型(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';

设计思路:

  1. 版本号机制:在表中引入一个 version 字段。
  2. 读取:读取当前数据,获取当前 version 值为 $V$,获取当前系统高精度时间戳 $T_1$。
  3. 写入:更新数据,同时设置 version = V + 1,并在 SQL 后面加上 USING TIMESTAMP T_1
  4. 冲突检测:如果另一个并发请求使用了更旧的 $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 写入路径。

点评评价

captcha
健康