Cassandra 是 Apache 旗下的分布式宽表(Wide-Column)NoSQL 数据库,天生为海量写入与跨机房高可用而生。它采用无主(Masterless)架构与一致性哈希,节点对等、可线性扩展,单机故障不影响整体可用。本文从数据建模、一致性级别讲到三节点集群部署,帮你把 Cassandra 真正落到生产。
一、Cassandra 解决什么问题
关系型数据库擅长复杂事务与join,但分库分表后扩展艰难;而Cassandra 把”写入吞吐”和”可用性”放到第一位,用最终一致换来了轻松的水平扩展。典型场景:物联网时序、设备上报、消息轨迹、画像标签、会话存储——写多读少、按主键查询、能接受秒级最终一致。
二、核心概念:Keyspace、表与宽表建模
Cassandra 的数据模型是”嵌套的 KV”:Keyspace 类似库,Table 类似表,但每一行由 partition key 决定落在哪个节点,clustering column 决定行内排序。建模的第一原则:先想清楚查询模式,再设计主键,而不是先画 ER 图。
2.1 创建 Keyspace
CREATE KEYSPACE IF NOT EXISTS iot
WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 3
};
生产跨机房要用 NetworkTopologyStrategy 并逐数据中心指定副本数,避免 SimpleStrategy 把三副本都放同一机架。
2.2 宽表建模:用聚类列支撑时间范围查询
CREATE TABLE iot.device_metrics (
device_id text,
bucket date, -- 按天分桶,避免单分区无限膨胀
ts timestamp,
temperature float,
payload text,
PRIMARY KEY ((device_id, bucket), ts)
) WITH CLUSTERING ORDER BY (ts DESC);
上面的 (device_id, bucket) 是复合分区键,ts 是聚类列并倒序排列。这样”某设备某天的最新 N 条”就是一次高效的范围扫描,而不是全表 filter。
三、一致性级别与读写路径
Cassandra 是 AP 系统(CAP 里偏向可用性+分区容忍),一致性由一致性级别(Consistency Level)在每次请求上调节:ONE 最快但最终一致、QUORUM 平衡、ALL 最强但牺牲可用性。
-- 写时要求多数副本确认
CONSISTENCY QUORUM;
INSERT INTO iot.device_metrics (device_id, bucket, ts, temperature)
VALUES ('dev-01', '2026-09-25', toTimestamp(now()), 36.5);
-- 读时同样用 QUORUM,配合读修复
SELECT * FROM iot.device_metrics
WHERE device_id='dev-01' AND bucket='2026-09-25'
LIMIT 10;
| 一致性级别 | 所需确认副本 | 特点 |
|---|---|---|
| ONE | 1 个 | 最低延迟,可能读到旧值 |
| QUORUM | 多数((RF/2)+1) | 延迟与一致性的平衡点 |
| ALL | 全部 | 强一致,任一副本宕机即写失败 |
| LOCAL_QUORUM | 本数据中心多数 | 跨机房场景首选,省跨城延迟 |
写路径:客户端请求 → 任一节点(coordinator)→ 写入 commitlog(持久化 WAL)和 memtable(内存)→ memtable 满后刷成不可变的 SSTable。读路径:先查 memtable,再合并相关 SSTable,并用读修复(read repair)把最新值回填落后副本。理解这条链路,才能解释”为什么偶尔读到旧数据”。
四、三节点集群部署(Docker Compose)
用 Docker 编排 起一个本地三节点集群,重点在 seeds、listen_address、rpc_address 三个配置。种子节点只用于发现,不是单点,建议至少配两个。
version: "3.8"
services:
cass1:
image: cassandra:5.0
environment:
- CASSANDRA_SEEDS=cass1,cass2
- CASSANDRA_LISTEN_ADDRESS=cass1
- CASSANDRA_RPC_ADDRESS=0.0.0.0
- CASSANDRA_CLUSTER_NAME=fsdata-cluster
ports: ["9042:9042"]
cass2:
image: cassandra:5.0
environment:
- CASSANDRA_SEEDS=cass1,cass2
- CASSANDRA_LISTEN_ADDRESS=cass2
cass3:
image: cassandra:5.0
environment:
- CASSANDRA_SEEDS=cass1,cass2
- CASSANDRA_LISTEN_ADDRESS=cass3
节点启动后,用 nodetool status 查看各节点 UN(Up/Normal)状态与负责的令牌区间。生产上数据盘建议独立挂载,并参考 K8s 持久化存储 的 PV/PVC 思路做有状态存储规划。
五、数据建模实战:设备上报时序
给每行加 TTL 可让冷数据自动过期,避免手动清理:
-- 写入时设置 30 天过期
INSERT INTO iot.device_metrics (device_id, bucket, ts, temperature)
VALUES ('dev-01', '2026-09-25', toTimestamp(now()), 36.5)
USING TTL 2592000;
-- 查询某设备最近 1 小时数据
SELECT ts, temperature FROM iot.device_metrics
WHERE device_id='dev-01' AND bucket='2026-09-25'
AND ts > '2026-09-25 15:00:00+0000'
LIMIT 100;
注意:分区键决定数据分布,单分区不能无限膨胀,否则会出现”大分区”导致 GC 停顿与读延迟陡增。按天/按小时分桶(bucket)是经典解法。
六、运维:nodetool 与修复
# 集群健康总览
nodetool status
# 某表的统计:读写延迟、SSTable 数量、压实情况
nodetool cfstats iot.device_metrics
# 手动修复(运维窗口执行,推荐用 incremental repair)
nodetool repair --inc iot device_metrics
副本间可能因节点宕机产生不一致,定期 repair 是必修课;配合 GitHub Actions 把 repair 放进定时任务,比人工记得更稳。监控侧可把 ReadLatency、WriteLatency、PendingCompactions 接入 Prometheus。
七、与关系型数据库怎么选
| 维度 | Cassandra | PostgreSQL / MySQL |
|---|---|---|
| 扩展方式 | 加节点线性扩展 | 分库分表或读副本 |
| 事务 | 单行轻事务(LWT),无跨行事务 | 完整 ACID |
| 查询 | 必须走主键/二级索引 | 任意列 join、聚合 |
| 一致性 | 可调,最终一致为主 | 强一致 |
| 适用 | 写多读少、海量时序 | 强一致业务、报表 |
当你需要跨行事务、复杂 join、强一致报表时,PostgreSQL 慢查询优化 与 MySQL 主从复制 仍是更稳妥的选择;Cassandra 不替代它们,而是补上”海量写入高可用”这一块。和 Redis 缓存设计 搭配时,也别指望 Cassandra 当低延迟缓存——它定位是持久存储。
八、生产落地检查清单
- 用
NetworkTopologyStrategy,副本数按机房规划,至少 3 节点起步; - 主键先按查询模式设计,分区键控制单分区大小(建议 < 100MB);
- 一致性级别默认
LOCAL_QUORUM,跨城写入再评估; - 写路径开启
QUORUM、配合读修复,避免永久脏数据; - 定时
nodetool repair,并监控压实堆积与 GC 停顿; - 冷数据用
TTL自动清理,分桶防止大分区。
九、压实策略与容量规划
SSTable 不可变,更新和删除都是”追加+墓碑(tombstone)”,真正的清理靠后台压实(Compaction)。选错压实策略会直接拖垮读写,三种常用策略要分清:
| 策略 | 适用场景 | 特点 |
|---|---|---|
| STCS(SizeTiered) | 默认,写多读少的通用负载 | 小 SSTable 滚动合并成大文件,写放大低 |
| LCS(Leveled) | 读多、读放大敏感 | 分层固定大小,读路径短,但写放大高 |
| TWCS(TimeWindow) | 时序、带 TTL 的数据 | 按时间窗口归档,过期窗口整段丢弃,几乎零无效压实 |
物联网、指标类场景强烈建议用 TWCS:每个时间窗口(如一天)独立压实,数据过期后整窗口被丢弃,不会反复重写历史数据。容量规划上,Cassandra 默认 num_tokens=256(vnode)让数据在节点间均匀打散;用 nodetool tablestats 观察各表待压实数据量与磁盘占用,提前规划扩容,而不是等磁盘写满。
十、客户端接入:CQL 驱动示例
应用侧用官方 CQL 驱动连接,注意连接池与一致性级别要在驱动层统一设置,而不是每次查询手写。下面是 Python 驱动的最小可运行示例:
from cassandra.cluster import Cluster
from cassandra.query import dict_factory
cluster = Cluster(
contact_points=["cass1", "cass2", "cass3"],
load_balancing_policy=None, # 默认 TokenAware 策略,请求直发目标分区节点
)
session = cluster.connect("iot")
session.row_factory = dict_factory
session.default_consistency_level = "LOCAL_QUORUM" # 驱动层统一一致性级别
rows = session.execute(
"SELECT ts, temperature FROM device_metrics "
"WHERE device_id=%(did)s AND bucket=%(bk)s LIMIT 10",
{"did": "dev-01", "bk": "2026-09-25"},
)
for r in rows:
print(r["ts"], r["temperature"])
驱动会自动做令牌感知路由,把请求发到持有目标分区的节点,减少 coordinator 转发跳数。生产环境务必配置重试策略与幂等写(用 IF NOT EXISTS 或轻事务 LWT 处理并发插入),并对批量写入使用 execute_async 做并发攒批,避免单线程串行把集群吞吐压瘪。
掌握以上要点,你就能把 Cassandra 用在该用的地方:让写入与可用性的天花板,由”加机器”而不是”改架构”来抬高。




