事务和乐观并发控制Transactions and optimistic concurrency control

数据库事务提供一种安全且可预测的编程模型来处理数据的并发更改。Database transactions provide a safe and predictable programming model to deal with concurrent changes to the data. 利用 SQL Server 等传统关系数据库,你可以使用存储过程和/或触发器编写业务逻辑,然后将其发送到该服务器以便直接在数据库引擎中执行。Traditional relational databases, like SQL Server, allow you to write the business logic using stored-procedures and/or triggers, send it to the server for execution directly within the database engine. 使用传统关系数据库时需要处理两种不同的编程语言 - 非事务性应用程序编程语言(例如 JavaScript、Python、C#、Java 等)和由数据库本机执行的事务性编程语言(例如 T-SQL)。With traditional relational databases, you are required to deal with two different programming languages the (non-transactional) application programming language such as JavaScript, Python, C#, Java, etc. and the transactional programming language (such as T-SQL) that is natively executed by the database.

Azure Cosmos DB 中的数据库引擎支持使用快照隔离且完全符合 ACID(原子性, 一致性, 隔离性, 持久性)的事务。The database engine in Azure Cosmos DB supports full ACID (Atomicity, Consistency, Isolation, Durability) compliant transactions with snapshot isolation. 容器逻辑分区范围内的所有数据库操作都以事务方式在分区副本托管的数据库引擎内执行。All the database operations within the scope of a container's logical partition are transactionally executed within the database engine that is hosted by the replica of the partition. 这些操作同时包括写入(更新逻辑分区内的一个或多个项)和读取操作。These operations include both write (updating one or more items within the logical partition) and read operations. 下表说明了不同的操作和事务类型:The following table illustrates different operations and transaction types:

操作Operation 操作类型Operation Type 单项或多项事务Single or Multi Item Transaction
插入(不带前/后触发器)Insert (without a pre/post trigger) 写入Write 单项事务Single item transaction
插入(带前/后触发器)Insert (with a pre/post trigger) 写入和读取Write and Read 多项事务Multi-item transaction
替换(不带前/后触发器)Replace (without a pre/post trigger) 写入Write 单项事务Single item transaction
替换(带前/后触发器)Replace (with a pre/post trigger) 写入和读取Write and Read 多项事务Multi-item transaction
Upsert(不带前/后触发器)Upsert (without a pre/post trigger) 写入Write 单项事务Single item transaction
Upsert(带前/后触发器)Upsert (with a pre/post trigger) 写入和读取Write and Read 多项事务Multi-item transaction
删除(不带前/后触发器)Delete (without a pre/post trigger) 写入Write 单项事务Single item transaction
删除(带前/后触发器)Delete (with a pre/post trigger) 写入和读取Write and Read 多项事务Multi-item transaction
执行存储过程Execute stored procedure 写入和读取Write and Read 多项事务Multi-item transaction
系统发起的合并过程执行System initiated execution of a merge procedure 写入Write 多项事务Multi-item transaction
系统基于项的有效期 (TTL) 而发起的删除项执行System initiated execution of deleting items based on expiration (TTL) of an item 写入Write 多项事务Multi-item transaction
读取Read 读取Read 单项事务Single-item transaction
更改源Change Feed 读取Read 多项事务Multi-item transaction
分页读取Paginated Read 读取Read 多项事务Multi-item transaction
分页查询Paginated Query 读取Read 多项事务Multi-item transaction
在分页查询中执行 UDFExecute UDF as part of the paginated query 读取Read 多项事务Multi-item transaction

多项事务Multi-item transactions

借助 Azure Cosmos DB 可以采用 JavaScript 编写存储过程、前/后触发器、用户定义的函数 (UDF) 和合并过程。Azure Cosmos DB allows you to write stored procedures, pre/post triggers, user-defined-functions (UDFs) and merge procedures in JavaScript. Azure Cosmos DB 以本机方式支持在其数据库引擎内执行 JavaScript。Azure Cosmos DB natively supports JavaScript execution inside its database engine. 可以在容器上注册存储过程、前/后触发器、用户定义的函数 (UDF) 和合并过程,并稍后在 Azure Cosmos 数据库引擎内以事务方式执行它们。You can register stored procedures, pre/post triggers, user-defined-functions (UDFs) and merge procedures on a container and later execute them transactionally within the Azure Cosmos database engine. 通过采用 JavaScript 编写应用程序逻辑,可以直接使用 JavaScript 语言在数据库事务内自然表达异常处理基元的控制流、变量作用域、分配以及集成。Writing application logic in JavaScript allows natural expression of control flow, variable scoping, assignment, and integration of exception handling primitives within the database transactions directly in the JavaScript language.

基于 JavaScript 的存储过程、触发器、UDF 和合并过程包含在环境 ACID 事务中,该事务在逻辑分区内的所有项目之间使用快照隔离。The JavaScript-based stored procedures, triggers, UDFs, and merge procedures are wrapped within an ambient ACID transaction with snapshot isolation across all items within the logical partition. 在其执行过程中,如果 JavaScript 程序引发异常,则整个事务都将中止并回退。During the course of its execution, if the JavaScript program throws an exception, the entire transaction is aborted and rolled-back. 生成的编程模型简单但功能强大。The resulting programming model is simple yet powerful. JavaScript 开发人员在继续使用其自己熟悉的语言构造和库基元的同时,可以获得“持久”的编程模型。JavaScript developers get a durable programming model while still using their familiar language constructs and library primitives.

直接在数据库引擎中执行 JavaScript 的能力可实现针对容器项的数据库操作的性能和事务性执行。The ability to execute JavaScript directly within the database engine provides performance and transactional execution of database operations against the items of a container. 此外,由于 Azure Cosmos 数据库引擎本机支持 JSON 和 JavaScript,因此应用程序和数据库类型系统之间不存在任何阻抗失配。Furthermore, since Azure Cosmos database engine natively supports JSON and JavaScript, there is no impedance mismatch between the type systems of an application and the database.

乐观并发控制Optimistic concurrency control

乐观并发控制可以防止丢失更新和删除。Optimistic concurrency control allows you to prevent lost updates and deletes. 并发冲突的操作受数据库引擎的常规悲观锁定的限制,该引擎由拥有该项的逻辑分区托管。Concurrent, conflicting operations are subjected to the regular pessimistic locking of the database engine hosted by the logical partition that owns the item. 如果两个并发操作尝试更新逻辑分区内的一个最新版的项,则其中一个会成功,而另一个将失败。When two concurrent operations attempt to update the latest version of an item within a logical partition, one of them will win and the other will fail. 但是,如果尝试同时更新相同项的一个或两个操作以前读取过该项的较旧值,则该数据库无法确定这一个或两个冲突操作以前读取的值是否确实是该项的最新值。However, if one or two operations attempting to concurrently update the same item had previously read an older value of the item, the database doesn't know if the previously read value by either or both the conflicting operations was indeed the latest value of the item. 幸运的是,在允许这两个操作进入数据库引擎内的事务边界之前,可以使用乐观并发控制 (OCC) 检测这种情况。Fortunately, this situation can be detected with the Optimistic Concurrency Control (OCC) before letting the two operations enter the transaction boundary inside the database engine. OCC 可防止数据意外覆盖其他人所做的更改。OCC protects your data from accidentally overwriting changes that were made by others. 它还可以防止其他人意外覆盖你自己的更改。It also prevents others from accidentally overwriting your own changes.

Azure Cosmos DB 的通信协议层对项的并发更新受 OCC 限制。The concurrent updates of an item are subjected to the OCC by Azure Cosmos DB's communication protocol layer. Azure Cosmos 数据库可确保要更新(或删除)项的客户端版本与 Azure Cosmos 容器中该项的版本相同。Azure Cosmos database ensures that the client-side version of the item that you are updating (or deleting) is the same as the version of the item in the Azure Cosmos container. 这可保证你的写入内容不会被他人的写入内容意外覆盖,反之亦然。This guarantees that your writes are protected from being overwritten accidentally by the writes of others and vice versa. 在多用户环境中,乐观并发控制可防止意外删除或更新项的错误版本。In a multi-user environment, the optimistic concurrency control protects you from accidentally deleting or updating wrong version of an item. 在这种情况下,可防止项出现令人痛恨的“丢失更新”或“丢失删除”问题。As such, items are protected against the infamous "lost update" or "lost delete" problems.

存储在 Azure Cosmos 容器中的每个项都具有系统定义的 _etag 属性。Every item stored in an Azure Cosmos container has a system defined _etag property. 每次更新项时,_etag 的值都由服务器自动生成和更新。The value of the _etag is automatically generated and updated by the server every time the item is updated. _etag 可与客户端提供的 if-match 请求标头配合使用,使服务器能够决定是否可以条件性地更新某项。_etag can be used with the client supplied if-match request header to allow the server to decide whether an item can be conditionally updated. 如果 if-match 标头的值与服务器上的 _etag 的值匹配,则会更新该项。The value of the if-match header matches the value of the _etag at the server, the item is then updated. 如果 if-match 请求标头值不再是最新值,则服务器会拒绝该操作,并提供“HTTP 412 前置条件失败”响应消息。If the value of the if-match request header is no longer current, the server rejects the operation with an "HTTP 412 Precondition failure" response message. 然后客户端可重新提取该项,以在服务器上获取该项的当前版本,或用该项自己的 _etag 值覆盖服务器中该项的版本。The client then can re-fetch the item to acquire the current version of the item on the server or override the version of item in the server with its own _etag value for the item. 此外,_etag 可以与 if-none-match 标头配合使用,以确定是否需要重新提取资源。In addition, _etag can be used with the if-none-match header to determine whether a refetch of a resource is needed.

每次更新项时,项的 _etag 值都会发生更改。The item's _etag value changes every time the item is updated. 对于替换项操作,必须在请求选项中显式表达 if-matchFor replace item operations, if-match must be explicitly expressed as a part of the request options. 有关示例,请参阅 GitHub 中的示例代码。For an example, see the sample code in GitHub. 将对存储过程接触的所有写入项隐式检查 _etag 值。_etag values are implicitly checked for all written items touched by the stored procedure. 如果检查到任何冲突,存储过程将回退事务并引发异常。If any conflict is detected, the stored procedure will roll back the transaction and throw an exception. 通过此方法,将以原子方式应用存储过程中的所有写入内容或不应用任何写入内容。With this method, either all or no writes within the stored procedure are applied atomically. 这是应用程序重新应用更新并重试原始客户端请求的信号。This is a signal to the application to reapply updates and retry the original client request.

后续步骤Next steps

请通过以下文章详细了解数据库事务和乐观并发控制:Learn more about database transactions and optimistic concurrency control in the following articles: