

# 在 DynamoDB 中检测和纠正索引键违规
<a name="GSI.OnlineOps.ViolationDetection"></a>

在全局二级索引创建的回填阶段，Amazon DynamoDB 会检查表中的每个项目，以确定它是否符合包含在索引中的条件。某些项目可能不符合条件，因为它们会导致索引键冲突。在这些情况下，项目仍保留在表中，但索引没有该项的相应条目。

以下情况下发生*索引键冲突*：
+ 属性值与索引键架构数据类型不匹配。例如，假设 `GameScores` 表的一个项目的类型 `String` 为 `TopScore` 值。如果您添加的全局二级索引的分区键 `TopScore` 类型 `Number`，则表中的项目将违反索引键。
+ 表的属性值超出索引键属性的最大长度。分区键的最大长度为 2048 字节，排序键的最大长度为 1024 字节。如果表中的任何相应属性值超过这些限制，则表中的项目将违反索引键。

**注意**  
如果为用作索引键的属性设置了字符串或二进制属性值，则属性值的长度必须大于零；否则，表中的项将与索引键冲突。  
此工具目前不会标记此索引键冲突。

如果发生索引键冲突，回填阶段将继续不会中断。但是，索引中不包含任何违规项目。回填阶段完成后，所有违反新索引键架构的项目写入操作都将被拒绝。

要标识和修复表中违反索引键的属性值，请使用 Violation Detector 工具。要运行 Violation Detector，您需要创建一个配置文件，该文件指定要扫描的表的名称、全局二级索引分区键和排序键的名称和数据类型，以及在发现任何索引键冲突时要执行的操作。Violation Detector 可以在以下两种不同模式之一运行：
+ **检测模式**— 检测索引键违规。使用检测模式报告表中会导致全局二级索引中键违规的项目。（您可以选择要求在找到这些违规表项时立即删除它们。） 检测模式的输出将写入文件，您可以使用该文件进行进一步分析。
+ **更正模式**— 更正索引键冲突。在更正模式下，Violation Detector 从检测模式中读取与输出文件格式相同的输入文件。更正模式从输入文件中读取记录，并且对于每条记录，它会删除或更新表中的相应项目。（请注意，如果选择更新项目，则必须编辑输入文件并为这些更新设置适当的值。）

## 下载并运行 Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Violation Detector 可作为可执行 Java 归档文件（`.jar` 文件）提供，并在 Windows、macOS 或 Linux 计算机上运行。Violation Detector 需要 Java 1.7（或更高版本）和 Apache Maven。
+ [从 GitHub 下载 Violation Detector](https://github.com/awslabs/dynamodb-online-index-violation-detector)

按照 `README.md` 文件说明下载并使用 Maven 安装 Violation Detector。

要启动 Violation Detector，请转至生成 `ViolationDetector.java` 的目录并输入以下命令。

```
java -jar ViolationDetector.jar [options]
```

Violation Detector 命令行接受以下选项：
+ `-h | --help`— 输出 Violation Detector 的使用摘要和选项。
+ `-p | --configFilePath` `value`— Violation Detector 配置文件的完全限定名称。有关更多信息，请参阅 [Violation Detector 配置文件](#GSI.OnlineOps.ViolationDetection.ConfigFile)。
+ `-t | --detect` `value`— 检测表中的索引键违规，并将其写入 Violation Detector 输出文件。如果此参数的值设置为 `keep`，则不会修改带有键违规的项目。如果值设置为 `delete`，则会从表中删除带有键违规的项目。
+ `-c | --correct` `value`— 从输入文件中读取索引键冲突，并对表中的项目采取纠正措施。如果此参数的值设置为 `update`，则具有键违规的项目将使用新的不违规值进行更新。如果值设置为 `delete`，则会从表中删除带有键违规的项目。

## Violation Detector 配置文件
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

在运行时，Violation Detector 工具需要配置文件。此文件中的参数确定违规检测器可以访问哪些 DynamoDB 资源，以及它可以占用的预置吞吐量。下表介绍这些参数。


****  

| 参数名称 | 说明 | 必填？ | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  包含 AWS 凭证的文件完全限定名称。凭证文件必须为以下格式： <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  是  | 
|  `dynamoDBRegion`  |  表所在的 AWS 区域。例如：`us-west-2`。  |  是  | 
|  `tableName`  | 要扫描的 DynamoDB 表的名称。 |  是  | 
|  `gsiHashKeyName`  |  索引分区键的名称。  |  是  | 
|  `gsiHashKeyType`  |  索引分区键的数据类型 —`String`、`Number` 或 `Binary`： `S \| N \| B`  |  是  | 
|  `gsiRangeKeyName`  |  索引排序键的名称。如果索引只有简单主键（分区键），请不要指定此参数。  |  否  | 
|  `gsiRangeKeyType`  |  索引排序键的数据类型 —`String`、`Number` 或 `Binary`： `S \| N \| B`  如果索引只有简单主键（分区键），请不要指定此参数。  |  否  | 
|  `recordDetails`  |  是否将索引键违规的完整详细信息写入输出文件。如果设置为 `true`（默认值），则会报告有关违规项目的完整信息。如果设置为 `false`，则仅报告违规次数。  |  否  | 
|  `recordGsiValueInViolationRecord`  |  是否将违规索引键的值写入输出文件。如果设置为 `true`（默认值），则会报告键值。如果设置为 `false`，则不会报告键值。  |  否  | 
|  `detectionOutputPath`  |  Violation Detector 输出文件的完整路径。此参数支持写入本地目录或 Amazon Simple Storage Service (Amazon S3)。示例如下： `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` 输出文件中的信息以逗号分隔值 (CSV) 格式显示。如果您没有设置 `detectionOutputPath`，则输出文件名为 `violation_detection.csv`，并写入到您当前的工作目录中。  |  否  | 
|  `numOfSegments`  | Violation Detector 扫描表时要使用的并行扫描段数。默认值为 1，表示按顺序扫描表。如果值为 2 或更高，则 Violation Detector 将表划分为多个逻辑段和相同数量的扫描线程。`numOfSegments` 最大设置为 4096。对于较大的表，并行扫描通常比顺序扫描快。此外，如果表大到跨越多个分区，则并行扫描会将其读取活动均匀分布到多个分区。有关 DynamoDB 中并行扫描的更多信息，请参阅 [并行扫描](Scan.md#Scan.ParallelScan)。 |  否  | 
|  `numOfViolations`  |  写入到输出文件的索引键违规上限。如果设置为 `-1`（默认值），则扫描整个表。如果设置为正整数，则 Violation Detector 会在遇到该数量的违规后停止。  |  否  | 
|  `numOfRecords`  |  要扫描的表中的项目数。如果设置为 -1（默认值），则扫描整个表。如果设置为正整数，Violation Detector 会在扫描表中的该数量项目后停止。  |  否  | 
|  `readWriteIOPSPercent`  |  调节表扫描期间使用的预置读取容量单位的百分比。有效值范围为 `1` 至 `100`。默认值 (`25`) 表示违规检测器消耗的表预置读取吞吐量不超过 25%。  |  否  | 
|  `correctionInputPath`  |  Violation Detector 更正输入文件的完整路径。如果在更正模式下运行 Violation Detector，则此文件的内容将用于修改或删除表中违反全局二级索引的数据项。 `correctionInputPath` 文件的格式与 `detectionOutputPath` 文件相同。这样，您就可以将检测模式的输出作为更正模式下的输入进行处理。  |  否  | 
|  `correctionOutputPath`  |  Violation Detector 更正输出文件的完整路径。仅当存在更新错误时创建此文件。 此参数支持写入本地目录或 Amazon S3。示例如下： `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` 输出文件中的信息以 CSV 格式显示。如果您没有设置 `correctionOutputPath`，则输出文件名为 `violation_update_errors.csv`，并写入到您当前的工作目录中。  |  否  | 

## 检测
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

若要检测索引键违规，请将 Violation Detector 与 `--detect` 命令行选项一起使用。要显示此选项的工作原理，请考虑 `ProductCatalog` 表。以下是表中项目的列表。仅显示主键 (`Id`) 和 `Price` 属性。


****  

| ID（主键） | Price | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

`Price` 的所有值类型为 `Number`。但是，由于 DynamoDB 无架构，可以添加具有非数字 `Price` 项目。例如，假设您将另一项添加到 `ProductCatalog` 表。


****  

| ID（主键） | Price | 
| --- | --- | 
| 999 | "Hello" | 

该表现在共有 9 个项目。

现在，您将新的全局二级索引添加到表中：`PriceIndex`。此索引的主键为分区键 `Price`，类型为 `Number`。构建索引后，它将包含 8 个项目-但 `ProductCatalog` 表中有 9 个项目。这种差异的原因是，`"Hello"` 值是类型 `String`，但 `PriceIndex` 具有 `Number` 类型的主键。`String` 值违反了全局二级索引键，因此不出现在索引中。

若要在这种情况下使用 Violation Detector，请首先创建一个配置文件，如下所示。

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

接下来，您运行 Violation Detector，如以下示例所示。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

如果 `recordDetails` 配置参数设置为 `true`，Violation Detector 将每个违规的详细信息写入输出文件，如以下示例所示。

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

输出文件采用 CSV 格式。文件中的第一行是标题，后面是违反索引键的每个项目的一条记录。这些违规记录的字段如下：
+ **Table hash key**（表哈希键）– 表中项目的分区键值。
+ **Table range key**（表范围键）– 表中项目的排序键值。
+ **GSI hash key value**（GSI 哈希键值）– 全局二级索引的分区键值。
+ **GSI hash key violation type**（GSI 哈希键违规类型）– `Type Violation` 或 `Size Violation`。
+ **GGSI hash key violation description**（GSI 哈希键违规描述）– 违规的原因。
+ **GSI hash key update Value(FOR USER)** [GSI 哈希键更新值（针对用户）] – 在更正模式下，用户为属性提供的新值。
+ **GSI range key value**（GSI 范围键值）– 全局二级索引的排序键值。
+ **GSI range key violation type**（GSI 范围键违规类型）– `Type Violation` 或 `Size Violation`。
+ **GSI range key violation description**（GSI 范围键违规描述）– 违规的原因。
+ **GSI range key update Value(FOR USER)** [GSI 范围键更新值（针对用户）] – 在更正模式下，用户为属性提供的新值。
+ **Delete blank attribute when Updating(Y/N)** [在更新时删除空白属性（是/否）] – 在更正模式下，确定是删除 (Y) 还是保留 (N) 表中违规项目，但仅当以下任一字段为空时：
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  如果这些字段中的任何一个不为空，则 `Delete Blank Attribute When Updating(Y/N)` 无效。

**注意**  
输出格式可能有所不同，具体取决于配置文件和命令行选项。例如，如果表具有简单主键（没有排序键），则输出中不会出现排序键字段。  
文件中的违规记录可能不按排序顺序排列。

## 纠正
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

要更正索引键冲突，请将 Violation Detector 与 `--correct` 命令行选项一起使用。在更正模式下，Violation Detector 读取 `correctionInputPath` 参数指定的输入文件。此文件具有的格式与 `detectionOutputPath` 文件相同，以便您可以使用检测输出作为更正的输入。

Violation Detector 提供了两种不同的方法来纠正索引键违规：
+ **删除违反**— 删除具有违反属性值的表项。
+ **更新违反**— 更新表项目，将违规属性替换为不违规的值。

无论哪种情况，都可以使用检测模式的输出文件作为更正模式的输入。

继续 `ProductCatalog` 示例，假设您要从表中删除违规项目。为此，请使用以下命令行。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

此时，系统将要求您确认是否要删除违规项目。

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

现在 `ProductCatalog` 和 `PriceIndex` 具有相同数量的项目。