Cassandra 分頁與傳統關聯式資料庫根本不同:**無 OFFSET/LIMIT 支援**,僅支援**游標式分頁**。下一頁查詢依賴上一頁最後一筆記錄的完整主鍵(PagingState),僅能實現「上一頁/下一頁」,無法直接跳轉第 N 頁。
// Cluster 層級預設
Cluster cluster = Cluster.builder()
.addContactPoint("127.0.0.1")
.withQueryOptions(new QueryOptions().setFetchSize(2000))
.build();
// 或運行時修改
cluster.getConfiguration().getQueryOptions().setFetchSize(2000);
// Statement 層級覆蓋(優先級最高)
Statement stmt = new SimpleStatement("SELECT * FROM users");
stmt.setFetchSize(50);
注意:Fetch Size 非精確值,Cassandra 可能返回略多或略少記錄。
ResultSet rs = session.execute("SELECT * FROM large_table");
for (Row row : rs) {
// 提前預取下一頁,避免迭代阻塞等待網路
if (rs.getAvailableWithoutFetching() == 100 && !rs.isFullyFetched()) {
rs.fetchMoreResults(); // 非同步預取
}
process(row);
}
關鍵 API:
getAvailableWithoutFetching():當前頁面剩餘未讀筆數isFullyFetched():是否已抓取所有頁面fetchMoreResults():主動觸發下一頁抓取(非同步)適用於無狀態 Web 服務:用戶點擊「下一頁」時,攜帶上一頁的 PacingState 繼續查詢。
ResultSet rs = session.execute(stmt);
// 迭代當前頁...
PagingState pagingState = rs.getExecutionInfo().getPagingState();
// 序列化為字串或位元組陣列
String stateStr = pagingState.toString(); // URL 參數傳遞
byte[] stateBytes = pagingState.toBytes(); // Session/Redis 儲存
// 從請求參數恢復
PagingState restored = PagingState.fromString(request.getParameter("pagingState"));
// 或
PagingState restored = PagingState.fromBytes(sessionBytes);
// 套用至新 Statement
Statement stmt = new SimpleStatement("SELECT * FROM users");
stmt.setFetchSize(20);
stmt.setPagingState(restored); // 關鍵:從上一頁結束處繼續
ResultSet nextPage = session.execute(stmt);
限制:
public interface ICassandraPage {
Map page(PagingState pagingState);
}
public class CassandraPageDao implements ICassandraPage {
private static final Session session = SessionRepository.getSession();
private static final String CQL = "SELECT * FROM mycas.teacher;";
private static final int PAGE_SIZE = 2; // 示例用小頁面
@Override
public Map page(PagingState pagingState) {
Map result = new HashMap<>(2);
List teachers = new ArrayList<>(PAGE_SIZE);
Statement stmt = new SimpleStatement(CQL);
stmt.setFetchSize(PAGE_SIZE);
if (pagingState != null) stmt.setPagingState(pagingState);
ResultSet rs = session.execute(stmt);
result.put("pagingState", rs.getExecutionInfo().getPagingState());
// 關鍵:不依賴 PAGE_SIZE,使用 getAvailableWithoutFetching()
int remaining = rs.getAvailableWithoutFetching();
for (Row row : rs) {
teachers.add(extractTeacher(row));
if (--remaining == 0) break; // 精確控制本頁筆數
}
result.put("teachers", teachers);
return result;
}
private Teacher extractTeacher(Row row) {
Teacher t = new Teacher();
t.setId(row.getInt("id"));
t.setName(row.getString("name"));
t.setAge(row.getInt("age"));
t.setHeight(row.getInt("height"));
t.setAddress(row.getString("address"));
return t;
}
}
public class PagingTest {
public static void main(String[] args) {
ICassandraPage dao = new CassandraPageDao();
Map result = dao.page(null);
PagingState state = (PagingState) result.get("pagingState");
System.out.println("第1頁: " + result.get("teachers"));
while (state != null) {
System.out.println("==============================================");
result = dao.page(state);
state = (PagingState) result.get("pagingState");
System.out.println("下一頁: " + result.get("teachers"));
}
}
}
偏移查詢需跳過前 N 筆資料,Cassandra 為分散式架構,跳過操作需協調多節點,**效能與跳過筆數呈線性正比**,大偏移會嚴重拖慢叢集。
場景:每頁 10 筆,需跳至第 12 頁(第 110-119 筆),Fetch Size 50。
代價:多次網路往返、傳輸無用資料、記憶體壓力。Fetch Size 需平衡:太小增加查詢次數,太大傳輸冗餘資料。
專案場景:Cassandra 儲存億級資料,Elasticsearch 提供二級索引。需定期全表掃描修復索引一致性。
// 修復任務核心邏輯
PagingState state = null;
do {
Map page = cassandraDao.page(state);
List records = (List) page.get("records");
state = (PagingState) page.get("pagingState");
for (Record r : records) {
Doc esDoc = esClient.get(r.getId());
if (esDoc == null) esClient.index(r); // 缺失→新增
else if (!esDoc.equals(r)) esClient.update(r); // 不一致→修復
}
} while (state != null);
此場景完美契合 Cassandra 游標分頁:單向流式處理、無需隨機跳頁、記憶體可控。
| 分頁需求 | 推薦方案 | 適用場景 |
|---|---|---|
| 單向順序讀取(報表、同步、匯出) | Cassandra 原生 PagingState | 大資料量、記憶體受限、無狀態服務 |
| 雙向翻頁(上一頁/下一頁) | 儲存前一頁 PagingState | Web 分頁介面、用戶操作歷程 |
| 隨機跳頁(第 1→100 頁) | **避免使用 Cassandra**,改用 ES/Solr | 使用者導向搜尋、後台管理介面 |
| 小資料量(<10萬筆)需跳頁 | 客戶端模擬偏移(謹慎評估) | 內部工具、管理介面、資料量可控 |
**核心原則**:Cassandra 專為寫入密集、大規模讀取設計。分頁機制反映其分散式本質——**順序流式處理優於隨機存取**。架構設計時應順應此特性,而非強行對抗。