今天在 GitHub Trending 上看到一个经典而持续活跃的项目:Apache Cassandra,一款被无数大型互联网企业生产验证过的高度可扩展分布式 NoSQL 数据库。

一、项目概述

Apache Cassandra 由 Facebook 于 2008 年开源,2010 年成为 Apache 顶级项目,至今在 GitHub 上持续维护活跃。它是一个分区行存储数据库(Partitioned Row Store),核心设计目标包括:

  • 去中心化架构(Decentralized):无单点故障,每个节点对等
  • 线性扩展(Linear Scalability):新增节点即可线性提升吞吐
  • 多数据中心支持(Multi-Datacenter):支持跨机房、跨地域复制
  • 高可用+最终一致性:配置副本因子,实现任意节点故障不宕机

Cassandra 采用类 SQL 的查询语言 CQL(Cassandra Query Language),对熟悉关系型数据库的开发者极为友好,同时摒弃了 JOIN 和子查询,专注于极致的写入性能。

二、技术原理

2.1 分区与副本机制

Cassandra 的数据按 Partition Key 分布在集群中,每条数据根据 partitioner(默认 Murmur3)计算 hash 值,映射到环(Ring)上的节点。一致性级别(Consistency Level)控制读写需要多少副本确认:

# CQL 建表示例:定义 partition key
CREATE KEYSPACE mykeyspace
WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 3};

USE mykeyspace;
CREATE TABLE users (
    user_id varchar PRIMARY KEY,
    first varchar,
    last varchar,
    age int
);

2.2 数据复制策略

Cassandra 支持两种复制策略:

  • SimpleStrategy:单数据中心,简单哈希分配
  • NetworkTopologyStrategy:多数据中心,可为每个 DC 配置不同副本数

2.3 写路径(Write Path)

Cassandra 的写入极为高效,写操作直接追加到 MemTable(内存表),同时顺序写入 CommitLog(保证持久化),由后台定期刷盘到 SSTable(排序字符串表)。

2.4 读取路径(Read Path)

读取时,Cassandra 从 MemTable 和多个 SSTable 中合并数据,通过 Bloom Filter 快速定位可能包含目标 Partition 的 SSTable,再结合 Compaction 策略合并重复数据。

2.5 核心依赖

Cassandra 基于 Java 构建,支持 Java 17+;CQL shell(cqlsh)依赖 Python 3.8+。

# 查看支持的 Java 版本(参考 build.xml 中 java.supported)
# 查看 cqlsh 支持的 Python 版本(参考 bin/cqlsh 中 is_supported_version 函数)

三、安装与快速开始

3.1 环境要求

依赖版本要求
Java17+(推荐)
Python3.8+(用于 cqlsh)
内存推荐 8GB+

3.2 快速启动

# 1. 下载并解压
tar -zxvf apache-cassandra-$VERSION.tar.gz
cd apache-cassandra-$VERSION

# 2. 启动单节点集群(前台运行,Ctrl+C 停止)
bin/cassandra -f

# 3. 新开终端,连接 cqlsh
bin/cqlsh

连接成功后会看到类似输出:

Connected to Test Cluster at localhost:9160.
[cqlsh 6.3.0 | Cassandra 7.0-SNAPSHOT | CQL spec 3.4.8 | Native protocol v5]
cqlsh>

3.3 Docker 快速体验

docker pull cassandra:latest
docker run --name cassandra -d -p 9042:9042 cassandra:latest
docker exec -it cassandra cqlsh

四、使用方法与实战

4.1 基本 CRUD 操作

-- 创建 Keyspace
CREATE KEYSPACE myapp
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};

USE myapp;

-- 建表
CREATE TABLE orders (
    order_id varchar PRIMARY KEY,
    user_id varchar,
    amount decimal,
    status varchar,
    created_at timestamp
);

-- 写入数据
INSERT INTO orders (order_id, user_id, amount, status, created_at)
VALUES ('ord_001', 'user_123', 299.00, 'pending', toTimestamp(now()));

-- 批量写入
BEGIN BATCH
  INSERT INTO orders (order_id, user_id, amount, status) VALUES ('ord_002', 'user_456', 150.00, 'paid');
  INSERT INTO orders (order_id, user_id, amount, status) VALUES ('ord_003', 'user_789', 89.50, 'shipped');
APPLY BATCH;

-- 查询
SELECT * FROM orders WHERE order_id = 'ord_001';
SELECT * FROM orders WHERE user_id = 'user_123' ORDER BY created_at DESC;

-- 更新
UPDATE orders SET status = 'delivered' WHERE order_id = 'ord_001';

-- TTL 示例(数据 1 天后自动过期)
INSERT INTO orders (order_id, user_id, amount, status)
VALUES ('ord_tmp', 'user_999', 10.00, 'temp') USING TTL 86400;

4.2 高级特性

-- 集合类型(Set/List/Map)
CREATE TABLE user_profiles (
    user_id varchar PRIMARY KEY,
    name varchar,
    emails set<varchar>,
    phone_numbers list<varchar>,
    attributes map<varchar, varchar>
);

INSERT INTO user_profiles (user_id, name, emails, phone_numbers, attributes)
VALUES ('u001', 'Alice', {'[email protected]'}, ['13800001111'], {'city': 'Shanghai', 'level': 'gold'});

-- 计数器(Counter)
CREATE TABLE page_views (
    page_url varchar PRIMARY KEY,
    view_count counter
);

UPDATE page_views SET view_count = view_count + 1 WHERE page_url = '/blog/cassandra';

4.3 Java 客户端连接

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.*;

try (CqlSession session = CqlSession.builder()
        .withNodeContactPoints("localhost")
        .withKeyspace("myapp")
        .build()) {

    PreparedStatement ps = session.prepare(
        "INSERT INTO orders (order_id, user_id, amount, status) VALUES (?, ?, ?, ?)"
    );

    session.execute(ps.bind("ord_010", "user_200", 499.0, "paid"));
    System.out.println("写入成功");
}

五、常见问题与解决方案

Q1:启动报错 Cassandra daemon is not running? 检查 Java 版本是否满足要求(Java 17+),确认 $JAVA_HOME 已正确设置。

Q2:cqlsh 连接失败? 确认 Cassandra 进程正常运行(ps aux | grep cassandra),端口 9042 未被占用:修改 conf/cassandra.yaml 中的 native_transport_port。

Q3:数据不一致如何排查? 使用 nodetool repair 触发一致性修复;查看 system.log 定位网络分区或节点宕机原因。

Q4:如何调整副本数? 修改 keyspace 复制策略后,需要逐节点运行 nodetool rebuild 确保数据完整同步。

Q5:Compaction 策略如何选择?

  • STCS(SizeTieredCompactionStrategy):适合写入为主、读取较少的场景
  • LCS(LeveledCompactionStrategy):适合读多写少、需要稳定读取延迟的场景
  • TWCS(TimeWindowCompactionStrategy):适合 TTL 数据和时间序列数据

六、总结

Apache Cassandra 以其去中心化架构、线性扩展能力和成熟的生产实践,成为处理海量数据写入场景的首选分布式数据库之一。如果你需要构建跨数据中心、零单点故障的高可用系统,Cassandra 依然是目前最值得深入学习的开源方案之一。

官方文档:https://cassandra.apache.org/doc/latest/ Slack 社区:#cassandra(ASF Slack)