DynamoDB全局二级索引 #

一、GSI概述 #

1.1 什么是GSI #

全局二级索引(Global Secondary Index)是一种允许使用与表不同的分区键和排序键查询数据的索引。

text
GSI特点:
├── 分区键可与表不同
├── 排序键可选
├── 跨分区查询
├── 默认最终一致性
├── 独立的容量配置
└── 最多20个/表

1.2 GSI vs 表主键 #

特性 表主键 GSI
分区键 必须指定 可选择任意属性
排序键 可选 可选
唯一性 必须唯一 不要求唯一
一致性 强一致/最终一致 最终一致(默认)

二、创建GSI #

2.1 创建表时定义GSI #

使用CLI:

bash
aws dynamodb create-table \
  --table-name Users \
  --attribute-definitions \
    AttributeName=UserId,AttributeType=S \
    AttributeName=Email,AttributeType=S \
  --key-schema \
    AttributeName=UserId,KeyType=HASH \
  --global-secondary-indexes \
    '[
      {
        "IndexName": "EmailIndex",
        "KeySchema": [
          {"AttributeName": "Email", "KeyType": "HASH"}
        ],
        "Projection": {"ProjectionType": "ALL"},
        "ProvisionedThroughput": {
          "ReadCapacityUnits": 5,
          "WriteCapacityUnits": 5
        }
      }
    ]' \
  --billing-mode PAY_PER_REQUEST

使用JavaScript SDK:

javascript
const { CreateTableCommand } = require('@aws-sdk/client-dynamodb');

const command = new CreateTableCommand({
  TableName: 'Users',
  AttributeDefinitions: [
    { AttributeName: 'UserId', AttributeType: 'S' },
    { AttributeName: 'Email', AttributeType: 'S' },
    { AttributeName: 'CreatedAt', AttributeType: 'S' }
  ],
  KeySchema: [
    { AttributeName: 'UserId', KeyType: 'HASH' }
  ],
  GlobalSecondaryIndexes: [
    {
      IndexName: 'EmailIndex',
      KeySchema: [
        { AttributeName: 'Email', KeyType: 'HASH' }
      ],
      Projection: {
        ProjectionType: 'ALL'
      }
    },
    {
      IndexName: 'CreatedAtIndex',
      KeySchema: [
        { AttributeName: 'CreatedAt', KeyType: 'HASH' }
      ],
      Projection: {
        ProjectionType: 'INCLUDE',
        NonKeyAttributes: ['UserId', 'Name', 'Email']
      }
    }
  ],
  BillingMode: 'PAY_PER_REQUEST'
});

2.2 向现有表添加GSI #

bash
aws dynamodb update-table \
  --table-name Users \
  --attribute-definitions \
    AttributeName=Phone,AttributeType=S \
  --global-secondary-index-updates \
    '[
      {
        "Create": {
          "IndexName": "PhoneIndex",
          "KeySchema": [
            {"AttributeName": "Phone", "KeyType": "HASH"}
          ],
          "Projection": {"ProjectionType": "ALL"}
        }
      }
    ]'
javascript
const { UpdateTableCommand } = require('@aws-sdk/client-dynamodb');

const command = new UpdateTableCommand({
  TableName: 'Users',
  AttributeDefinitions: [
    { AttributeName: 'Phone', AttributeType: 'S' }
  ],
  GlobalSecondaryIndexUpdates: [
    {
      Create: {
        IndexName: 'PhoneIndex',
        KeySchema: [
          { AttributeName: 'Phone', KeyType: 'HASH' }
        ],
        Projection: {
          ProjectionType: 'ALL'
        }
      }
    }
  ]
});

2.3 带排序键的GSI #

javascript
const command = new CreateTableCommand({
  TableName: 'Orders',
  AttributeDefinitions: [
    { AttributeName: 'OrderId', AttributeType: 'S' },
    { AttributeName: 'CustomerId', AttributeType: 'S' },
    { AttributeName: 'OrderDate', AttributeType: 'S' }
  ],
  KeySchema: [
    { AttributeName: 'OrderId', KeyType: 'HASH' }
  ],
  GlobalSecondaryIndexes: [
    {
      IndexName: 'CustomerOrderIndex',
      KeySchema: [
        { AttributeName: 'CustomerId', KeyType: 'HASH' },
        { AttributeName: 'OrderDate', KeyType: 'RANGE' }
      ],
      Projection: {
        ProjectionType: 'ALL'
      }
    }
  ],
  BillingMode: 'PAY_PER_REQUEST'
});

三、投影类型 #

3.1 投影类型说明 #

类型 说明 存储成本
KEYS_ONLY 仅索引键和表主键 最低
INCLUDE 键+指定属性 中等
ALL 所有属性 最高

3.2 KEYS_ONLY #

javascript
{
  IndexName: 'EmailIndex',
  KeySchema: [
    { AttributeName: 'Email', KeyType: 'HASH' }
  ],
  Projection: {
    ProjectionType: 'KEYS_ONLY'
  }
}

适用场景:

text
适用场景:
├── 仅需要主键回表查询
├── 存储成本敏感
└── 索引属性较少

3.3 INCLUDE #

javascript
{
  IndexName: 'EmailIndex',
  KeySchema: [
    { AttributeName: 'Email', KeyType: 'HASH' }
  ],
  Projection: {
    ProjectionType: 'INCLUDE',
    NonKeyAttributes: ['Name', 'Status', 'CreatedAt']
  }
}

适用场景:

text
适用场景:
├── 需要部分属性
├── 平衡存储和性能
└── 特定查询模式

3.4 ALL #

javascript
{
  IndexName: 'EmailIndex',
  KeySchema: [
    { AttributeName: 'Email', KeyType: 'HASH' }
  ],
  Projection: {
    ProjectionType: 'ALL'
  }
}

适用场景:

text
适用场景:
├── 需要所有属性
├── 避免回表查询
└── 查询性能优先

四、查询GSI #

4.1 基本查询 #

javascript
const { QueryCommand } = require('@aws-sdk/lib-dynamodb');

const response = await docClient.send(new QueryCommand({
  TableName: 'Users',
  IndexName: 'EmailIndex',
  KeyConditionExpression: 'Email = :email',
  ExpressionAttributeValues: {
    ':email': 'john@example.com'
  }
}));

4.2 带排序键查询 #

javascript
const response = await docClient.send(new QueryCommand({
  TableName: 'Orders',
  IndexName: 'CustomerOrderIndex',
  KeyConditionExpression: 'CustomerId = :cid AND OrderDate BETWEEN :start AND :end',
  ExpressionAttributeValues: {
    ':cid': 'customer001',
    ':start': '2024-01-01',
    ':end': '2024-12-31'
  },
  ScanIndexForward: false  // 降序
}));

4.3 过滤表达式 #

javascript
const response = await docClient.send(new QueryCommand({
  TableName: 'Orders',
  IndexName: 'CustomerOrderIndex',
  KeyConditionExpression: 'CustomerId = :cid',
  FilterExpression: 'TotalAmount > :minAmount',
  ExpressionAttributeValues: {
    ':cid': 'customer001',
    ':minAmount': 100
  }
}));

五、稀疏索引 #

5.1 概念 #

稀疏索引只包含有索引键属性的项目。

text
稀疏索引特点:
├── 仅索引有键属性的项目
├── 适合可选属性
├── 减少索引大小
└── 提高查询效率

5.2 示例 #

javascript
// 创建稀疏索引
{
  IndexName: 'PhoneIndex',
  KeySchema: [
    { AttributeName: 'Phone', KeyType: 'HASH' }
  ],
  Projection: {
    ProjectionType: 'ALL'
  }
}

// 只有有Phone属性的用户会被索引
// 写入数据
await docClient.send(new PutCommand({
  TableName: 'Users',
  Item: {
    UserId: 'user1',
    Name: 'User One',
    Phone: '+86-138-xxxx-xxxx'  // 有Phone属性
  }
}));

await docClient.send(new PutCommand({
  TableName: 'Users',
  Item: {
    UserId: 'user2',
    Name: 'User Two'
    // 无Phone属性,不会被索引
  }
}));

// 查询PhoneIndex只会返回user1

5.3 应用场景 #

text
应用场景:
├── 可选属性查询
├── 状态过滤
├── 分类查询
└── 减少索引存储

六、GSI容量管理 #

6.1 按需模式 #

javascript
// GSI继承表的计费模式
{
  BillingMode: 'PAY_PER_REQUEST'
}

6.2 预置模式 #

javascript
{
  GlobalSecondaryIndexes: [
    {
      IndexName: 'EmailIndex',
      KeySchema: [
        { AttributeName: 'Email', KeyType: 'HASH' }
      ],
      Projection: { ProjectionType: 'ALL' },
      ProvisionedThroughput: {
        ReadCapacityUnits: 10,
        WriteCapacityUnits: 5
      }
    }
  ]
}

6.3 更新GSI容量 #

bash
aws dynamodb update-table \
  --table-name Users \
  --global-secondary-index-updates \
    '[
      {
        "Update": {
          "IndexName": "EmailIndex",
          "ProvisionedThroughput": {
            "ReadCapacityUnits": 20,
            "WriteCapacityUnits": 10
          }
        }
      }
    ]'

七、删除GSI #

7.1 删除GSI #

bash
aws dynamodb update-table \
  --table-name Users \
  --global-secondary-index-updates \
    '[
      {
        "Delete": {
          "IndexName": "PhoneIndex"
        }
      }
    ]'
javascript
const command = new UpdateTableCommand({
  TableName: 'Users',
  GlobalSecondaryIndexUpdates: [
    {
      Delete: {
        IndexName: 'PhoneIndex'
      }
    }
  ]
});

八、GSI限制 #

8.1 数量限制 #

text
限制:
├── 每表最多20个GSI
├── 可申请提高到最多50个
├── 每个GSI最多5个属性定义
└── 创建GSI时表状态必须为ACTIVE

8.2 大小限制 #

text
大小限制:
├── 索引键大小:最大2048字节
├── 项目大小:无限制(表限制400KB)
└── 索引项目大小:无限制

8.3 一致性限制 #

text
一致性限制:
├── 默认最终一致性
├── 读取延迟通常<1秒
└── 不支持强一致性读取

九、GSI设计模式 #

9.1 GSI过载模式 #

一个GSI支持多种查询模式:

javascript
// 表设计
{
  PK: 'USER#12345',
  SK: 'PROFILE',
  GSI1PK: 'EMAIL#john@example.com',
  GSI1SK: 'USER#12345'
}

// 另一种项目类型
{
  PK: 'ORDER#001',
  SK: 'DETAIL',
  GSI1PK: 'STATUS#PENDING',
  GSI1SK: '2024-01-01T10:00:00'
}

// 一个GSI支持多种查询
// 查询Email
await query('GSI1', 'EMAIL#john@example.com');

// 查询状态
await query('GSI1', 'STATUS#PENDING');

9.2 复合排序键模式 #

javascript
// 排序键设计:STATUS#PRIORITY#TIMESTAMP
{
  PK: 'PROJECT#001',
  SK: 'TASK#001',
  GSI1PK: 'PROJECT#001',
  GSI1SK: 'OPEN#HIGH#2024-01-01T10:00:00'
}

// 查询高优先级开放任务
await docClient.send(new QueryCommand({
  TableName: 'Tasks',
  IndexName: 'GSI1',
  KeyConditionExpression: 'GSI1PK = :pk AND begins_with(GSI1SK, :prefix)',
  ExpressionAttributeValues: {
    ':pk': 'PROJECT#001',
    ':prefix': 'OPEN#HIGH'
  }
}));

十、最佳实践 #

10.1 GSI设计建议 #

text
设计建议:
├── 仅创建必要的索引
├── 选择合适的投影类型
├── 考虑稀疏索引
├── 避免热点分区键
└── 监控索引使用情况

10.2 性能优化 #

text
性能建议:
├── 使用索引覆盖查询
├── 合理设置投影类型
├── 监控索引容量
├── 使用Auto Scaling
└── 避免过多索引

10.3 成本优化 #

text
成本建议:
├── 使用KEYS_ONLY或INCLUDE减少存储
├── 监控索引使用率
├── 删除不使用的索引
├── 使用稀疏索引
└── 合理配置容量

十一、总结 #

GSI要点:

特性 说明
分区键 可选择任意属性
排序键 可选
投影类型 KEYS_ONLY/INCLUDE/ALL
一致性 最终一致
数量限制 20个/表

下一步,让我们学习本地二级索引!

最后更新:2026-03-27