我有一个场景:按年级查询学校的学生列表,然后使用该信息执行一些其他任务,例如为每个学生打印证书
我使用无服务器框架来处理这个Lambda的场景:
const queryStudent = async (_school_id, _year) => {
var params = {
TableName: `schoolTable`,
KeyConditionExpression: 'partition_key = _school_id AND begins_with(sort_key, _year)',
};
try {
let _students = [];
let items;
do {
items = await dynamoClient.query(params).promise();
_students = items.Items;
params.ExclusiveStartKey = items.LastEvaluatedKey;
} while (typeof items.LastEvaluatedKey != 'undefined');
return _students;
} catch (e) {
console.log('Error: ', e);
}
};
const mainHandler = async (event, context) => {
…
let students = await queryStudent(body.school_id, body.year);
await printCerificate(students)
…
}
到目前为止,它在大约5k个学生(只是样本数据)中运行良好
我的担忧:它是在DynamoDB中查询大数据的可伸缩解决方案吗?
据我所知,Lambda有有限的执行时间,如果学生人数增加到一百万,上述解决方案是否仍然有效?
对于这种情况,任何最佳实践方法都是非常赞赏和欢迎的。
如果您考虑扩展,这里有多个潜在的瓶颈,您可以解决:
- 热分区:现在,您将单个学校的所有学生存储在单个项目集合中。这意味着它们将存储在引擎盖下的单个存储节点上。如果对此运行许多查询,可能会遇到吞吐量限制。你可以在这里使用读/写分片之类的东西,例如,给分区键添加后缀,并对数据进行散射-聚集。
- λ:查询:如果您想查询一百万条记录,这将花费时间。Lambda可能无法在15分钟内完成(和处理),如果它在完全完成之前失败,您就会丢失已经完成的信息。您可以为此做检查点,即将LastEvaluatedKey保存在其他地方,并检查它是否存在于新的Lambda调用中,并从那里开始。
- λ:处理:您似乎在同一Lambda函数中为每个学生在一年内创建证书。如果它是一个同步过程,并且你有一百万学生,那么这个解决方案就无法扩展。如果东西失败了,你还必须考虑重试,并在代码中构建该逻辑。
如果你想把它扩展到每所学校一百万名学生,我可能会把架构改成这样:
你有一个步骤函数要打印证书时调用的。这个阶跃函数只有一个Lambda函数。Lambda函数跨分片分区键查询表,并将每个学生写入SQS队列,用于证书打印任务。如果Lambda注意到,它接近运行时限制,它返回LastEvaluatedKey
,步进函数识别它并使用此偏移量再次启动函数。SQS队列可以调用Lambda函数来实际创建证书,可能是批量创建的。
这样可以将查询与处理解耦,并且还以SQS/Lambda集成的形式为失败的任务提供内置的重试逻辑。您还包括跨多个项的查询的检查点。
实现这一点需要更多的努力,所以我首先要弄清楚,每所学校每年100万学生是否是一个现实的数字:-)