DynamoDB 中的分页
DynamoDB 绝不会在一次调用里返回"全部"结果。一次 Query 或 Scan 最多返回 1 MB 数据,然后交给你一个 LastEvaluatedKey 用来续读。把分页做对,意味着围绕这个键循环——而不是围绕一个计数器。
DynamoDB 中的分页是如何工作的?
一次 Query 或 Scan 每次调用最多返回 1 MB,然后交回一个 LastEvaluatedKey。要翻页,你把那个键作为下一次调用的 ExclusiveStartKey 传入,循环下去,直到 DynamoDB 不再返回键。没有页码、没有总数,而 Limit 限制的是被评估的项——不是被返回的项。
let key;
do {
const out = await client.send(new QueryCommand({...params, ExclusiveStartKey: key}));
process(out.Items);
key = out.LastEvaluatedKey;
} while (key);当 LastEvaluatedKey 为 undefined 时,你就到头了。把它作为 ExclusiveStartKey 传回去,即可取下一片。
控制流是一个单一循环,只有在键缺失时才退出:
每一趟要么从返回的键续读,要么停下——没有计数器。
Limit 不是页大小
Limit 限制的是 DynamoDB 评估多少项,而不是它在应用 FilterExpression 之后返回多少项。一个 Limit: 25 且带筛选的查询可能只返回 3 个项,却仍然交给你一个 LastEvaluatedKey——你必须一直翻页,直到键为空,哪怕某一页看起来很短。非空的 LastEvaluatedKey 也从不承诺还有更多匹配的项;只有缺失的键才证明你已到头。
让 SDK 替你分页
两个 SDK 都封装了上面的循环,让你可以直接迭代页:
// AWS SDK for JavaScript v3
import {paginateQuery} from '@aws-sdk/lib-dynamodb';
for await (const page of paginateQuery({client}, params)) {
process(page.Items);
}# boto3
paginator = client.get_paginator('query')
for page in paginator.paginate(**params):
process(page['Items'])没有页码
DynamoDB 没有总数、没有随机页访问——你无法跳到"第 7 页",也无法在不重放游标的情况下往回翻。要围绕无限滚动 /"加载更多"来设计 UI,而不是带编号的页。(一个 Select: 'COUNT' 查询为了计数,仍然会读取——并按每一个匹配的项计费。)
面向 API 的无状态游标
LastEvaluatedKey 只不过是最后一个项的键属性。把它 base64 编码,作为一个不透明的 nextToken 交给客户端;在下一次请求时把它解码回 ExclusiveStartKey。没有服务端游标状态。
那个 token 是 DynamoDB-JSON——用 DynamoDB-JSON 转换器肉眼看或手工造一个。如果你分页是为了绕开某个 Scan,那通常是个信号:改为添加一个索引才对。
要完全跳过手写这个循环,查询构建器会组装完整的 Query/Scan 请求,并输出可运行的 SDK v3、CLI 或 boto3 程序——分页循环也包含在内。
试用 DynoTable,可视化地翻阅查询结果,而游标由它替你跟踪。