

# 关系（SQL）数据库和 DynamoDB 在管理索引方面的差异
<a name="SQLtoNoSQL.Indexes"></a>

索引使您能够访问替代查询模式，并可以加快查询速度。本节将 SQL 和 Amazon DynamoDB 中的索引创建和使用进行了比较和对比。

无论您使用的是关系数据库还是 DynamoDB，在创建索引时都应谨慎。只要对表进行写入，就必须更新表的所有索引。在具有大型表的写入密集型环境中，这会占用大量系统资源。在只读环境或主读环境中，这算不上一个问题。但是，您应确保索引实际上由应用程序使用，而不只是占用空间。

**Topics**
+ [关系（SQL）数据库和 DynamoDB 在创建索引方面的差异](#SQLtoNoSQL.Indexes.Creating)
+ [关系（SQL）数据库和 DynamoDB 在查询和扫描索引方面的差异](#SQLtoNoSQL.Indexes.QueryAndScan)

## 关系（SQL）数据库和 DynamoDB 在创建索引方面的差异
<a name="SQLtoNoSQL.Indexes.Creating"></a>

将 SQL 中的 `CREATE INDEX` 语句与 Amazon DynamoDB 中的 `UpdateTable` 操作进行比较。

**Topics**
+ [使用 SQL 创建索引](#SQLtoNoSQL.Indexes.Creating.SQL)
+ [在 DynamoDB 中创建索引](#SQLtoNoSQL.Indexes.Creating.DynamoDB)

### 使用 SQL 创建索引
<a name="SQLtoNoSQL.Indexes.Creating.SQL"></a>

在关系数据库中，索引是一个数据结构，可让您对表中的不同的列执行快速查询。您可以使用 `CREATE INDEX` SQL 语句将索引添加到现有表，并指定要建立索引的列。在创建索引后，您可以照常查询表中的数据，但现在数据库可使用索引快速查找表中的指定行，而不是扫描整个表。

在创建一个索引后，数据库将为您维护此索引。只要您修改表中的数据，就会自动更改索引以反映表中的更改。

在 MySQL 中，您可以创建如下所示的索引。

```
CREATE INDEX GenreAndPriceIndex
ON Music (genre, price);
```

### 在 DynamoDB 中创建索引
<a name="SQLtoNoSQL.Indexes.Creating.DynamoDB"></a>

在 DynamoDB 中，您可以创建和使用*二级索引*来实现类似目的。

DynamoDB 中的索引与其关系对应项不同。当您创建二级索引时，必须指定其键属性—分区键和排序键。创建二级索引后，可以 `Query` 或 `Scan`，就像对表一样。DynamoDB 没有查询优化程序，因此仅在 `Query` 或 `Scan` 时使用二级索引。

DynamoDB 支持两种不同的索引：
+ 全局二级索引 – 索引的主键可以是其表中的任意两个属性。
+ 本地二级索引 – 索引的分区键必须与其表的分区键相同。不过，排序键可以是任何其他属性。

DynamoDB 确保二级索引中的数据最终与其表保持一致。您可以请求对表或本地二级索引执行强一致性 `Query` 或 `Scan` 操作。但是，全局二级索引仅支持最终一致性。

可以使用 `UpdateTable` 操作并指定 `GlobalSecondaryIndexUpdates`，将全局二级索引添加到现有表。

```
{
    TableName: "Music",
    AttributeDefinitions:[
        {AttributeName: "Genre", AttributeType: "S"},
        {AttributeName: "Price", AttributeType: "N"}
    ],
    GlobalSecondaryIndexUpdates: [
        {
            Create: {
                IndexName: "GenreAndPriceIndex",
                KeySchema: [
                    {AttributeName: "Genre", KeyType: "HASH"}, //Partition key
                    {AttributeName: "Price", KeyType: "RANGE"}, //Sort key
                ],
                Projection: {
                    "ProjectionType": "ALL"
                },
                ProvisionedThroughput: {                                // Only specified if using provisioned mode
                    "ReadCapacityUnits": 1,"WriteCapacityUnits": 1
                }
            }
        }
    ]
}
```

您必须向 `UpdateTable` 提供以下参数：
+ `TableName` – 索引将关联到的表。
+ `AttributeDefinitions` – 索引的键架构属性的数据类型。
+ `GlobalSecondaryIndexUpdates` – 有关要创建的索引的详细信息：
  + `IndexName` – 索引的名称。
  + `KeySchema` – 用于索引主键的属性。
  + `Projection` – 表中要复制到索引的属性。在此情况下，`ALL` 意味着复制所有属性。
  + `ProvisionedThroughput (for provisioned tables)` – 每秒需对此索引执行的读取和写入次数。(它与表的预调配吞吐量设置是分开的。) 

在此操作中，会将表中的数据回填到新索引。在回填期间，表保持可用。但索引未准备就绪，直至其 `Backfilling` 属性从 true 变为 false。您可以使用 `DescribeTable` 操作查看此属性。

## 关系（SQL）数据库和 DynamoDB 在查询和扫描索引方面的差异
<a name="SQLtoNoSQL.Indexes.QueryAndScan"></a>

将使用 SQL 中的 SELECT 语句查询并扫描索引与使用 Amazon DynamoDB 中的 `Query` 和 `Scan` 操作查询并扫描索引进行比较。

**Topics**
+ [使用 SQL 查询并扫描索引](#SQLtoNoSQL.Indexes.QueryAndScan.SQL)
+ [在 DynamoDB 中查询并扫描索引](#SQLtoNoSQL.Indexes.QueryAndScan.DynamoDB)

### 使用 SQL 查询并扫描索引
<a name="SQLtoNoSQL.Indexes.QueryAndScan.SQL"></a>

在关系数据库中，不能直接使用索引。相反，您通过发出 `SELECT` 语句来查询表，查询优化程序可使用任何索引。

*查询优化程序* 是一个关系数据库管理系统 (RDBMS) 组件，它将评估可用索引并确定是否可使用这些索引来加快查询速度。如果索引可用来加快查询速度，则 RDBMS 会先访问索引，然后使用索引查找表中的数据。

以下几个 SQL 语句可使用 *GenreAndPriceIndex* 提高性能。我们假定 *Music* 表包含足够多的数据以促使查询优化程序决定使用此索引，而不是扫描整个表。

```
/* All of the rock songs */

SELECT * FROM Music
WHERE Genre = 'Rock';
```

```
/* All of the cheap country songs */

SELECT Artist, SongTitle, Price FROM Music
WHERE Genre = 'Country' AND Price < 0.50;
```

### 在 DynamoDB 中查询并扫描索引
<a name="SQLtoNoSQL.Indexes.QueryAndScan.DynamoDB"></a>

在 DynamoDB 中，直接对索引执行 `Query` 和 `Scan` 操作，就如同对表执行此操作一样。您可以使用 DynamoDB API 或 [PartiQL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html)（一种与 SQL 兼容的查询语言）来查询或扫描索引。您必须指定 `TableName` 和 `IndexName`。

下面是一些 DynamoDB 中对 *GenreAndPriceIndex* 的查询。（此索引的键架构包含 *Genre* 和 *Price*。）

------
#### [ DynamoDB API ]

```
// All of the rock songs

{
    TableName: "Music",
    IndexName: "GenreAndPriceIndex",
    KeyConditionExpression: "Genre = :genre",
    ExpressionAttributeValues: {
        ":genre": "Rock"
    },
};
```

此示例使用 `ProjectionExpression` 指示您只希望结果中显示部分而不是全部属性。

```
// All of the cheap country songs

{
    TableName: "Music",
    IndexName: "GenreAndPriceIndex",
    KeyConditionExpression: "Genre = :genre and Price < :price",
    ExpressionAttributeValues: {
        ":genre": "Country",
        ":price": 0.50
    },
    ProjectionExpression: "Artist, SongTitle, Price"
};
```

以下是对 *GenreAndPriceIndex* 进行的扫描。

```
// Return all of the data in the index

{
    TableName:  "Music",
    IndexName: "GenreAndPriceIndex"
}
```

------
#### [ PartiQL for DynamoDB ]

使用 PartiQL，您可以使用 `Select` 语句来对索引执行查询和扫描。

```
// All of the rock songs

SELECT * 
FROM Music.GenreAndPriceIndex
WHERE Genre = 'Rock'
```

```
// All of the cheap country songs

SELECT * 
FROM Music.GenreAndPriceIndex
WHERE Genre = 'Rock' AND Price < 0.50
```

以下是对 *GenreAndPriceIndex* 进行的扫描。

```
// Return all of the data in the index

SELECT *
FROM Music.GenreAndPriceIndex
```

**注意**  
有关使用 `Select` 的代码示例，请参阅 [PartiQL for DynamoDB 的 Select 语句](ql-reference.select.md)。

------