不使用Scan在DynamoDB中按两个条件查询数据的实现方案
实现方案
DynamoDB的Query原生不支持跨不同属性的OR条件查询,要避免Scan操作,你可以通过两次独立Query+结果去重的方式实现需求,全程不会触发全表扫描。
前置准备
你已经有了isValue作为分区键的GSI,还需要新增一个GSI支持按xdate范围查询:
- 新增GSI的分区键:可以设置一个所有记录都共用的固定值字段(比如所有记录新增
recordType字段,值固定为default)作为该GSI的分区键 - 新增GSI的排序键:设置为
xdate
核心逻辑
拆成两个互不干扰的Query查询,再合并结果去重:
- 第一次查询:使用已有的
isValueGSI,查询分区键值为TRUE的所有记录,得到所有满足isValue == true的数据集 - 第二次查询:使用新建的xdate GSI,查询分区键为固定值、排序键
xdate < 今日ISO格式日期的所有记录,得到所有满足xdate条件的数据集 - 合并两个数据集,按表主键(ID + SID)去重,避免同时满足两个条件的记录重复返回
TypeScript 代码示例(基于AWS SDK v3)
首先安装依赖:
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
代码实现:
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb"; // 初始化客户端 const client = new DynamoDBClient({ region: "你的AWS区域" }); const docClient = DynamoDBDocumentClient.from(client); const TABLE_NAME = "你的表名"; const XDATE_GSI_NAME = "你新建的xdate GSI名称"; const IS_VALUE_GSI_NAME = "已有的isValue GSI名称"; // 生成今日的ISO日期字符串,和表中存储格式保持一致 const todayIso = new Date().toISOString(); async function queryTargetData() { // 第一次查询:所有isValue == true的记录 const isValueQuery = new QueryCommand({ TableName: TABLE_NAME, IndexName: IS_VALUE_GSI_NAME, KeyConditionExpression: "isValue = :val", ExpressionAttributeValues: { ":val": true } }); const isValueRes = await docClient.send(isValueQuery); const isValueItems = isValueRes.Items || []; // 第二次查询:所有xdate < 今日的记录 const xdateQuery = new QueryCommand({ TableName: TABLE_NAME, IndexName: XDATE_GSI_NAME, KeyConditionExpression: "recordType = :type AND xdate < :today", ExpressionAttributeValues: { ":type": "default", ":today": todayIso } }); const xdateRes = await docClient.send(xdateQuery); const xdateItems = xdateRes.Items || []; // 合并去重,按主键ID+SID作为唯一标识 const itemMap = new Map(); [...isValueItems, ...xdateItems].forEach(item => { const key = `${item.ID}#${item.SID}`; if (!itemMap.has(key)) { itemMap.set(key, item); } }); return Array.from(itemMap.values()); } // 调用示例 queryTargetData().then(console.log).catch(console.error);
注意事项
- 如果单次查询返回数据量超过1MB,需要判断返回结果中的
LastEvaluatedKey,循环分页查询完所有符合条件的记录再合并 - 如果单表数据量超过10GB,固定值作为分区键的GSI可能出现热分区问题,可以将固定值拆分为多个分片(比如
default#1、default#2...共10个),写入时随机分配分片值,查询时并行查询所有分片的结果再合并即可解决 - 必须保证xdate存储的是ISO8601格式的字符串,才能保证字符串比较的结果和日期先后顺序一致
内容的提问来源于stack exchange,提问作者user2427573
相关产品推荐
相关产品推荐

