1. 概述
Cassandra 是一款 NoSQL 数据库,在保证高性能的同时提供了高可用性和水平扩展能力。
要充分发挥 Cassandra 的性能优势,我们需要根据具体业务场景的查询模式精心设计数据模型。简单粗暴地说,数据建模是 Cassandra 性能优化的核心。
本文将深入探讨 Cassandra 数据建模的关键概念和实践方法。如果对 Cassandra 基础操作不熟悉,建议先阅读我们的 Cassandra 与 Java 集成 文章。
2. 分区键
Cassandra 作为分布式数据库,数据会被分区并存储在集群的多个节点上。
分区键由一个或多个数据字段组成,分区器通过哈希分区键生成令牌,从而将数据均匀分布到整个集群。⚠️ 分区键设计不当会导致数据倾斜,这是常见的性能踩坑点。
3. 聚合键
聚合键由一个或多个字段组成,用于将具有相同分区键的行进行分组,并按指定顺序存储。
举个典型场景:存储时间序列数据时,如果需要按时间顺序检索数据,包含时间字段的聚合键就能大幅提升查询效率。
✅ 关键点:分区键 + 聚合键 = 主键,唯一标识 Cassandra 集群中的任何记录。
4. 查询模式设计原则
开始 Cassandra 数据建模前,必须先明确查询模式,并遵循以下原则:
- 单分区查询:每个查询应只从单个分区获取数据
- 分区大小控制:需监控单个分区的数据量(Cassandra 对单分区列数有限制)
- 数据反规范化:为支持不同查询模式,允许数据冗余存储
基于以上原则,我们通过实际案例说明如何设计 Cassandra 数据模型。
5. 实战数据建模案例
5.1. Facebook 帖子存储
假设我们要存储不同用户的 Facebook 帖子,常见查询是获取指定用户的最新 N 条帖子。
根据设计原则:
- 必须将单个用户的所有数据存储在同一个分区
- 使用帖子时间戳作为聚合键,可高效获取最新帖子
表结构设计如下:
CREATE TABLE posts_facebook (
user_id uuid,
post_id timeuuid,
content text,
PRIMARY KEY (user_id, post_id) )
WITH CLUSTERING ORDER BY (post_id DESC);
查询用户 Anna 的最新 20 条帖子:
SELECT content FROM posts_facebook WHERE user_id = "Anna_id" LIMIT 20
5.2. 全国健身房信息
假设要存储不同国家、州/省、城市的健身房信息,需求包括:
- 查询指定城市的所有健身房
- 按开业日期排序返回结果
根据设计原则:
- 将同一国家、州/省、城市的健身房数据放在单个分区
- 使用开业日期和健身房名称作为聚合键
表结构设计:
CREATE TABLE gyms_by_city (
country_code text,
state text,
city text,
gym_name text,
opening_date timestamp,
PRIMARY KEY (
(country_code, state_province, city),
(opening_date, gym_name))
WITH CLUSTERING ORDER BY (opening_date ASC, gym_name ASC);
查询美国亚利桑那州菲尼克斯市最早开业的 10 家健身房:
SELECT * FROM gyms_by_city
WHERE country_code = "us" AND state = "Arizona" AND city = "Phoenix"
LIMIT 10
查询最近开业的 10 家健身房(注意性能问题):
SELECT * FROM gyms_by_city
WHERE country_code = "us" and state = "Arizona" and city = "Phoenix"
ORDER BY opening_date DESC
LIMIT 10
⚠️ 踩坑提示:最后查询的排序方向与表定义相反,Cassandra 需要先获取数据再内存排序,性能较差。
5.3. 电商客户与商品
假设运营电商系统,需要存储客户和商品信息,常见查询包括:
- 获取客户信息
- 获取商品信息
- 查询喜欢某商品的所有客户
- 查询某客户喜欢的所有商品
基础方案:
- 使用单独表存储客户和商品信息
为支持查询 3 和 4,需引入数据冗余:
- 创建
Customer_By_Liked_Product
表 - 创建
Product_Liked_By_Customer
表
完整表结构:
CREATE TABLE Customer (
cust_id text,
first_name text,
last_name text,
registered_on timestamp,
PRIMARY KEY (cust_id));
CREATE TABLE Product (
prdt_id text,
title text,
PRIMARY KEY (prdt_id));
CREATE TABLE Customer_By_Liked_Product (
liked_prdt_id text,
liked_on timestamp,
title text,
cust_id text,
first_name text,
last_name text,
PRIMARY KEY (prdt_id, liked_on));
CREATE TABLE Product_Liked_By_Customer (
cust_id text,
first_name text,
last_name text,
liked_prdt_id text,
liked_on timestamp,
title text,
PRIMARY KEY (cust_id, liked_on));
✅ 设计技巧:使用
liked_on
作为聚合键,可高效获取"最近喜欢"的数据。
查询最近喜欢 "Pepsi" 的 10 位客户:
SELECT * FROM Customer_By_Liked_Product WHERE title = "Pepsi" LIMIT 10
查询客户 Anna 最近喜欢的 10 件商品:
SELECT * FROM Product_Liked_By_Customer
WHERE first_name = "Anna" LIMIT 10
6. 低效查询模式
由于 Cassandra 的分布式存储特性,以下查询模式性能较差:
跨分区查询 ❌
需要协调器从多个节点获取数据,在堆内存中临时存储并聚合,严重影响性能关联查询(JOIN) ❌
Cassandra 不支持关系型数据库的 JOIN 操作,强制使用会导致:- 查询速度极慢
- 数据一致性问题
- 可用性风险
7. 总结
本文系统介绍了 Cassandra 数据建模的最佳实践:
- 核心原则:基于查询模式设计数据模型,而非实体关系
- 关键要素:合理设计分区键和聚合键
- 实战技巧:通过数据反规范化支持多查询模式
- 避坑指南:避免跨分区查询和 JOIN 操作
✅ 记住:Cassandra 数据建模的本质是用空间换时间,通过数据冗余换取查询效率。对于有经验的开发者来说,理解并应用这些原则是发挥 Cassandra 强大性能的关键。