排序与分页 #

一、排序概述 #

1.1 排序特点 #

Cassandra的排序有以下特点:

text
排序特点:

✓ 仅支持聚簇列排序
✓ 排序方向必须与表定义一致
✓ 默认按聚簇列升序
✓ 可通过CLUSTERING ORDER指定

1.2 排序限制 #

text
排序限制:

不支持
├── 非聚簇列排序
├── 多方向排序(部分支持)
└── ORDER BY + ALLOW FILTERING

限制
├── 必须指定分区键
└── 排序方向受表定义约束

二、CLUSTERING ORDER #

2.1 表定义排序 #

sql
-- 默认升序
CREATE TABLE events_asc (
    device_id TEXT,
    event_time TIMESTAMP,
    data TEXT,
    PRIMARY KEY (device_id, event_time)
);
-- event_time升序排列

-- 指定降序
CREATE TABLE events_desc (
    device_id TEXT,
    event_time TIMESTAMP,
    data TEXT,
    PRIMARY KEY (device_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
-- event_time降序排列

-- 多列排序
CREATE TABLE multi_sort (
    user_id UUID,
    event_date DATE,
    event_time TIMESTAMP,
    data TEXT,
    PRIMARY KEY (user_id, event_date, event_time)
) WITH CLUSTERING ORDER BY (event_date DESC, event_time ASC);
-- event_date降序,event_time升序

2.2 排序方向 #

text
排序方向规则:

单聚簇列
├── ASC:升序(默认)
└── DESC:降序

多聚簇列
├── 每列可独立指定方向
├── 必须按顺序指定
└── 方向在表创建时确定

三、ORDER BY使用 #

3.1 基本排序 #

sql
-- 按聚簇列排序
SELECT * FROM events 
WHERE device_id = 'device-001'
ORDER BY event_time DESC;

-- 多列排序
SELECT * FROM multi_sort 
WHERE user_id = ?
ORDER BY event_date DESC, event_time ASC;

3.2 排序规则 #

text
ORDER BY规则:

必须匹配表定义
├── 方向必须一致
├── 列顺序必须一致
└── 不能只指定部分列

示例(CLUSTERING ORDER BY (event_date DESC, event_time ASC)):
✓ ORDER BY event_date DESC
✓ ORDER BY event_date DESC, event_time ASC
✗ ORDER BY event_date ASC(方向不一致)
✗ ORDER BY event_time ASC(跳过event_date)

3.3 排序示例 #

sql
-- 表定义
CREATE TABLE orders (
    user_id UUID,
    order_date DATE,
    order_id UUID,
    amount DECIMAL,
    PRIMARY KEY (user_id, order_date, order_id)
) WITH CLUSTERING ORDER BY (order_date DESC, order_id ASC);

-- 有效排序
SELECT * FROM orders WHERE user_id = ?
ORDER BY order_date DESC;

SELECT * FROM orders WHERE user_id = ?
ORDER BY order_date DESC, order_id ASC;

-- 无效排序
-- SELECT * FROM orders WHERE user_id = ?
-- ORDER BY order_date ASC;  -- 方向不一致

-- SELECT * FROM orders WHERE user_id = ?
-- ORDER BY amount DESC;  -- 非聚簇列

四、分页机制 #

4.1 LIMIT分页 #

sql
-- 限制返回行数
SELECT * FROM users LIMIT 10;

-- 组合条件
SELECT * FROM orders 
WHERE user_id = ?
LIMIT 100;

4.2 基于主键分页 #

sql
-- 第一页
SELECT * FROM orders 
WHERE user_id = ?
LIMIT 100;

-- 第二页(使用上一页最后一条记录的主键)
SELECT * FROM orders 
WHERE user_id = ?
AND (order_date, order_id) > (?, ?)
LIMIT 100;

4.3 Token分页 #

sql
-- 使用Token分页
SELECT * FROM users 
WHERE token(user_id) > token(?)
LIMIT 100;

-- 组合排序
SELECT * FROM users 
WHERE token(user_id) > token(?)
LIMIT 100;

五、驱动分页 #

5.1 Java驱动 #

java
// 自动分页
Statement statement = QueryBuilder.select()
    .all()
    .from("orders")
    .where(QueryBuilder.eq("user_id", userId))
    .setFetchSize(100);

ResultSet resultSet = session.execute(statement);

// 迭代时自动获取下一页
for (Row row : resultSet) {
    // 处理每一行
    System.out.println(row.getUUID("order_id"));
}

// 手动分页
ResultSet resultSet = session.execute(statement);
while (!resultSet.isFullyFetched()) {
    resultSet.fetchMoreResults();
    // 处理当前页
    for (Row row : resultSet) {
        // 处理行
    }
}

5.2 Python驱动 #

python
from cassandra.query import SimpleStatement

# 自动分页
query = "SELECT * FROM orders WHERE user_id = %s"
statement = SimpleStatement(query, fetch_size=100)

rows = session.execute(statement, (user_id,))
for row in rows:
    # 处理每一行
    print(row.order_id)

# 手动分页
rows = session.execute(statement, (user_id,))
while rows:
    for row in rows.current_rows:
        # 处理行
        pass
    if rows.has_more_pages:
        rows = session.execute(statement, (user_id,), paging_state=rows.paging_state)
    else:
        break

5.3 Go驱动 #

go
// 自动分页
query := session.Query(
    "SELECT * FROM orders WHERE user_id = ?",
    userId,
).PageSize(100)

iter := query.Iter()
for {
    row := map[string]interface{}{}
    if !iter.MapScan(row) {
        break
    }
    // 处理行
}

六、分页最佳实践 #

6.1 分页大小选择 #

text
分页大小建议:

小数据
├── fetch_size: 100-500
└── 适合交互式查询

中等数据
├── fetch_size: 500-1000
└── 适合批量处理

大数据
├── fetch_size: 1000-5000
└── 适合数据导出

注意
├── 过小:频繁请求
├── 过大:内存压力
└── 根据数据大小调整

6.2 分页优化 #

text
分页优化建议:

1. 使用合适fetch_size
   └── 平衡请求次数和内存

2. 避免深度分页
   └── 使用Token范围分页

3. 监控分页性能
   └── 使用tracing分析

4. 处理分页状态
   └── 保存paging_state用于断点续传

七、排序与分页组合 #

7.1 排序分页 #

sql
-- 排序后分页
SELECT * FROM orders 
WHERE user_id = ?
ORDER BY order_date DESC
LIMIT 100;

-- 排序后翻页
SELECT * FROM orders 
WHERE user_id = ?
AND order_date < ?
ORDER BY order_date DESC
LIMIT 100;

7.2 复合主键分页 #

sql
-- 复合主键分页
SELECT * FROM orders 
WHERE user_id = ?
AND (order_date, order_id) > (?, ?)
ORDER BY order_date DESC, order_id ASC
LIMIT 100;

八、性能考虑 #

8.1 排序性能 #

text
排序性能考虑:

聚簇列排序
├── 数据已按聚簇列排序存储
├── 无额外排序开销
└── 性能最优

反向排序
├── 需要反向遍历
├── 性能略低
└── 但仍可接受

8.2 分页性能 #

text
分页性能考虑:

浅分页
├── 性能好
└── 前几页响应快

深度分页
├── 需要跳过大量数据
├── 性能下降
└── 考虑Token范围分页

建议
├── 限制最大页数
├── 使用Token分页
└── 避免跳页查询

九、总结 #

排序与分页要点:

  1. 排序限制:仅支持聚簇列排序
  2. 方向一致:ORDER BY必须与表定义一致
  3. 分页方式:LIMIT、主键分页、Token分页
  4. 驱动分页:使用fetch_size自动分页
  5. 分页大小:根据数据大小选择合适值
  6. 避免深度分页:使用Token范围分页

下一步,让我们学习索引!

最后更新:2026-03-27