一、理解Cassandra的宽列存储本质

Cassandra的宽列存储和我们熟悉的Excel表格完全不同。它的每一行可以拥有不同的列结构,就像是一个可以动态扩展的档案袋。举个例子:

// 技术栈:Cassandra CQL
// 用户活动记录表 - 每个用户的活动类型和次数可以动态增加
CREATE TABLE user_activities (
    user_id uuid,
    activity_date date,
    activities map<text, int>,  // 键值对存储不同活动及其次数
    PRIMARY KEY (user_id, activity_date)
);

这个设计允许我们存储这样的数据:

  • 用户A在2023-01-01有 {'login':5, 'click':12}
  • 用户B在同一天可能有 {'login':3, 'purchase':2, 'share':1}

这种灵活性带来了两个重要特性:

  1. 稀疏存储:不会为不存在的列占用空间
  2. 动态扩展:随时可以添加新的列而不需要修改表结构

二、主键设计的黄金法则

Cassandra的主键就像快递分拣系统的邮政编码,决定了数据怎么分布和怎么快速找到。它由两部分组成:

// 技术栈:Cassandra CQL
// 电商订单表设计示例
CREATE TABLE orders (
    customer_id uuid,      // 分区键:决定数据存储在哪个节点
    order_date timestamp,  // 第一个聚类键:按时间排序
    order_id uuid,         // 第二个聚类键:确保唯一性
    items list<text>,      // 订单商品列表
    total decimal,
    PRIMARY KEY ((customer_id), order_date, order_id) 
) WITH CLUSTERING ORDER BY (order_date DESC);

这里有三点需要注意:

  1. 分区键要尽量分散:就像不要把所有的鸡蛋放在一个篮子里
  2. 聚类键顺序要考虑查询模式:我们经常按时间倒序查订单
  3. 复合主键就像多级文件夹:customer_id是文件夹,order_date是子文件夹

三、反规范化实战技巧

在Cassandra中,复制数据比关联表更高效。来看一个社交网络的例子:

// 技术栈:Cassandra CQL
// 用户信息反规范化设计
CREATE TABLE user_posts (
    user_id uuid,
    post_id timeuuid,      // 使用时间UUID保证时间顺序
    content text,
    author_name text,      // 反规范化:冗余存储作者名
    author_avatar text,    // 反规范化:冗余存储头像
    likes_counter int,
    PRIMARY KEY (user_id, post_id)
) WITH CLUSTERING ORDER BY (post_id DESC);

CREATE TABLE post_by_category (
    category text,
    post_id timeuuid,
    user_id uuid,
    content text,
    author_name text,      // 同样的数据在另一张表重复存储
    PRIMARY KEY (category, post_id)
);

这种设计虽然增加了存储成本,但带来了两个好处:

  1. 查询性能提升:不需要多表关联
  2. 数据本地化:相关数据存储在相邻位置

四、集合类型的正确使用姿势

Cassandra提供了三种集合类型:set、list和map。它们像不同形状的容器:

// 技术栈:Cassandra CQL
// 产品标签系统设计
CREATE TABLE products (
    product_id uuid,
    name text,
    tags set<text>,              // 适合存储无序唯一值
    versions list<text>,         // 保留版本顺序
    attributes map<text, text>,  // 键值对存储特性
    PRIMARY KEY (product_id)
);

// 插入示例数据
INSERT INTO products (product_id, name, tags, versions, attributes)
VALUES (
    uuid(), 
    '智能手表',
    {'电子', '穿戴', '智能'},                     // 集合初始化
    ['v1.0', 'v1.1', 'v2.0'],                    // 列表保持顺序
    {'颜色':'黑色', '重量':'50g', '材质':'金属'}  // 键值对
);

使用集合时要注意:

  1. 单个集合不要超过64KB
  2. 频繁更新的集合考虑拆分成独立表
  3. map的键不要使用动态生成的值

五、时间序列数据优化策略

Cassandra特别适合存储时间序列数据,比如物联网传感器读数:

// 技术栈:Cassandra CQL
// 温度传感器数据存储
CREATE TABLE temperature_readings (
    sensor_id text,
    bucket text,       // 时间分桶:按周存储
    reading_time timestamp,
    temperature float,
    PRIMARY KEY ((sensor_id, bucket), reading_time)
) WITH CLUSTERING ORDER BY (reading_time DESC);

// 插入示例数据
INSERT INTO temperature_readings 
(sensor_id, bucket, reading_time, temperature)
VALUES (
    'sensor-1', 
    '2023-week50',  // 2023年第50周
    '2023-12-15 08:00:00',
    23.5
);

这种设计解决了两个问题:

  1. 热点问题:通过分桶避免单个分区过大
  2. 查询效率:相同时间段的数据物理上相邻

六、物化视图的替代方案

虽然Cassandra有物化视图功能,但在生产环境中更推荐手动维护:

// 技术栈:Cassandra CQL
// 原始用户表
CREATE TABLE users (
    user_id uuid PRIMARY KEY,
    email text,
    username text,
    registration_date timestamp
);

// 手动维护的email索引表
CREATE TABLE users_by_email (
    email text,
    user_id uuid,
    PRIMARY KEY (email)
);

// 批量插入时使用批处理保持一致性
BEGIN BATCH
    INSERT INTO users (user_id, email, username, registration_date)
    VALUES (uuid(), 'user@example.com', 'john_doe', toTimestamp(now()));
    
    INSERT INTO users_by_email (email, user_id)
    VALUES ('user@example.com', user_id);
APPLY BATCH;

这种方式的优势在于:

  1. 更可控的写入性能
  2. 避免物化视图的一些已知问题
  3. 可以自定义同步策略

七、实战案例分析:电商系统设计

让我们看一个完整的电商系统数据模型:

// 技术栈:Cassandra CQL
// 产品目录
CREATE TABLE products (
    product_id uuid PRIMARY KEY,
    name text,
    price decimal,
    description text,
    categories set<text>,
    stock_counter int
);

// 按类别查询产品的物化视图
CREATE TABLE products_by_category (
    category text,
    product_id uuid,
    name text,
    price decimal,
    PRIMARY KEY (category, product_id)
);

// 用户购物车
CREATE TABLE shopping_carts (
    user_id uuid PRIMARY KEY,
    items map<uuid, int>  // 产品ID到数量的映射
);

// 订单历史
CREATE TABLE orders_by_user (
    user_id uuid,
    order_date timestamp,
    order_id uuid,
    items list<text>,  // 简化的订单项
    total decimal,
    PRIMARY KEY (user_id, order_date, order_id)
) WITH CLUSTERING ORDER BY (order_date DESC);

这个设计考虑了:

  1. 高频查询路径
  2. 写操作的分布
  3. 数据访问模式
  4. 业务增长需求

八、性能调优小贴士

最后分享几个实战中总结的优化技巧:

  1. 批量插入控制在5-50条语句之间
  2. 使用异步查询处理非关键路径操作
  3. 监控分区大小,保持在100MB以内
  4. 定期执行nodetool repair维护数据一致性
  5. 考虑使用SSTable压缩节省空间

记住,Cassandra不是万能的,它在以下场景表现最佳:

  • 高写入吞吐量
  • 地理分布式部署
  • 灵活的数据模式
  • 最终一致性可接受的场景