DynamoDB作为AWS提供的全托管NoSQL数据库,支持全局二级索引(GSI)来扩展查询能力。稀疏全局二级索引是GSI的一种特殊使用形式,和传统GSI会为表中所有记录创建索引条目不同,它仅当记录包含GSI定义的分区键或排序键属性时,才会为这条记录生成索引条目,非常适合只需要对部分符合条件的记录做快速查询的场景。

稀疏全局二级索引的核心特性
传统GSI会为基表的所有条目创建索引,即使某些条目没有对应的索引键属性,DynamoDB也会为这些条目在索引中存储空值条目。而稀疏全局二级索引不会处理这类不包含索引键属性的记录,因此具备以下优势:
- 减少索引存储空间占用,降低存储成本
- 提升索引查询效率,因为索引中只包含需要查询的目标记录
- 可以按照业务条件灵活筛选需要纳入索引的记录,实现按需索引管理
按条件管理索引记录的实现逻辑
要实现按条件管理索引记录,核心是在设计表结构时,只对需要纳入索引的记录添加对应的索引键属性。比如我们有一个用户订单表,需要快速查询所有已支付的订单,那么可以创建一个稀疏GSI,分区键设置为payment_status属性,只有当订单的支付状态为已支付时,才给该订单记录添加payment_status属性,这样GSI中就只会包含已支付的订单记录。
创建稀疏全局二级索引的示例
下面是使用AWS SDK for Python(Boto3)创建包含稀疏GSI的DynamoDB表的代码示例:
import boto3
# 创建DynamoDB资源
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
# 创建用户订单表,同时创建稀疏GSI
table = dynamodb.create_table(
TableName='user_orders',
KeySchema=[
{
'AttributeName': 'order_id',
'KeyType': 'HASH' # 分区键
}
],
AttributeDefinitions=[
{
'AttributeName': 'order_id',
'AttributeType': 'S'
},
{
'AttributeName': 'payment_status',
'AttributeType': 'S'
},
{
'AttributeName': 'order_time',
'AttributeType': 'N'
}
],
GlobalSecondaryIndexes=[
{
'IndexName': 'paid_orders_index',
'KeySchema': [
{
'AttributeName': 'payment_status',
'KeyType': 'HASH'
},
{
'AttributeName': 'order_time',
'KeyType': 'RANGE'
}
],
'Projection': {
'ProjectionType': 'ALL'
},
'ProvisionedThroughput': {
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
}
],
ProvisionedThroughput={
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
)
print(f"表创建成功:{table.table_name}")
写入符合条件的记录
接下来我们写入两条订单记录,一条是已支付订单,一条是未支付订单,只有已支付订单会包含payment_status属性,从而被纳入稀疏GSI:
# 获取表对象
table = dynamodb.Table('user_orders')
# 写入已支付订单,包含payment_status属性,会被纳入稀疏GSI
table.put_item(
Item={
'order_id': 'order_001',
'user_id': 'user_123',
'order_amount': 299.9,
'payment_status': 'paid', # 包含GSI的分区键属性
'order_time': 1698765432
}
)
# 写入未支付订单,不包含payment_status属性,不会被纳入稀疏GSI
table.put_item(
Item={
'order_id': 'order_002',
'user_id': 'user_123',
'order_amount': 199.9,
'order_time': 1698765433
# 没有payment_status属性,不会进入稀疏GSI
}
)
print("订单记录写入完成")
查询稀疏全局二级索引
查询稀疏GSI时,只需要指定索引名称和对应的键条件即可,因为索引中只包含已支付的订单,所以查询结果不会包含未支付订单:
# 查询所有已支付订单,按订单时间倒序排列
response = table.query(
IndexName='paid_orders_index',
KeyConditionExpression='payment_status = :status',
ExpressionAttributeValues={
':status': 'paid'
},
ScanIndexForward=False # 按排序键倒序
)
# 打印查询结果
print("已支付订单列表:")
for item in response['Items']:
print(f"订单ID:{item['order_id']},金额:{item['order_amount']},下单时间:{item['order_time']}")
使用注意事项
在使用稀疏全局二级索引时,需要注意以下几点:
- 稀疏GSI的索引键属性不是基表的必填属性,需要业务层控制何时添加该属性
- 如果后续需要把某条记录纳入索引,只需要给该记录更新添加对应的索引键属性即可
- 如果要把某条记录从索引中移除,可以删除该记录的索引键属性,DynamoDB会自动从GSI中删除对应的索引条目
- 稀疏GSI同样支持投影配置,可以根据需要选择只投影部分属性到索引中,进一步减少存储开销
稀疏全局二级索引是DynamoDB优化索引存储和查询性能的重要手段,合理设计索引键属性的添加逻辑,就能实现灵活的按条件索引记录管理,适配各类按需查询的业务场景。