如何使用 dynamodb aws 中的索引有效地检索记录
Posted
技术标签:
【中文标题】如何使用 dynamodb aws 中的索引有效地检索记录【英文标题】:How to retrieve records efficiently using indices in dynamodb aws 【发布时间】:2021-08-28 16:49:38 【问题描述】:我想获取 5 班到 11 班所有在 x1 和 x2 分数之间的男女学生的记录数学,科学 y1 和 y2 分数之间,英语 z1 和 z2 分数之间以及 分数之间w1 和 w2 标记在社交...
我的模型如下所示:
type StudentMarks
@model
@key(
name: "filterbyClassAndMarks"
fields: [
"gender"
"classCode"
"mathsMarks"
"socialMarks"
"englishMarks"
"scienceMarks"
]
queryField: "filterbyClassAndMarks"
)
@auth(
rules: [
allow: private, provider: iam, operations: [read]
allow: public, provider: iam, operations: [read]
]
)
id: ID!
name: String
gender: String
classCode: String
mathsMarks: String
socialMarks: String
englishMarks: String
scienceMarks: String
以性别作为分区键(hashkey)和带有classCode、mathMarks、socialMarks、scienceMarks、englishMarks字段is showing item count as 0.的复合排序/范围键创建的GSI@
我正在尝试使用以下 graphql 进行查询:
const listOfStudents = await API.graphql(
query: queries.filterbyClassAndMarks,
variables:
gender: "m",
classCodeMathsMarksSocialMarksEnglishMarksScienceMarks:
between: [
classCode: "05",
mathsMarks: "06",
scienceMarks: "07",
englishMarks: "04",
socialMarks: "05",
,
classCode: "11",
mathsMarks: "90",
scienceMarks: "91",
englishMarks: "95",
socialMarks: "92",
,
],
,
,
authMode: "AWS_IAM",
);
This table 有 11 条记录应返回 4 条记录,如绿色所示
云看日志如下:
"logType": "RequestMapping",
"path": [
"filterbyClassAndMarks"
],
"fieldName": "filterbyClassAndMarks",
"resolverArn": "arn:aws:appsync:ap-south-1:488377001042:apis/4tbr6xolzjfctl5tdbiur7jqnu/types/Query/resolvers/filterbyClassAndMarks",
"requestId": "5de45e66-1a5d-44dd-80cc-1a5b93a3c3aa",
"context":
"arguments":
"gender": "m",
"classCodeMathsMarksSocialMarksEnglishMarksScienceMarks":
"between": [
"classCode": "05",
"mathsMarks": "06",
"socialMarks": "05",
"englishMarks": "04",
"scienceMarks": "07"
,
"classCode": "11",
"mathsMarks": "90",
"socialMarks": "92",
"englishMarks": "95",
"scienceMarks": "91"
]
,
,
"stash": ,
"outErrors": []
,
"fieldInError": false,
"errors": [],
"parentType": "Query",
"graphQLAPIId": "4tbr6xolzjfctl5tdbiur7jqnu",
"transformedTemplate": "\"version\":\"2018-05-29\",\"operation\":\"Query
\",\"limit\":100,\"query\":\"expression\":\"#gender = :gender AND #sortKey
BETWEEN :sortKey0 AND :sortKey1\",\"expressionNames\":\"#gender\":\"gender
\",\"#sortKey\":\"classCode#mathsMarks#socialMarks#englishMarks#scienceMarks
\",\"expressionValues\":\":gender\":\"S\":\"m\",\":sortKey0\":\"S
\":\"05#06#05#04#07\",\":sortKey1\":\"S\":\"11#90#92#95#91\",\"index
\":\"filterbyClassAndMarks\",\"scanIndexForward\":true"
在结果中显示扫描计数为 0:
"result":
"items": [],
"scannedCount": 0
,
最终响应为成功,结果为空,如下所示:
Object data: …
data: Object filterbyClassAndMarks: …
filterbyClassAndMarks: Object items: [], nextToken: null
items: Array []
length: 0
<prototype>: Array []
nextToken: null
Array []
复合排序键的键条件是可能的as per aws documentation...我不明白我到底错过了什么。可能 GSI 索引没有正确配置/写入。
由于大多数时候结果不到表中记录的 1% 并且是读取密集型的,因此扫描/读取所有记录并过滤它们是一种非常幼稚的解决方案。需要使用索引或其他方式获得更好的解决方案。
第二次编辑:Example of expected behavior of applying hash key and compound sort key before filtering.。该示例仅用于强调预期行为,并未指出由于散列键或复合排序键而未读取的记录的大致百分比。
【问题讨论】:
我不知道您是否可以创建满足任意查询的复合排序键。如果没有等效的扫描/过滤器,您将如何在 RDBMS 中执行此操作? 我不知道它是如何在 RDBMS 中完成的,但是根据aws documentation,复合排序键的键条件是可能的 【参考方案1】:DynamoDB 不擅长支持任意属性列表中的即席查询。如果您想使用未定义(或大量)的属性(例如,通过 mathMarks、socialMarks、scienceMarks、姓名、性别等获取学生)获取相同的项目,则最好使用 DynamoDB 以外的其他内容。
编辑:
您已使用从根本上改变访问模式的信息更新了您的问题。你一开始说
我想从 dynamodb 获取所有数学成绩超过 x1、科学成绩超过 x2、社交成绩低于 y1 和英语成绩低于 y2 的男性或女性学生的记录...
后来将这种访问模式改为说(强调我的)
我想获取从 5 班到 11 班数学成绩介于 x1 和 x2 之间、科学成绩介于 y1 和 y2 之间、z1 和 z2 之间的所有男性或女性学生的记录英语分数和社交分数在w1和w2之间
根据性别和班级范围对数据进行分区可能允许您实现这种访问模式。您还从示例模式中删除了... other variables
,这表明您可能有一个固定的标记列表,包括数学、科学、社交和英语。这不如性别和阶级重要,但听我说:)
您在最初的问题中没有提到它,但您的数据表明您有 fetch Student by ID
访问模式。所以我首先通过 ID 定义学生:
分区键是 STUDENT#student_id,排序键是 A
。我有时使用“METADATA”或“META”作为排序键,但“A”又好又短。
为了支持您的主要访问模式,我创建了一个名为 GSI1
的全局二级索引,其 PK 和 SK 属性为 GSI1PK
和 GSI1SK
。我分配了 GSI1PK STUDENTS#gender
,而 GSISK 是 class
属性。
这会按性别和类别划分您的数据。要进一步缩小结果范围,您需要对各种标记使用过滤器。例如,如果您想获取所有具有特定分数的 5 到 9 年级的学生,您可以执行以下操作(在 DynamoDB 伪代码中):
QUERY from GSI1 where PK=STUDENTS#M SK BETWEEN 05 and 09
FILTER englishMark BETWEEN 009 and 050 AND
mathsMark BETWEEN 050 and 075 AND
scienceMark BETWEEN 045 and 065 AND
socialMark BETWEEN 020 and 035 AND
DynamoDB 中的过滤并不像大多数人想象的那样工作。过滤时,DynamoDB:
-
从表中读取项目
应用过滤器删除不匹配的项目
退货
如果您有一个大表并且正在过滤非常小的数据集,这可能会导致糟糕的性能(和成本)。例如,如果您对 TB 级数据执行 scan
操作并应用过滤器来识别单个项目,那么您将遇到麻烦。
但是,在某些情况下,过滤是一个很好的解决方案。一个例子是当您可以使用分区键和排序键来缩小您正在使用的数据集在应用过滤器之前。在上面的示例中,我将您的数据按性别划分(也许将您的搜索空间分成两半),然后再按类别进一步缩小项目范围。您剩下的数据量可能足够小,可以有效地过滤掉剩余的项目。
但是,我要指出,性别具有相当低的基数。也许有更多关于您的访问模式的细节可以帮助解决这个问题。例如,也许您可以按小学/中学对学生进行分组,并创建一个 STUDENTS#F#1-5 的 PK。也许您可以按学区或邮政编码对它们进行分组?在使用 NoSQL 数据库时,没有关于访问模式的详细信息太具体了!
这个练习的重点是说明两点:
-
DynamoDB 中的过滤最好通过选择一个好的主键来实现。
使用 DynamoDB 过滤机制最好用于较小的数据子集。不要害怕在正确的地方使用它!
【讨论】:
感谢您对 pk、sk 和过滤概念的精彩解释。抱歉,如果我没有正确提出问题。我的重点是构建具有 2 个或更多变量的复合排序键并应用键条件,以便尽可能减少过滤前的结果子集,如我的 SECOND EDIT 中所述。 复合排序键不适用于此访问模式。您不能将多个数值连接在一起并有选择地对每个数值执行愤怒查询。 @Phoenix 如果这对您有用,请考虑接受它作为答案,以便其他人可以从回复中受益。以上是关于如何使用 dynamodb aws 中的索引有效地检索记录的主要内容,如果未能解决你的问题,请参考以下文章
AWS AppSync:如何通过 DynamoDB 返回有效的 JSON
如何从无服务器应用程序安全地连接到 AWS DynamoDB 或其他 aws 数据库