在此模块中,您将练习一些在 DynamoDB 中使用单个 API 调用检索多个项目的简单示例。您还将学习如何使用二级索引在您的 DynamoDB 表上启用额外的查询模式。

完成模块所需时间:15 分钟


在模块 2 中,您学习了如何使用 GetItem API 调用检索 DynamoDB 表中的单本图书。这种访问模式十分有用,但您的应用程序也将需要能通过一次调用检索多个项目。例如,您可能需要检索 John Grisham 著作的所有图书,从而展示给用户。在本模块的第 1 步中,您将使用 Query API 来检索特定作者的所有图书。

不论是通过 GetItem API 调用检索单本图书,还是通过 QueryAPI 调用检索某个作者的全部图书,都将使用 Books 表中的指定主键。然而,您可能需要支持其他的访问模式,例如检索特定类别(例如历史类或传记类)的所有图书。Category 不是表主键的一部分,但您可以创建二级索引来支持更多的访问模式。在本模块的第 2 步和第 3 步中,您将创建一个二级索引并查询该二级索引。


  • 第 1 步:通过查询检索多个项目

    如果您的表使用复合主键,您可以通过 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 FirmThe 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 调用即可轻松完成。

  • 第 2 步:创建二级索引

    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)

    全局二级索引的创建与表的创建十分相似。您需要指定索引的名称、索引中的属性、索引的键 schema 以及预置吞吐量(应用程序可以从表或索引中消耗的最大容量)。每个索引的预置吞吐量都独立于表的预置吞吐量。这有利于您根据应用程序的需要精确定义吞吐量。

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

    $ python add_secondary_index.py

    以下脚本将向您的 Books 表添加一个名为 CategoryIndex 的全局二级索引。

  • 第 3 步:查询二级索引

    现在您拥有了 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'}

    查询将返回两名不同作者的三本图书。使用表的主键 schema 将难以实现这种查询模式,但使用二级索引则可轻松完成。


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