模块 5:部分规范化
学习如何实现部分规范化
概述
在上一个模块中,您在表中添加了一个反向索引。反向索引为关系型数据添加了额外的查询模式。但是,我们发现还是遇到了问题。虽然我们可以查询指定 User 实体的所有 Friendship 实体,但 Friendship 实体并不包含被关注用户的信息。
在本模块中,您将学习部分规范化。
时长
20 分钟
部分规范化
在第二个模块中,我们了解了在使用 DynamoDB 建模时,不能伪造关系模式。关系模型的一个核心是数据规范化,规范化可以帮助您避免在多个位置复制数据。在关系型数据库中,规范化是一种不错的模式,但在查询时可能需要通过连接 (join) 来重新组合数据,而数据 join 的消耗比较高。
使用 DynamoDB 时,通常需要对数据进行非规范化处理。非规范化有助于避免数据 join,且能提高查询性能。为此,您可以将属性从一个数据项复制到引用该数据项的另一个数据项,以避免在查询期间需要同时查询这两个数据项。
但是,有时非规范化会使数据模型复杂化。例如,数据模型有一个 Friendship 实体,该实体既引用被关注的用户,也引用关注者用户。在创建 Friendship 实体时,您可以将每个 User 实体的所有属性复制到 Friendship 实体中。然后,当您检索 Friendship 实体时,也将获得关于这两个用户的所有详细信息。
每当用户更改个人资料中的信息时,这可能会造成问题。例如,如果用户更换了头像,则包含该用户的每个 Friendship 实体中的数据都将过时。每当有更新时,您都需要更新包含该用户的每个 Friendship 实体。
在下面的步骤中,您将了解如何使用部分规范化和调用 BatchGetItem API 来处理这种情况。
操作步骤
-
使用部分规范化查找关注的用户
在此步骤中,您将了解如何查找关注的用户。这些用户是某特定用户在应用程序中关注的用户。您还将了解如何检索被关注用户的所有数据。
正如本模块简介中所述,您可能希望对 Friendship 和 User 数据使用部分规范化技术。您可以使用 BatchGetItem API 检索 Friendship 实体中用户的信息,而不必将每个用户的全部信息存储在 Friendship 实体中。
在您下载的代码中,application/ 目录下有一个名为 find_and_enrich_following_for_user.py 文件。该脚本的内容如下所示。
import boto3 from entities import User dynamodb = boto3.client('dynamodb') USERNAME = "haroldwatkins" def find_and_enrich_following_for_user(username): friend_value = "#FRIEND#{}".format(username) resp = dynamodb.query( TableName='quick-photos', IndexName='InvertedIndex', KeyConditionExpression="SK = :sk", ExpressionAttributeValues={":sk": {"S": friend_value}}, ScanIndexForward=True ) keys = [ { "PK": {"S": "USER#{}".format(item["followedUser"]["S"])}, "SK": {"S": "#METADATA#{}".format(item["followedUser"]["S"])}, } for item in resp["Items"] ] friends = dynamodb.batch_get_item( RequestItems={ "quick-photos": { "Keys": keys } } ) enriched_friends = [User(item) for item in friends['Responses']['quick-photos']] return enriched_friends follows = find_and_enrich_following_for_user(USERNAME) print("Users followed by {}:".format(USERNAME)) for follow in follows: print(follow)
find_and_enrich_following_for_user 函数类似于您在上一个模块中使用的 find_follower_for_user 函数。该函数接受用户名,以帮助您查询该用户关注的用户。该函数先使用反向索引发出 Query 请求,查找指定用户名关注的所有用户。然后,组合一个 BatchGetItem 来获取每个被关注用户的完整 User 实体,并返回这些实体。
这会向 DynamoDB 发出两个请求,而不是理想下的一个请求。不过,可以实现相当复杂的访问模式,并避免每次更新用户个人资料时都要更新 Friendship 实体。这种部分规范化非常适合满足您的建模需求。
在终端运行以下命令,执行该脚本。
python application/find_and_enrich_following_for_user.py
控制台应输出指定用户关注的用户列表。
Users followed by haroldwatkins: User<ppierce -- Ernest Mccarty> User<vpadilla -- Jonathan Scott> User<david25 -- Abigail Alvarez> User<jacksonjason -- John Perry> User<chasevang -- Leah Miller> User<frankhall -- Stephanie Fisher> User<nmitchell -- Amanda Green> User<tmartinez -- Kristin Stevens> User<natasha87 -- Walter Carlson> User<geoffrey32 -- Mary Martin>
请注意,您现在处理的是 User 实体,而不是 Friendship 实体。User 实体中包含用户的最完整、最新信息。虽然需要发出两个请求才能实现目的,但与完全非规范化以及由此产生的数据完整性问题相比,这仍然是一个更好的选择。
总结
在本模块中,我们了解了如何通过部分规范化和调用 BatchGetItem API 在保持低查询请求的同时,维护对象之间的数据完整性。
在下一个模块中,我们将使用 DynamoDB 事务来实现添加照片互动或关注用户。