

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# Amazon QLDB 并发模型
并发模式

**重要**  
终止支持通知：现有客户将能够使用 Amazon QLDB，直到 2025 年 7 月 31 日终止支持。有关更多详细信息，请参阅[将亚马逊 QLDB 账本迁移到亚马逊 Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

Amazon QLDB 旨在满足高性能联机事务处理（OLTP）的工作负载需求。QLDB 支持类似 SQL 的查询功能，并提供完整 ACID 事务。此外，QLDB 数据项为文档，可提供架构灵活性和直观的数据建模。以日志为核心，您可以使用 QLDB 访问数据所有更改的完整且可验证的历史记录，并根据需要将连贯的事务流式传输到其他数据服务。

**Topics**
+ [

## 乐观并发控制
](#concurrency.occ)
+ [

## 使用索引避免全表扫描
](#concurrency.indexes)
+ [

## 插入 OCC 冲突
](#concurrency.inserts)
+ [

## 使事务幂等
](#concurrency.idempotent)
+ [

## 编辑 OCC 冲突
](#concurrency.redaction)
+ [

## 管理并发会话
](#concurrency.sessions)

## 乐观并发控制


在 QLDB 中，并发控制通过*乐观并发控制*（OCC）实现。OCC 的运作原则是，多笔事务可以在不相互干扰的情况下经常完成。

通过 OCC，QLDB 中的事务不会获取数据库资源的锁，并且在完全可序列化的隔离下运行。QLDB 以串行方式运行并发事务，因此它产生的效果与串行启动事务的效果相同。

在提交之前，每个事务都会执行验证检查，以确保没有其他已提交的事务修改其正在访问的数据。如果此检查发现冲突的修改，或者数据的状态发生更改，则提交事务将被拒绝。但是，事务处理可以重新启动。

当事务写入 QLDB 时，OCC 模型的验证检查由 QLDB 本身实现。如果由于 OCC 的验证阶段失败而无法将事务写入日志，QLDB 将 `OccConflictException` 返回到应用层。应用软件负责确保事务重新启动。应用程序应中止被拒绝的事务，然后从头开始重试整个事务。

要了解 QLDB 驱动程序如何处理和重试 OCC 冲突和其他临时异常，请参阅 [使用 Amazon QLDB 中的驱动程序了解重试策略](driver-retry-policy.md)。

## 使用索引避免全表扫描


QLDB 中的每个 PartiQL 语句（包括每个 `SELECT` 查询）都是在事务中处理的，并且受[事务超时限制](limits.md#limits.fixed)的约束。

按最佳实践标准，您应运行带有 `WHERE` 谓词子句的语句，该子句可以筛选索引字段或文档 ID。QLDB 需要在索引字段上使用*相等*运算符才能高效地查找文档；例如 `WHERE indexedField = 123` 或 `WHERE indexedField IN (456, 789)`。

如果没有索引查询，QLDB 在读取文档时需进行 *全表扫描*。这可能会导致查询延迟和事务超时，还会导致 OCC 与竞争事务发生冲突的机会增加。

例如，假设一个名为 `Vehicle` 的表，该表仅在该 `VIN` 字段上有索引。它将包含以下文档。


****  

| VIN | Make | 模型 | 颜色 | 
| --- | --- | --- | --- | 
| "1N4AL11D75C109151" | "Audi" | "A5" | "Silver" | 
| "KM8SRDHF6EU074761" | "Tesla" | "Model S" | "Blue" | 
| "3HGGK5G53FM761765" | "Ducati" | "Monster 1200" | "Yellow" | 
| "1HVBBAANXWH544237" | "Ford" | "F 150" | "Black" | 
| "1C4RJFAG0FC625797" | "Mercedes" | "CLK 350" | "White" | 

两个名为 Alice 和 Bob 的并发用户正在使用分类账中的同一个表。以下两人想要更新两个不同的文档，如下所示。

Alice：

```
UPDATE Vehicle AS v
SET v.Color = 'Blue'
WHERE v.VIN = '1N4AL11D75C109151'
```

Bob：

```
UPDATE Vehicle AS v
SET v.Color = 'Red'
WHERE v.Make = 'Tesla' AND v.Model = 'Model S'
```

假设 Alice 和 Bob 同时开始事务。Alice 的 `UPDATE` 语句对 `VIN` 字段进行索引查找，因此它只需要读取那个文档即可。Alice 先完成并成功提交了她的事务。

Bob 的语句会筛选非索引字段，因此它会进行表扫描并遇到 `OccConflictException`。这是因为 Alice 提交的事务修改了 Bob 语句正在访问的数据，其中包括表中的每个文档，而非仅仅是 Bob 正在更新的文档。

## 插入 OCC 冲突


OCC 冲突可能包括新插入文档，而不仅仅是先前存在的文档。请考虑下图，其中两位用户（Alice 和 Bob）正在处理 表中的同一项目。他们都希望仅在谓词值尚不存在的情况下插入新文档。

![\[Amazon QLDB 乐观并发控制（OCC）图表显示两个并发用户之间的冲突异常示例。\]](http://docs.aws.amazon.com/zh_cn/qldb/latest/developerguide/images/occ-example.png)


在此示例中，Alice 和 Bob 在单个事务中运行以下 `SELECT` 和 `INSERT` 语句。只有当 `INSERT` 语句未返回任何结果时，他们的应用程序才会运行该 `SELECT` 语句。

```
SELECT * FROM Vehicle v WHERE v.VIN = 'ABCDE12345EXAMPLE'
```

```
INSERT INTO Vehicle VALUE
{
    'VIN' : 'ABCDE12345EXAMPLE',
    'Type' : 'Wagon',
    'Year' : 2019,
    'Make' : 'Subaru',
    'Model' : 'Outback',
    'Color' : 'Gray'
}
```

假设 Alice 和 Bob 同时开始事务。他们的两个 `SELECT` 查询都没有返回任何带有 `ABCDE12345EXAMPLE` 为 `VIN` 的现有文档。因此，他们的应用程序继续执行 `INSERT` 语句。

Alice 先完成并成功提交了她的事务。然后，Bob 尝试提交他的事务，但是 QLDB 拒绝了事务并抛出了`OccConflictException`。这是因为 Alice 提交的事务修改了 Bob `SELECT`的 查询结果集，而 OCC 在提交 Bob 的事务之前就检测到了这种冲突。

此事务示例需要 `SELECT` 查询才能具有[幂等](#concurrency.idempotent)。然后 Bob 可从一开始就重试整个事务。但是他的下一个 `SELECT` 查询将返回 Alice 插入的文档，因此 Bob 的应用程序将无法运行 `INSERT`。

## 使事务幂等


[上一节](#concurrency.inserts)中的插入事务也是*幂等*事务的示例。换句话说，多次运行同一个事务会产生相同结果。如果 Bob 在不事先检查特定 `INSERT` 是否 `VIN` 存在的情况下运行，则该表最后可能会出现具有重复 `VIN` 值的文档。

除 OCC 冲突之外，请考虑其他重试方案。例如，假设 QLDB 在服务器端成功提交了事务，但客户端在等待响应时超时。作为最佳做法，我们建议将写事务设置为幂等，以避免在并发或重试时出现任何意想不到的副作用。

## 编辑 OCC 冲突


QLDB 可防止在同一日志块[同时编辑](working.redaction.md)修订版。举一个例子，其中两个并发用户（Alice 和 Bob）想要编辑在分类账的同一个区块上提交的两个不同的文档修订版。首先，Alice 通过运行 `REDACT_REVISION` 存储过程请求编辑一个修订版，如下所示。

```
EXEC REDACT_REVISION `{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`, '5PLf9SXwndd63lPaSIa0O6', 'ADR2Ll1fGsU4Jr4EqTdnQF'
```

然后，当 Alice 的请求仍在处理，Bob 请求编辑另一个修订版，如下所示。

```
EXEC REDACT_REVISION `{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`, '8F0TPCmdNQ6JTRpiLj2TmW', '05K8zpGYWynDlEOK5afDRc'
```

QLDB 以 `OccConflictException` 拒绝了 Bob 的请求， 尽管他们正试图编辑两个不同的文档修订版。这是因为 Bob 的修订版与 Alice 正编辑的修订版位于同一个区块上。Alice 的请求处理完毕后，Bob 可以重试他的编辑请求。

同样，如果两个并发事务尝试编辑同一修订版，则只能处理一个请求。在编辑完成之前，另一个请求失败并显示 OCC 冲突异常。之后，任何对同一修订版本进行编辑的请求都会导致错误，表明该修订已被编辑。

## 管理并发会话


如您有使用关系数据库管理系统（RDBMS）的经验，可能已熟悉并发连接限制。QLDB 与传统 RDBMS 连接的概念不同，因为事务通过 HTTP 请求和响应消息运行。

在 QLDB 中，类比概念是*活跃会话*。从概念上讲，会话类似于用户登录，它管理有关您对分类账的数据事务请求的信息。活动会话是指正在积极运行事务会话。它可以是最近完成事务的会话，服务预计它将立即启动另一笔事务。QLDB 支持每个会话正在运行的事务。

每个分类账的并发活动会话限制在[Amazon QLDB 资源中的限额和限制](limits.md#limits.fixed)中定义。达到此限制后，任何尝试启动事务的会话都会导致错误（`LimitExceededException`）。

有关会话生命周期以及 QLDB 驱动程序在运行数据事务时如何处理会话的信息，请参阅[驱动程序会话管理](driver-session-management.md#driver-session-mgmt.lifecycle)。有关使用 QLDB 驱动程序在应用程序中配置会话池的最佳实践，请参阅*Amazon QLDB 驱动程序建议*中的[配置 QldbDriver 对象](driver.best-practices.md#driver.best-practices.configuring)。