

# 在 DynamoDB 中监控设备状态更新
<a name="data-modeling-device-status"></a>

此使用案例讨论使用 DynamoDB 来监控 DynamoDB 中的设备状态更新（或设备状态更改）。

## 使用案例
<a name="data-modeling-schema-device-status-use-case"></a>

在 IoT 使用案例（例如智能工厂）中，许多设备需要由操作员监控，它们会定期将其状态或日志发送到监控系统。当设备出现问题时，该设备的状态会从*正常*更改为*警告*。根据设备中异常行为的严重程度和类型，日志级别或状态会有所不同。然后，系统会指派一名操作员检查设备，如果需要，操作员可以将问题上报给主管。

此系统的一些典型访问模式包括：
+ 为设备创建日志条目
+ 获取特定设备状态的所有日志，首先显示最新的日志
+ 获取给定操作员在两个日期之间的所有日志
+ 获取向给定主管上报的所有日志
+ 获取向给定主管上报的带有特定设备状态的所有日志
+ 获取特定日期向给定主管上报的带有特定设备状态的所有日志

## 实体关系图
<a name="data-modeling-schema-device-status-erd"></a>

这是我们将用于监控设备状态更新的实体关系图（ERD）。

![\[设备状态更新的 ERD。它显示了实体：Device 和 Operator。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-1-ERD.jpg)


## 访问模式
<a name="data-modeling-schema-device-status-access-patterns"></a>

这些是我们将考虑用于监控设备状态更新的访问模式。

1. `createLogEntryForSpecificDevice`

1. `getLogsForSpecificDevice`

1. `getWarningLogsForSpecificDevice`

1. `getLogsForOperatorBetweenTwoDates`

1. `getEscalatedLogsForSupervisor`

1. `getEscalatedLogsWithSpecificStatusForSupervisor`

1. `getEscalatedLogsWithSpecificStatusForSupervisorForDate`

## 架构设计的演变
<a name="data-modeling-schema-device-status-design-evolution"></a>

**步骤 1：解决访问模式 1（`createLogEntryForSpecificDevice`）和 2（`getLogsForSpecificDevice`）**

设备跟踪系统的扩展单位将是各个设备。在该系统中，`deviceID` 唯一地标识设备。这使得 `deviceID` 成为分区键的出色候选者。每个设备都会定期（比如每五分钟左右）向跟踪系统发送信息。这种排序使日期成为逻辑排序标准，因此成为排序键。本案例中的示例数据如下所示：

![\[存储多台设备的状态的表。DeviceID 是主键，状态更新日期是排序键。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-2-Step1.png)


要获取特定设备的日志条目，我们可以使用分区键 `DeviceID="d#12345"` 执行[查询](Query.md)操作。

**步骤 2：解决访问模式 3（`getWarningLogsForSpecificDevice`）**

由于 `State` 是一个非键属性，因此，使用当前架构解决访问模式 3 将需要一个[筛选表达式](Query.FilterExpression.md)。在 DynamoDB 中，筛选表达式是在使用键条件表达式读取数据之后应用的。例如，如果我们要获取 `d#12345` 的警告日志，那么分区键为 `DeviceID="d#12345"` 的查询操作将从上表中读取四个项目，然后筛选出一个没有*警告*状态的项目。这种方法在规模较大时效率不高。如果所排除的项目的比率较低或查询不频繁执行，则筛选表达式可能是一种排除所查询的项目的好方法。但是，对于从表中检索到许多项目并且大多数项目被筛选掉的情况，我们可以继续改进我们的表设计，使其运行效率更高。

让我们通过使用[复合排序键](data-modeling-blocks.md#data-modeling-blocks-composite)来更改处理这种访问模式的方式。您可以从 [DeviceStateLog\$13.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/device-state-log/json/DeviceStateLog_3.json) 导入示例数据，其中排序键更改为 `State#Date`。此排序键是属性 `State`、`#` 和 `Date` 的组合。在本例中，`#` 用作分隔符。数据现在如下所示：

![\[使用复合排序键 State#Date 获取的设备 d#12345 的状态更新数据。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-3-Step2.png)


要仅获取设备的警告日志，则使用此架构可以使查询更具针对性。查询的键条件使用分区键 `DeviceID="d#12345"` 和排序键 `State#Date begins_with “WARNING”`。此查询将只读取具有*警告*状态的相关三个项目。

**步骤 3：解决访问模式 4（`getLogsForOperatorBetweenTwoDates`）**

您可以导入 [DeviceStateLog\$14.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/device-state-log/json/DeviceStateLog_4.json)，其中 `Operator` 属性已添加到带有示例数据的 `DeviceStateLog` 表中。

![\[DeviceStateLog 表设计，带有 Operator 属性，用于获取操作员在特定日期之间的日志。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-4-Step3.png)


由于 `Operator` 当前不是分区键，因此无法基于 `OperatorID` 对该表执行直接键/值查找。我们需要在 `OperatorID` 上创建一个具有全局二级索引的新[项目集合](WorkingWithItemCollections.md)。访问模式需要基于日期的查找，因此 Date 是[全局二级索引（GSI）](GSI.md)的排序键属性。这就是 GSI 现在的样子：

![\[此 GSI 设计以 OperatorID 和 Date 作为分区键和排序键，来获取特定操作员的日志。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-5-Step3.png)


对于访问模式 4（`getLogsForOperatorBetweenTwoDates`），您可以在 `2020-04-11T05:58:00` 和 `2020-04-24T14:50:00` 之间使用分区键 `OperatorID=Liz` 和排序键 `Date` 来查询此 GSI。

![\[使用 OperatorID 和 Date 对 GSI 进行查询，来获取操作员在两个日期之间的日志。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-6-GSI1_1.png)


**步骤 4：解决访问模式 5（`getEscalatedLogsForSupervisor`）、6（`getEscalatedLogsWithSpecificStatusForSupervisor`）和 7（`getEscalatedLogsWithSpecificStatusForSupervisorForDate`）**

我们将使用[稀疏索引](data-modeling-blocks.md#data-modeling-blocks-sparse-index)来解决这些访问模式。

原定设置情况下，全局二级索引是稀疏索引，因此，只有基表中包含索引的主键属性的项目才会实际出现在索引中。这是排除与正在建模的访问模式无关的项目的另一种方法。

您可以导入 [DeviceStateLog\$16.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/device-state-log/json/DeviceStateLog_6.json)，其中 `EscalatedTo` 属性已添加到带有示例数据的 `DeviceStateLog` 表中。如前所述，并非所有日志都会上报给主管。

![\[此 GSI 设计使用 EscalatedTo 属性来获取主管的所有上报的日志。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-7-Step4.png)


现在可以创建一个新的 GSI，其中 `EscalatedTo` 是分区键，`State#Date` 是排序键。请注意，只有同时具有 `EscalatedTo` 和 `State#Date` 属性的项目才会出现在索引中。

![\[此 GSI 设计旨在获取所有具有 EscalatedTo 和 State#Date 属性的项目。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-8-Step4.png)


其余的访问模式汇总如下：

    对于访问模式 5（getEscalatedLogsForSupervisor），您可以使用分区键 EscalatedTo="Sara" 对上报 GSI 执行查询   对于访问模式 6（getEscalatedLogsWithSpecificStatusForSupervisor），您可以使用分区键 EscalatedTo="Sara" 和排序键 State\$1Date begins\$1with “WARNING” 对上报 GSI 执行查询    对于访问模式 7（getEscalatedLogsWithSpecificStatusForSupervisorForDate），您可以使用分区键 EscalatedTo="Sara" 和排序键 State\$1Date begins\$1with “WARNING4\$12020-04-27” 对上报 GSI 执行查询    

下表总结了所有访问模式以及架构设计如何解决访问模式：


| 访问模式 | 基表/GSI/LSI | 操作 | 分区键值 | 排序键值 | 其他条件/筛选条件 | 
| --- | --- | --- | --- | --- | --- | 
| createLogEntryForSpecificDevice | 基表 | PutItem | DeviceID=deviceId | State\$1Date=state\$1date |  | 
| getLogsForSpecificDevice | 基表 | Query | DeviceID=deviceId | State\$1Date begins\$1with "state1\$1" | ScanIndexForward = False | 
| getWarningLogsForSpecificDevice | 基表 | Query | DeviceID=deviceId | State\$1Date begins\$1with "WARNING" |  | 
| getLogsForOperatorBetweenTwoDates | GSI-1 | 查询 | Operator=operatorName | date1 和 date2 之间的日期 |  | 
| getEscalatedLogsForSupervisor | GSI-2 | 查询 | EscalatedTo=supervisorName |  |  | 
| getEscalatedLogsWithSpecificStatusForSupervisor | GSI-2 | 查询 | EscalatedTo=supervisorName | State\$1Date begins\$1with "state1\$1" |  | 
| getEscalatedLogsWithSpecificStatusForSupervisorForDate | GSI-2 | 查询 | EscalatedTo=supervisorName | State\$1Date begins\$1with "state1\$1date1" |  | 

## 最终架构
<a name="data-modeling-schema-device-status-final-schema"></a>

这是最终的架构设计。要以 JSON 文件格式下载此架构设计，请参阅 GitHub 上的 [DynamoDB 示例](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/schema_design/SchemaExamples)。

**基表**

![\[包含设备状态元数据（例如设备 ID、状态和日期）的基表设计。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-9-Table.png)


**GSI-1**

![\[GSI-1 设计。它显示了主键和属性：DeviceID、State#Date 和 State。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-10-GSI1.png)


**GSI-2**

![\[GSI-2 设计。它显示了主键和属性：DeviceID、Operator、Date 和 State。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/DataModeling/DeviceStatus-11-GSI2.png)


## 在此架构设计中使用 NoSQL Workbench
<a name="data-modeling-schema-device-status-nosql"></a>

若要进一步探索和编辑新项目，您可以将此最终架构导入到 [NoSQL Workbench](workbench.md)，这是一款为 DynamoDB 提供数据建模、数据可视化和查询开发功能的可视化工具。请按照以下步骤开始使用：

1. 下载 NoSQL Workbench。有关更多信息，请参阅 [下载 NoSQL Workbench for DynamoDB](workbench.settingup.md)。

1. 下载上面列出的 JSON 架构文件，该文件已经采用 NoSQL Workbench 模型格式。

1. 将 JSON 架构文件导入到 NoSQL Workbench。有关更多信息，请参阅 [导入现有数据模型](workbench.Modeler.ImportExisting.md)。

1. 导入到 NOSQL Workbench 后，您便可编辑数据模型。有关更多信息，请参阅 [编辑现有数据模型](workbench.Modeler.Edit.md)。