使用 Amazon DynamoDB 创建和管理非关系型数据库

模块 3:查询和全局二级索引

您将演练一些使用 DynamoDB 在一个 API 调用中检索多个条目的简单示例

概览

在本模块中,您将演练一些使用 DynamoDB 在一个 API 调用中检索多个条目的简单示例。您还将了解如何使用二级索引针对 DynamoDB 表启用其他查询模式。

应用程序用例

在模块 2 中,您学习了如何通过使用 GetItem API 调用从 DynamoDB 表中检索单本图书。这种访问模式非常有用,但您的应用程序还需要能够在一个调用中检索多个条目。例如,您可能想要检索由 John Grisham 撰写的所有图书,以便可以向用户显示它们。在本模块的步骤 1 中,您将使用 Query API 检索特定作者的所有图书。

用于获取单本图书的 GetItem API 调用和用于检索作者所有图书的 Query API 调用均使用 Books 表中指定的主键。但是,您可能希望启用其他访问模式,例如检索历史或传记等特定类别中的所有图书。Category 不是表主键的一部分,但您可以创建二级索引以允许使用其他访问模式。您将在本模块的步骤 2 和 3 中创建和查询二级索引。

 完成所需时间

15 分钟

执行步骤

  • 如果您的表使用复合主键,可以使用 Query API 调用来检索具有相同哈希键的所有条目。对于您的应用程序,这意味着您可以检索具有相同 Author 属性的所有图书。

    在 AWS Cloud9 终端,运行以下命令。

    $ python query_items.py
    

    此命令运行以下脚本,检索由 John Grisham 编写的所有图书。

    import boto3
    from boto3.dynamodb.conditions import Key
    
    # boto3 is the AWS SDK library for Python.
    # The "resources" interface allows for a higher-level abstraction than the low-level client interface.
    # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html
    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
    table = dynamodb.Table('Books')
    
    # When making a Query API call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query.
    # You’re using the Key object from the Boto 3 library to specify that you want the attribute name ("Author")
    # to equal "John Grisham" by using the ".eq()" method.
    resp = table.query(KeyConditionExpression=Key('Author').eq('John Grisham'))
    
    print("The query returned the following items:")
    for item in resp['Items']:
        print(item)

    运行该脚本后,您应当会看到 John Grisham 的两本图书:The Firm 和 The Rainmaker

    $ python query_items.py
    The query returned the following items:
    {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'}

    在 DynamoDB 中,常见模式是通过单个调用来检索多个条目,这可以通过 Query API 调用轻松完成。

  • DynamoDB 允许您创建二级索引以考虑表的其他数据访问模式。二级索引是为 DynamoDB 表提高查询灵活性的强大方法。

    DynamoDB 有两种二级索引:全局二级索引和局部二级索引。在本部分中,您将向 Category 属性中添加一个全局二级索引,该索引将允许您检索特定类别中的所有图书。

    以下示例脚本向现有表添加全局二级索引。

    import boto3
    
    # Boto3 is the AWS SDK library for Python.
    # You can use the low-level client to make API calls to DynamoDB.
    client = boto3.client('dynamodb', region_name='us-east-1')
    
    try:
        resp = client.update_table(
            TableName="Books",
            # Any attributes used in your new global secondary index must be declared in AttributeDefinitions
            AttributeDefinitions=[
                {
                    "AttributeName": "Category",
                    "AttributeType": "S"
                },
            ],
            # This is where you add, update, or delete any global secondary indexes on your table.
            GlobalSecondaryIndexUpdates=[
                {
                    "Create": {
                        # You need to name your index and specifically refer to it when using it for queries.
                        "IndexName": "CategoryIndex",
                        # Like the table itself, you need to specify the key schema for an index.
                        # For a global secondary index, you can use a simple or composite key schema.
                        "KeySchema": [
                            {
                                "AttributeName": "Category",
                                "KeyType": "HASH"
                            }
                        ],
                        # You can choose to copy only specific attributes from the original item into the index.
                        # You might want to copy only a few attributes to save space.
                        "Projection": {
                            "ProjectionType": "ALL"
                        },
                        # Global secondary indexes have read and write capacity separate from the underlying table.
                        "ProvisionedThroughput": {
                            "ReadCapacityUnits": 1,
                            "WriteCapacityUnits": 1,
                        }
                    }
                }
            ],
        )
        print("Secondary index added!")
    except Exception as e:
        print("Error updating table:")
        print(e)

    创建全局二级索引与创建表有很多共同点。指定索引的名称、将位于索引中的属性、索引的键模式以及预配的吞吐量(应用程序可以从表或索引中使用的最大容量)。针对每个索引预配的吞吐量与针对表预配的吞吐量是分开的。这使您可以根据应用程序的需求按粒度定义吞吐量。

    在终端运行以下命令来添加全局二级索引。

    $ python add_secondary_index.py
    

    此脚本将名为 CategoryIndex 的全局二级索引添加到 Books 表中。

  • 现在您已经有了 CategoryIndex,可以使用它来检索具有特定类别的所有图书。使用二级索引来查询表与使用 Query API 调用相似。现在向 API 调用中添加索引名称。

    当您向现有表中添加全局二级索引时,DynamoDB 会使用表中的现有条目异步回填索引。所有条目回填后,可使用索引进行查询。回填时间因表大小不同而异。

    可以使用 query_with_index.py 脚本来查询新索引。在终端使用以下命令运行该脚本。

    $ python query_with_index.py
    

    该命令运行以下脚本来检索商店中 Category 为 Suspense 的所有图书。

    import time
    
    import boto3
    from boto3.dynamodb.conditions import Key
    
    # Boto3 is the AWS SDK library for Python.
    # The "resources" interface allows for a higher-level abstraction than the low-level client interface.
    # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html
    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
    table = dynamodb.Table('Books')
    
    # When adding a global secondary index to an existing table, you cannot query the index until it has been backfilled.
    # This portion of the script waits until the index is in the “ACTIVE” status, indicating it is ready to be queried.
    while True:
        if not table.global_secondary_indexes or table.global_secondary_indexes[0]['IndexStatus'] != 'ACTIVE':
            print('Waiting for index to backfill...')
            time.sleep(5)
            table.reload()
        else:
            break
    
    # When making a Query call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query.
    # If you want to use a specific index, you also need to pass the IndexName in our API call.
    resp = table.query(
        # Add the name of the index you want to use in your query.
        IndexName="CategoryIndex",
        KeyConditionExpression=Key('Category').eq('Suspense'),
    )
    
    print("The query returned the following items:")
    for item in resp['Items']:
        print(item)

    请注意,该脚本的一部分会等待索引可供查询。

    您应当会在终端看到以下输出结果。

    $ python query_with_index.py
    The query returned the following items:
    {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'Along Came a Spider', 'Formats': {'Hardcover': 'C9NR6RJ7', 'Paperback': '37JVGDZG', 'Audiobook': '6348WX3U'}, 'Author': 'James Patterson', 'Category': 'Suspense'}

    该查询返回两位不同作者的三本图书。这查询模式借助于表的主键模式很难实现,但借助于二级索引的强大功能很易实现。

    在下一个模块中,您将学习如何使用 UpdateItem API 更新表中现有条目的属性。 

更新条目