Cassandra教程FG005-Cassandra数据模型与分区键设计实战
本文档风哥主要介绍Cassandra数据库数据模型与分区键设计实战,包括数据模型概述、分区键概念、聚簇键概念、分区键设计原则、热点分区避免策略、数据类型选择、分区键设计实战、聚簇键设计实战、复合主键设计实战、时序数据模型设计、用户数据模型设计、热点分区问题解决等内容,风哥教程参考Cassandra官方文档Data Modeling内容编写,适合DBA人员和开发人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 Cassandra数据库数据模型概述
Cassandra数据库采用宽列存储模型(Wide Column Store),与关系型数据库的数据模型有本质区别。Cassandra数据模型基于查询驱动设计理念,表结构设计服务于查询需求。更多视频教程www.fgedu.net.cn
1.1.1 Cassandra数据库数据模型特点
# 1. 宽列存储
– 每行可以有不同的列集合
– 列可以动态添加
– 支持嵌套数据类型
# 2. 查询驱动设计
– 根据查询需求设计表结构
– 一个查询一个表的设计理念
– 避免复杂查询和连接操作
# 3. 反规范化
– 允许数据冗余
– 通过空间换时间
– 提高查询性能
# 4. 分区存储
– 数据按分区键分布存储
– 每个分区存储在一个节点上
– 分区内数据按聚簇键排序
# 数据模型层次结构
Cluster(集群)
└── Keyspace(键空间)
└── Table(表)
└── Partition(分区)
└── Row(行)
└── Column(列)
1.1.2 Cassandra与关系型数据库对比
# 关系型数据库(RDBMS)
– 范式化设计
– 表结构固定
– 支持JOIN操作
– 支持复杂查询
– ACID事务
# Cassandra
– 反范式化设计
– 表结构灵活
– 不支持JOIN操作
– 查询简单直接
– 最终一致性
# 设计理念对比
RDBMS: 数据驱动设计
Cassandra: 查询驱动设计
# 示例对比
# RDBMS设计
用户表: user_id, user_name, email
订单表: order_id, user_id, order_time, amount
订单明细表: order_item_id, order_id, product_id, quantity
# Cassandra设计(根据查询需求)
用户信息表: user_id, user_name, email
用户订单表: user_id, order_time, order_id, amount
订单明细表: order_id, product_id, quantity
1.2 Cassandra数据库分区键概念
分区键(Partition Key)是Cassandra主键的第一部分,决定数据在集群中的分布位置。
1.2.1 Cassandra数据库分区键作用
# 1. 数据分布
– 通过哈希函数计算分区位置
– 决定数据存储在哪个节点
– 相同分区键的数据存储在同一分区
# 2. 数据定位
– 查询时必须指定分区键
– 支持快速数据定位
– 避免全表扫描
# 3. 数据隔离
– 每个分区独立存储
– 分区间无关联
– 支持分区级别操作
# 分区键计算过程
分区键值 → 哈希函数 → 令牌值 → 节点定位
# 示例
分区键: user_id = “fgedu001”
哈希函数: Murmur3Hash
令牌值: -1234567890
存储节点: Node3
# 分区键类型
# 1. 简单分区键
PRIMARY KEY (user_id)
# 2. 复合分区键
PRIMARY KEY ((user_id, order_date))
1.2.2 Cassandra数据库分区键特点
# 1. 高基数
– 分区键值应该具有高基数
– 避免数据倾斜
– 确保数据均匀分布
# 2. 查询友好
– 分区键应该是常用查询条件
– 查询必须包含分区键
– 支持高效查询
# 3. 不可变
– 分区键值不能修改
– 如需修改需删除重建
– 设计时应考虑稳定性
# 4. 唯一性
– 分区键组合必须唯一
– 确保数据唯一标识
– 避免数据覆盖
# 分区键选择原则
1. 选择高基数列
2. 选择常用查询条件
3. 选择稳定的列
4. 避免热点分区
1.3 Cassandra数据库聚簇键概念
聚簇键(Clustering Key)是Cassandra主键的第二部分,决定分区内数据的排序方式。
1.3.1 Cassandra数据库聚簇键作用
# 1. 数据排序
– 决定分区内数据的物理排序
– 支持升序或降序排列
– 影响查询性能
# 2. 范围查询
– 支持范围查询操作
– 支持ORDER BY排序
– 支持分页查询
# 3. 数据唯一性
– 与分区键组合确保数据唯一
– 支持多列聚簇键
– 支持复杂排序需求
# 聚簇键语法
PRIMARY KEY (partition_key, clustering_key1, clustering_key2)
# 示例
PRIMARY KEY (user_id, order_time)
– 分区键: user_id
– 聚簇键: order_time
# 排序方向
WITH CLUSTERING ORDER BY (order_time DESC)
– ASC: 升序(默认)
– DESC: 降序
1.3.2 Cassandra数据库聚簇键特点
# 1. 排序存储
– 分区内数据按聚簇键排序存储
– 支持高效范围查询
– 支持ORDER BY操作
# 2. 可选性
– 聚簇键是可选的
– 无聚簇键时分区只有一行
– 有聚簇键时分区可有多行
# 3. 多列支持
– 支持多列聚簇键
– 按列顺序排序
– 支持复杂排序需求
# 4. 查询灵活性
– 查询可不指定聚簇键
– 支持范围查询
– 支持分页查询
# 聚簇键选择原则
1. 选择需要排序的列
2. 选择需要范围查询的列
3. 考虑查询排序方向
4. 支持分页查询需求
Part02-生产环境规划与建议
2.1 Cassandra数据库分区键设计原则
Cassandra数据库分区键设计原则详解:
2.1.1 Cassandra数据库分区键设计要点
# 1. 高基数原则
– 分区键值应该具有高基数
– 避免数据倾斜
– 确保数据均匀分布
# 好的分区键示例
– user_id: 用户ID,基数高
– device_id: 设备ID,基数高
– order_id: 订单ID,基数高
# 不好的分区键示例
– status: 状态值,基数低
– country: 国家,基数低
– date: 日期,基数低(可能产生热点)
# 2. 查询驱动原则
– 分区键应该是常用查询条件
– 查询必须包含分区键
– 避免ALLOW FILTERING
# 查询示例
# 好的设计
SELECT * FROM fgedu_orders WHERE user_id = ?;
# 不好的设计(需要ALLOW FILTERING)
SELECT * FROM fgedu_orders WHERE status = ?;
# 3. 数据均匀分布原则
– 分区键值应该均匀分布
– 避免热点分区
– 监控分区大小
# 4. 分区大小控制
– 单个分区建议不超过100MB
– 单个分区不超过10万行
– 超大分区需要拆分
2.1.2 Cassandra数据库分区键设计模式
# 模式1: 简单分区键
适用场景: 单一维度查询
示例: PRIMARY KEY (user_id)
# 模式2: 复合分区键
适用场景: 多维度查询,需要更细粒度分区
示例: PRIMARY KEY ((user_id, order_date))
# 模式3: 时间桶分区
适用场景: 时序数据,避免时间热点
示例: PRIMARY KEY ((device_id, date_bucket), timestamp)
# 模式4: 哈希桶分区
适用场景: 高基数数据,均匀分布
示例: PRIMARY KEY ((user_id_hash_bucket, user_id), timestamp)
# 模式5: 地理位置分区
适用场景: 地理位置相关查询
示例: PRIMARY KEY ((region, user_id), timestamp)
# 选择建议
– 优先考虑查询需求
– 确保数据均匀分布
– 控制分区大小
– 避免热点分区
2.2 Cassandra数据库热点分区避免策略
Cassandra数据库热点分区避免策略详解:
2.2.1 Cassandra数据库热点分区原因
# 1. 低基数分区键
分区键值种类少,数据集中在少数分区
示例: 按status分区,只有几个状态值
# 2. 时间序列热点
按时间分区,最新数据写入频繁
示例: 按date分区,当天数据写入集中
# 3. 业务热点
某些业务实体访问频繁
示例: 热门商品、热门用户
# 4. 数据倾斜
数据分布不均匀
示例: 某些用户数据量特别大
# 热点分区危害
– 节点负载不均衡
– 查询性能下降
– 写入延迟增加
– 节点故障风险增加
2.2.2 Cassandra数据库热点分区解决方案
# 方案1: 时间桶分区
将时间划分为多个桶,分散写入
CREATE TABLE fgedu_events (
device_id text,
date_bucket text, — 格式: 2024-01-15-00, 2024-01-15-01
event_time timestamp,
event_data text,
PRIMARY KEY ((device_id, date_bucket), event_time)
);
# 方案2: 哈希桶分区
添加哈希桶前缀,分散数据
CREATE TABLE fgedu_logs (
bucket int, — 0-99的哈希桶
log_id uuid,
log_time timestamp,
log_data text,
PRIMARY KEY ((bucket, log_id), log_time)
);
# 方案3: 复合分区键
使用多个列组成分区键
CREATE TABLE fgedu_metrics (
metric_name text,
time_bucket text,
timestamp timestamp,
value double,
PRIMARY KEY ((metric_name, time_bucket), timestamp)
);
# 方案4: 随机前缀
添加随机前缀分散数据
CREATE TABLE fgedu_hot_items (
prefix int, — 1-10的随机前缀
item_id text,
view_time timestamp,
PRIMARY KEY ((prefix, item_id), view_time)
);
# 方案5: 分区拆分
将大分区拆分为多个小分区
— 原设计
PRIMARY KEY (user_id)
— 改进设计
PRIMARY KEY ((user_id, year_month))
2.3 Cassandra数据库数据类型选择
Cassandra数据库数据类型选择建议:
2.3.1 Cassandra数据库数据类型列表
# 数值类型
tinyint – 8位有符号整数
smallint – 16位有符号整数
int – 32位有符号整数
bigint – 64位有符号整数
varint – 任意精度整数
float – 32位浮点数
double – 64位浮点数
decimal – 高精度十进制数
# 文本类型
text – UTF-8编码字符串
varchar – UTF-8编码字符串(与text相同)
ascii – ASCII字符串
# 时间类型
timestamp – 时间戳(毫秒精度)
date – 日期
time – 时间
duration – 时间间隔
# 布尔类型
boolean – 布尔值(true/false)
# 二进制类型
blob – 二进制数据
# 标识类型
uuid – UUID类型
timeuuid – 时间有序UUID
# 集合类型
list
set
map
# 元组类型
tuple
# 用户定义类型
frozen
2.3.2 Cassandra数据库数据类型选择建议
# 主键选择
– uuid: 随机ID,全局唯一
– timeuuid: 时间有序ID,支持时间范围查询
– text: 字符串ID,可读性好
# 时间选择
– timestamp: 精确时间,支持时间运算
– date: 仅日期,节省存储
– time: 仅时间,配合date使用
# 数值选择
– int: 一般整数
– bigint: 大整数
– decimal: 精确计算(金额)
# 文本选择
– text: 通用文本
– ascii: 纯ASCII文本,节省存储
# 集合选择
– list: 有序列表,允许重复
– set: 无序集合,不允许重复
– map: 键值对映射
# 示例
CREATE TABLE fgedu_products (
product_id uuid PRIMARY KEY,
product_name text,
price decimal,
stock int,
created_at timestamp,
tags set
attributes map
images list
);
Part03-生产环境项目实施方案
3.1 Cassandra数据库分区键设计实战
Cassandra数据库分区键设计实战案例:
3.1.1 Cassandra数据库简单分区键设计
# cqlsh 192.168.1.101 9042 -u fgedu -p Fgedu@2024
Connected to fgedu_cluster at 192.168.1.101:9042
# 切换到Keyspace
cqlsh> USE fgedudb;
cqlsh:fgedudb>
# 创建简单分区键表
cqlsh:fgedudb> CREATE TABLE fgedu_user_profiles (
… user_id uuid PRIMARY KEY,
… user_name text,
… email text,
… phone text,
… avatar text,
… created_at timestamp,
… updated_at timestamp
… );
# 插入测试数据
cqlsh:fgedudb> INSERT INTO fgedu_user_profiles (user_id, user_name, email, phone, avatar, created_at, updated_at)
… VALUES (
… uuid(),
… ‘zhangsan’,
… ‘zhangsan@fgedu.net.cn’,
… ‘13800138000’,
… ‘https://cdn.fgedu.net.cn/avatar/zhangsan.jpg’,
… toTimestamp(now()),
… toTimestamp(now())
… );
# 查询数据(必须指定分区键)
cqlsh:fgedudb> SELECT * FROM fgedu_user_profiles
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b;
user_id | avatar | created_at | email | phone | updated_at | user_name
————————————–+————————————————+———————————-+———————-+————-+———————————-+———–
8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b | https://cdn.fgedu.net.cn/avatar/zhangsan.jpg | 2024-01-15 10:30:00.123000+0000 | zhangsan@fgedu.net.cn | 13800138000 | 2024-01-15 10:30:00.123000+0000 | zhangsan
(1 row)
# 查看分区信息
cqlsh:fgedudb> SELECT token(user_id), user_id, user_name
… FROM fgedu_user_profiles LIMIT 5;
system.token(user_id) | user_id | user_name
———————–+————————————–+———–
-123456789012345678 | 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b | zhangsan
3.1.2 Cassandra数据库复合分区键设计
cqlsh:fgedudb> CREATE TABLE fgedu_user_activities (
… user_id uuid,
… activity_date date,
… activity_time timestamp,
… activity_type text,
… activity_data text,
… PRIMARY KEY ((user_id, activity_date), activity_time)
… ) WITH CLUSTERING ORDER BY (activity_time DESC);
# 说明
# 分区键: (user_id, activity_date) 复合分区键
# 聚簇键: activity_time
# 按activity_time降序排列
# 插入测试数据
cqlsh:fgedudb> INSERT INTO fgedu_user_activities
… (user_id, activity_date, activity_time, activity_type, activity_data)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… ‘2024-01-15’,
… ‘2024-01-15 10:00:00’,
… ‘LOGIN’,
… ‘{“ip”: “192.168.1.100”, “device”: “iPhone”}’
… );
cqlsh:fgedudb> INSERT INTO fgedu_user_activities
… (user_id, activity_date, activity_time, activity_type, activity_data)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… ‘2024-01-15’,
… ‘2024-01-15 11:00:00’,
… ‘PURCHASE’,
… ‘{“order_id”: “order-001”, “amount”: 7999.00}’
… );
# 查询用户某天的活动(必须指定完整的分区键)
cqlsh:fgedudb> SELECT * FROM fgedu_user_activities
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND activity_date = ‘2024-01-15’;
user_id | activity_date | activity_time | activity_data | activity_type
————————————–+—————+———————————–+————————————————+—————
8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b | 2024-01-15 | 2024-01-15 11:00:00.000000+0000 | {“order_id”: “order-001”, “amount”: 7999.00} | PURCHASE
8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b | 2024-01-15 | 2024-01-15 10:00:00.000000+0000 | {“ip”: “192.168.1.100”, “device”: “iPhone”} | LOGIN
(2 rows)
3.2 Cassandra数据库聚簇键设计实战
Cassandra数据库聚簇键设计实战案例:
3.2.1 Cassandra数据库单列聚簇键设计
cqlsh:fgedudb> CREATE TABLE fgedu_user_messages (
… user_id uuid,
… message_time timestamp,
… message_id uuid,
… sender_id uuid,
… message_content text,
… is_read boolean,
… PRIMARY KEY (user_id, message_time)
… ) WITH CLUSTERING ORDER BY (message_time DESC);
# 说明
# 分区键: user_id
# 聚簇键: message_time
# 按message_time降序排列(最新消息在前)
# 插入测试数据
cqlsh:fgedudb> INSERT INTO fgedu_user_messages
… (user_id, message_time, message_id, sender_id, message_content, is_read)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… ‘2024-01-15 10:00:00’,
… uuid(),
… uuid(),
… ‘Hello, how are you?’,
… false
… );
cqlsh:fgedudb> INSERT INTO fgedu_user_messages
… (user_id, message_time, message_id, sender_id, message_content, is_read)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… ‘2024-01-15 11:00:00’,
… uuid(),
… uuid(),
… ‘I am fine, thank you!’,
… false
… );
# 查询用户消息(按时间降序)
cqlsh:fgedudb> SELECT * FROM fgedu_user_messages
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… LIMIT 10;
# 范围查询
cqlsh:fgedudb> SELECT * FROM fgedu_user_messages
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND message_time >= ‘2024-01-15 10:30:00’;
# 分页查询
cqlsh:fgedudb> SELECT * FROM fgedu_user_messages
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… LIMIT 10;
# 使用分页状态
# 在应用代码中使用paging state实现分页
3.2.2 Cassandra数据库多列聚簇键设计
cqlsh:fgedudb> CREATE TABLE fgedu_product_reviews (
… product_id uuid,
… review_date date,
… review_time timestamp,
… user_id uuid,
… rating int,
… review_title text,
… review_content text,
… helpful_count int,
… PRIMARY KEY (product_id, review_date, review_time)
… ) WITH CLUSTERING ORDER BY (review_date DESC, review_time DESC);
# 说明
# 分区键: product_id
# 聚簇键: (review_date, review_time) 多列聚簇键
# 先按review_date降序,再按review_time降序
# 插入测试数据
cqlsh:fgedudb> INSERT INTO fgedu_product_reviews
… (product_id, review_date, review_time, user_id, rating, review_title, review_content, helpful_count)
… VALUES (
… uuid(),
… ‘2024-01-15’,
… ‘2024-01-15 10:00:00’,
… uuid(),
… 5,
… ‘Great product!’,
… ‘This is the best product I have ever bought.’,
… 10
… );
# 查询商品评论(按日期和时间降序)
cqlsh:fgedudb> SELECT * FROM fgedu_product_reviews
… WHERE product_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… LIMIT 10;
# 查询特定日期的评论
cqlsh:fgedudb> SELECT * FROM fgedu_product_reviews
… WHERE product_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND review_date = ‘2024-01-15’;
# 范围查询(日期范围)
cqlsh:fgedudb> SELECT * FROM fgedu_product_reviews
… WHERE product_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND review_date >= ‘2024-01-10’
… AND review_date <= '2024-01-15';
3.3 Cassandra数据库复合主键设计实战
Cassandra数据库复合主键设计实战案例:
3.3.1 Cassandra数据库复合主键完整示例
cqlsh:fgedudb> CREATE TABLE fgedu_order_details (
… user_id uuid,
… order_date date,
… order_time timestamp,
… order_id uuid,
… product_id uuid,
… product_name text,
… quantity int,
… unit_price decimal,
… total_price decimal,
… status text,
… PRIMARY KEY ((user_id, order_date), order_time, order_id)
… ) WITH CLUSTERING ORDER BY (order_time DESC, order_id ASC);
# 说明
# 复合分区键: (user_id, order_date)
# 聚簇键: (order_time, order_id)
# 排序: 先按order_time降序,再按order_id升序
# 插入测试数据
cqlsh:fgedudb> INSERT INTO fgedu_order_details
… (user_id, order_date, order_time, order_id, product_id, product_name, quantity, unit_price, total_price, status)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… ‘2024-01-15’,
… ‘2024-01-15 10:00:00’,
… uuid(),
… uuid(),
… ‘iPhone 15 Pro’,
… 1,
… 9999.00,
… 9999.00,
… ‘PAID’
… );
# 查询用户某天的订单
cqlsh:fgedudb> SELECT * FROM fgedu_order_details
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND order_date = ‘2024-01-15’;
# 查询用户某天某时间段的订单
cqlsh:fgedudb> SELECT * FROM fgedu_order_details
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… AND order_date = ‘2024-01-15’
… AND order_time >= ‘2024-01-15 09:00:00’
… AND order_time <= '2024-01-15 12:00:00';
# 查看表结构
cqlsh:fgedudb> DESCRIBE TABLE fgedu_order_details;
Part04-生产案例与实战讲解
4.1 Cassandra数据库时序数据模型设计
Cassandra数据库时序数据模型设计实战:
4.1.1 Cassandra数据库时序数据设计需求
# 场景: IoT设备数据采集
– 设备数量: 10万台
– 数据频率: 每秒1次
– 保留周期: 30天
– 查询需求: 按设备查询最近数据
# 设计挑战
1. 写入量大: 10万TPS
2. 时间热点: 最新数据写入集中
3. 数据量大: 30天数据约260TB
4. 查询效率: 快速查询最近数据
# 设计方案
# 表1: 设备最新数据表(快速查询最新数据)
CREATE TABLE fgedu_device_latest (
device_id text PRIMARY KEY,
timestamp timestamp,
temperature double,
humidity double,
pressure double,
status text
);
# 表2: 设备历史数据表(时间桶分区)
CREATE TABLE fgedu_device_history (
device_id text,
time_bucket text, — 格式: 2024-01-15-00
timestamp timestamp,
temperature double,
humidity double,
pressure double,
status text,
PRIMARY KEY ((device_id, time_bucket), timestamp)
) WITH CLUSTERING ORDER BY (timestamp DESC)
AND default_time_to_live = 2592000; — 30天自动过期
# 表3: 设备统计数据表(小时统计)
CREATE TABLE fgedu_device_hourly_stats (
device_id text,
stat_date date,
stat_hour int,
avg_temperature double,
max_temperature double,
min_temperature double,
avg_humidity double,
sample_count int,
PRIMARY KEY ((device_id, stat_date), stat_hour)
) WITH CLUSTERING ORDER BY (stat_hour ASC)
AND default_time_to_live = 7776000; — 90天自动过期
4.1.2 Cassandra数据库时序数据操作实战
cqlsh:fgedudb> INSERT INTO fgedu_device_latest (device_id, timestamp, temperature, humidity, pressure, status)
… VALUES (
… ‘device-001’,
… toTimestamp(now()),
… 25.5,
… 60.0,
… 1013.25,
… ‘NORMAL’
… );
# 插入历史数据(使用时间桶)
cqlsh:fgedudb> INSERT INTO fgedu_device_history
… (device_id, time_bucket, timestamp, temperature, humidity, pressure, status)
… VALUES (
… ‘device-001’,
… ‘2024-01-15-10’,
… toTimestamp(now()),
… 25.5,
… 60.0,
… 1013.25,
… ‘NORMAL’
… );
# 查询设备最新数据
cqlsh:fgedudb> SELECT * FROM fgedu_device_latest
… WHERE device_id = ‘device-001’;
device_id | humidity | pressure | status | temperature | timestamp
————+———-+———-+——–+————-+———————————-
device-001 | 60.0 | 1013.25 | NORMAL | 25.5 | 2024-01-15 10:30:00.123000+0000
(1 row)
# 查询设备历史数据(指定时间桶)
cqlsh:fgedudb> SELECT * FROM fgedu_device_history
… WHERE device_id = ‘device-001’
… AND time_bucket = ‘2024-01-15-10’
… LIMIT 100;
# 查询设备统计数据
cqlsh:fgedudb> SELECT * FROM fgedu_device_hourly_stats
… WHERE device_id = ‘device-001’
… AND stat_date = ‘2024-01-15’;
4.2 Cassandra数据库用户数据模型设计
Cassandra数据库用户数据模型设计实战:
4.2.1 Cassandra数据库用户数据设计需求
# 场景: 社交应用用户系统
– 用户数量: 1亿
– 查询需求:
1. 根据用户ID查询用户信息
2. 根据邮箱查询用户
3. 根据用户名查询用户
4. 查询用户好友列表
5. 查询用户动态列表
# 设计方案
# 表1: 用户主表
CREATE TABLE fgedu_users (
user_id uuid PRIMARY KEY,
user_name text,
email text,
phone text,
password_hash text,
nickname text,
avatar text,
gender text,
birthday date,
region text,
created_at timestamp,
updated_at timestamp,
status text
);
# 表2: 邮箱索引表
CREATE TABLE fgedu_users_by_email (
email text PRIMARY KEY,
user_id uuid
);
# 表3: 用户名索引表
CREATE TABLE fgedu_users_by_name (
user_name text PRIMARY KEY,
user_id uuid
);
# 表4: 用户好友表
CREATE TABLE fgedu_user_friends (
user_id uuid,
friend_time timestamp,
friend_id uuid,
friend_name text,
friend_avatar text,
status text,
PRIMARY KEY (user_id, friend_time)
) WITH CLUSTERING ORDER BY (friend_time DESC);
# 表5: 用户动态表
CREATE TABLE fgedu_user_posts (
user_id uuid,
post_time timestamp,
post_id uuid,
post_type text,
post_content text,
like_count int,
comment_count int,
PRIMARY KEY (user_id, post_time)
) WITH CLUSTERING ORDER BY (post_time DESC)
AND default_time_to_live = 31536000; — 1年自动过期
4.2.2 Cassandra数据库用户数据操作实战
cqlsh:fgedudb> BEGIN BATCH
… INSERT INTO fgedu_users (user_id, user_name, email, phone, password_hash, nickname, avatar, gender, birthday, region, created_at, updated_at, status)
… VALUES (uuid(), ‘wangwu’, ‘wangwu@fgedu.net.cn’, ‘13900139000’, ‘hash123’, ‘王五’, ‘avatar.jpg’, ‘MALE’, ‘1990-01-01’, ‘Beijing’, toTimestamp(now()), toTimestamp(now()), ‘ACTIVE’);
… INSERT INTO fgedu_users_by_email (email, user_id)
… VALUES (‘wangwu@fgedu.net.cn’, uuid());
… INSERT INTO fgedu_users_by_name (user_name, user_id)
… VALUES (‘wangwu’, uuid());
… APPLY BATCH;
# 查询用户(按ID)
cqlsh:fgedudb> SELECT * FROM fgedu_users
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b;
# 查询用户(按邮箱)
cqlsh:fgedudb> SELECT u.* FROM fgedu_users u
… JOIN fgedu_users_by_email e ON u.user_id = e.user_id
… WHERE e.email = ‘wangwu@fgedu.net.cn’;
# 注意: Cassandra不支持JOIN,需要分两步查询
# 步骤1: 根据邮箱查询user_id
cqlsh:fgedudb> SELECT user_id FROM fgedu_users_by_email
… WHERE email = ‘wangwu@fgedu.net.cn’;
user_id
————————————–
8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
(1 row)
# 步骤2: 根据user_id查询用户信息
cqlsh:fgedudb> SELECT * FROM fgedu_users
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b;
# 添加好友
cqlsh:fgedudb> INSERT INTO fgedu_user_friends (user_id, friend_time, friend_id, friend_name, friend_avatar, status)
… VALUES (
… 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b,
… toTimestamp(now()),
… uuid(),
… ‘李四’,
… ‘avatar_lisi.jpg’,
… ‘ACCEPTED’
… );
# 查询用户好友列表
cqlsh:fgedudb> SELECT * FROM fgedu_user_friends
… WHERE user_id = 8e5f6a7b-8c9d-0e1f-2a3b-4c5d6e7f8a9b
… LIMIT 20;
4.3 Cassandra数据库热点分区问题解决
Cassandra数据库热点分区问题解决实战:
4.3.1 Cassandra数据库热点分区检测
# 方法1: 使用nodetool tablehistograms
# nodetool tablehistograms fgedudb.fgedu_events
fgedudb/fgedu_events histograms
Percentile SSTables Write Latency Read Latency Partition Size Cell Count
(micros) (micros) (bytes)
50% 0.00 125.45 234.56 1024.00 10.00
75% 0.00 234.56 456.78 2048.00 20.00
95% 0.00 456.78 890.12 4096.00 40.00
98% 0.00 567.89 1234.56 8192.00 80.00
99% 0.00 678.90 1567.89 16384.00 160.00
Min 0.00 100.00 200.00 512.00 5.00
Max 0.00 1234.56 2345.67 1048576.00 1024.00
# 方法2: 使用nodetool cfstats
# nodetool cfstats fgedudb.fgedu_events | grep -E “Partition|Row”
Maximum partition size: 1048576 bytes
Mean partition size: 8192 bytes
Maximum row size: 1024 bytes
Mean row size: 256 bytes
# 方法3: 查询分区大小
cqlsh:fgedudb> SELECT partition_key, COUNT(*) as row_count
… FROM fgedu_events
… GROUP BY partition_key
… LIMIT 10;
# 方法4: 使用sstable-tools分析
# sstable-tools /cassandra/fgdata/data/fgedudb-fgedu_events-*/mc-1-big-Data.db partition-size
4.3.2 Cassandra数据库热点分区解决
# 问题: 按日期分区导致热点
# 原设计
CREATE TABLE fgedu_events_bad (
event_date date PRIMARY KEY,
event_data text
);
# 解决方案1: 时间桶分区
CREATE TABLE fgedu_events_v1 (
event_date date,
bucket int, — 0-99
event_time timestamp,
event_id uuid,
event_data text,
PRIMARY KEY ((event_date, bucket), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
# 解决方案2: 复合分区键
CREATE TABLE fgedu_events_v2 (
region text,
event_date date,
event_time timestamp,
event_id uuid,
event_data text,
PRIMARY KEY ((region, event_date), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
# 解决方案3: 随机前缀
CREATE TABLE fgedu_events_v3 (
prefix int, — 1-10
event_date date,
event_time timestamp,
event_id uuid,
event_data text,
PRIMARY KEY ((prefix, event_date), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
# 数据迁移
# 1. 创建新表
# 2. 编写迁移脚本
# 3. 迁移数据
# 4. 验证数据
# 5. 切换应用
# 6. 删除旧表
# 迁移脚本示例
#!/bin/bash
# migrate_events.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
# 导出旧数据
#cqlsh -e “COPY fgedudb.fgedu_events_bad TO ‘/tmp/events.csv'”
# 转换数据格式
#awk -F’,’ ‘{print $1″,”int(rand()*100)”,”$2″,”$3″,”$4}’ /tmp/events.csv > /tmp/events_new.csv
# 导入新数据
#cqlsh -e “COPY fgedudb.fgedu_events_v1 (event_date, bucket, event_time, event_id, event_data) FROM ‘/tmp/events_new.csv'”
Part05-风哥经验总结与分享
5.1 Cassandra数据库数据模型设计最佳实践
Cassandra数据库数据模型设计最佳实践总结:
- 查询驱动设计:根据查询需求设计表结构,一个查询一个表
- 分区键设计:选择高基数列,确保数据均匀分布
- 聚簇键设计:根据排序需求设计,支持范围查询
- 热点避免:使用时间桶、哈希桶等技术避免热点分区
- 分区大小控制:单个分区不超过100MB,不超过10万行
- 数据类型选择:选择合适的数据类型,考虑存储和查询效率
- TTL使用:设置合理的数据过期时间,自动清理过期数据
- 反规范化:允许数据冗余,避免连接查询
5.2 Cassandra数据库数据模型设计检查清单
# 1. 查询需求分析
[ ] 查询模式已确定
[ ] 查询条件已明确
[ ] 查询频率已评估
[ ] 查询性能要求已确定
# 2. 分区键设计
[ ] 分区键基数足够高
[ ] 分区键分布均匀
[ ] 分区键支持查询条件
[ ] 分区大小可控
# 3. 聚簇键设计
[ ] 聚簇键支持排序需求
[ ] 聚簇键支持范围查询
[ ] 聚簇键排序方向正确
[ ] 聚簇键数量合理
# 4. 数据类型选择
[ ] 数据类型选择合适
[ ] 数据类型支持查询需求
[ ] 数据类型存储效率高
[ ] 数据类型兼容性好
# 5. 性能优化
[ ] 压缩策略选择合理
[ ] 压缩配置合理
[ ] 缓存配置合理
[ ] TTL设置合理
# 6. 热点避免
[ ] 无明显热点分区
[ ] 数据分布均匀
[ ] 写入负载均衡
[ ] 查询负载均衡
5.3 Cassandra数据库数据模型分析工具
Cassandra数据库数据模型分析工具推荐:
5.3.1 Cassandra数据库分析工具
# 1. nodetool tablehistograms
# 分析分区大小分布
# nodetool tablehistograms fgedudb.fgedu_table
# 2. nodetool cfstats
# 查看表统计信息
# nodetool cfstats fgedudb.fgedu_table
# 3. sstable-tools
# SSTable分析工具
# sstable-tools /path/to/sstable partition-size
# 4. cassandra-stress
# 性能测试工具
# cassandra-stress user profile=/path/to/profile.yaml
# 5. cqlsh
# CQL命令行工具
# DESCRIBE TABLE fgedudb.fgedu_table
# 6. DataStax DevCenter
# 可视化开发工具
# 支持表结构查看和数据分析
本文档详细介绍了Cassandra数据库数据模型与分区键设计实战,包括数据模型概述、分区键概念、聚簇键概念、分区键设计原则、热点分区避免策略、数据类型选择、分区键设计实战、聚簇键设计实战、复合主键设计实战、时序数据模型设计、用户数据模型设计、热点分区问题解决、数据模型设计最佳实践、数据模型设计检查清单、数据模型分析工具等内容。通过学习本文档,读者可以掌握Cassandra数据库数据模型设计技能,为应用开发打下坚实基础。
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
