MongoDB 8.0——事务

MongoDB 8.0——事务 事务1、事务基础原理2、驱动程序API2.1、回调API2.2、核心API2.3、事务错误处理2.3.1、TransientTransactionError2.3.2、UnknownTransactionCommitResult2.3.3、TransactionTooLargeForCache3、事务与操作3.1、事务操作基础3.2、在事务中创建集合和索引3.3、计数 限制性与去重操作4、读取偏好与读写关注4.1、事务和读取偏好4.2、事务和读关注4.3、事务和写关注事务是传统数据库所具备的一项基本能力其根本目的是为数据的可靠性和一致性提供保障。在通常的实现中事务包含一个系列的数据库读写操作这些操作要么全部完成要么全部撤销。例如在电子商城场景中当顾客下单购买某件商品时除生成订单外还应该同时扣减商品的库存这些操作应该被作为一个整体的执行单元进行处理否则会产生数据不一致的情况。在MongoDB数据库中对单个文档的操作具有原子性。因为在单个文档结构中使用内嵌文档和数据可以获得数据之间的关系所以不必跨多个文档和集合进行范式化这种结构特性避免了在大多数场景中对多文档事务的需求。1、事务基础原理MongoDB数据支持对多个文档无论是单个集合还是多个集合的读写操作具有原子性并且支持多文档分布式的事务。利用分布式事务可以跨多个操作、集合、数据库、文档和分片执行事务。在下面的代码示例中将重点介绍事务API的关键组件其使用了回调API。回调API主要包括以下步骤步骤1启动事务。步骤2执行指定操作。步骤3提交结果或在出错时中止​。注意回调API包含特定错误的重试逻辑。示例代码如下上述代码的解释参见代码中的注释这里就不展开了。MongoDB数据库的事务和原子性支持对多个文档无论是单个集合还是多个集合进行原子性读取和写入的情况。MongoDB支持分布式事务包括副本集和分片集群上的事务。关于分布式事务的原子性说明如下事务要么应用所有数据更改要么回滚(Callback)更改。在提交事务时事务中所进行的所有数据更改都会保存并且在事务之外可见。在提交事务前在事务中所进行的数据更改在事务外不可见。不过当事务写入多个分片时并非所有外部读取操作都需等待已提交事务的结果在各个分片上可见。当事务中止后在事务中所进行的所有数据更改都会被丢弃并且不会变得可见。事务中的任何操作失败事务都会中止事务中所进行的所有数据更改将被丢弃并且不会变得可见。在大多数情况下与单文档写入操作相比分布式事务会产生更高的性能成本并且分布式事务的可用性不应取代有效的模式设计。在许多情况下非规范化数据模型嵌入式文档和数组仍然是数据和使用案例的最佳选择。换言之对于许多场景适当的数据建模将最大限度地减少对分布式事务的需求。2、驱动程序APIMongoDB数据库事务的驱动程序API主要包括回调(Callback)API、核心(Core)API以及事务错误处理等方面的内容。2.1、回调APIMongoDB数据库的回调API(Callback API)的主要功能是启动事务、执行指定操作并提交或在出错时中止​。回调API自动包含TransientTransactionError和UnknownTransactionCommitResult的错误处理逻辑。回调API主要包含以下逻辑如果事务遇到TransientTransactionError错误则将事务作为一个整体进行重试。如果提交操作遇到UnknownTransactionCommitResult错误则重试提交操作。从MongoDB 6.2版本开始服务器在收到TransactionTooLargeForCache错误时不会重试事务。在下面的代码示例中使用新的回调API来处理事务具体操作是启动事务、执行指定操作并提交或在出错时中止​。新的回调API包含针对TransientTransactionError或UnknownTransactionCommitResult提交错误的重试逻辑。具体代码如下有关上述代码的说明可参见代码中的注释。2.2、核心APIMongoDB数据库的核心API的主要功能是需要显式调用以启动并提交事务。核心API不包含对TransientTransactionError和UnknownTransactionCommitResult的错误处理逻辑而是提供对这些错误进行自定义错误处理的灵活性。核心API不包含标记为以下错误的重试逻辑。TransientTransactionError如果事务中的操作返回标记为TransientTransactionError的错误则可以将事务作为一个整体进行重试。如果要处理TransientTransactionError则应用程序应显式包含该错误的重试逻辑。UnknownTransactionCommitResult如果提交返回标记为UnknownTransactionCommit Result的错误则可以重试提交。如果要处理UnknownTransactionCommitResult则应用程序应显式包含该错误的重试逻辑。在下面的代码示例中包含在出现暂时性错误时重试事务的逻辑以及在出现未知提交错误时重试提交的逻辑。如果要将读取和写入操作与事务关联则必须将会话传递给事务中的每个操作。具体代码如下2.3、事务错误处理无论是哪种数据库系统包括非关系数据库或关系数据库​应用程序都应采取相应措施来处理事务提交期间的错误并包含事务的重试逻辑。2.3.1、TransientTransactionError无论retryWrites的值如何事务中的单独写入操作均不可重试。如果操作遇到与标签相关的TransientTransactionError错误例如主节点降级时​可以将事务作为一个整体进行重试。回调API包含TransientTransactionError错误的重试逻辑。核心事务API不包含TransientTransactionError的重试逻辑。如果要处理TransientTransactionError则应用程序应显式包含错误的重试逻辑。2.3.2、UnknownTransactionCommitResult提交操作是可以重试写入操作的。如果提交操作遇到错误那么无论retryWrites的值如何MongoDB驱动程序都会重试提交。如果提交操作遇到标记为UnknownTransactionCommitResult的错误则可以重试提交。回调API包含UnknownTransactionCommitResult的重试逻辑。核心事务API不包含UnknownTransactionCommitResult的重试逻辑如果要处理UnknownTransactionCommitResult则应用程序应显式包含错误的重试逻辑。2.3.3、TransactionTooLargeForCache从MongoDB 6.2版本开始服务器在收到TransactionTooLargeForCache错误后不会重试事务。此错误意味着缓存过小重试可能会失败。TransactionTooLargeForCacheThreshold阈值的默认值为0.75当事务使用超过75%的缓存时服务器会返回TransactionTooLargeForCache而不是重试事务。在MongoDB的早期版本中服务器会返回TemporarilyUnavailable或WriteConflict而不是TransactionTooLargeForCache。在下面的mongosh代码示例中主要省略了重试逻辑和强大的错误处理功能。具体代码如下3、事务与操作MongoDB数据库可以在跨多个操作、集合、数据库、文档和分片上使用分布式事务具体内容如下。3.1、事务操作基础MongoDB数据库在事务中创建集合和索引如果事务不是跨分片写入事务则可以在分布式事务中执行创建集合的操作并在先前同一事务中创建的新空集合上创建索引。对于MongoDB事务操作而言主要包含以下几方面的内容可以在事务中创建集合和索引。事务中使用的集合可以位于不同的数据库中。注意设计人员无法在跨分片写事务中创建新集合。不能写入固定大小集合。从固定大小集合读取时不能使用读关注snapshot从MongoDB 5.0版本开始​。不能在config、admin或local数据库中读取/写入集合。不能写入system.*集合。不能使用explain或类似命令返回受支持操作的查询计划。对于在ACID事务外部创建的游标无法在ACID事务内部调用getMore。对于在事务中创建的游标无法在事务外部调用getMore。不能将killCursors指定为事务中的第一个操作。3.2、在事务中创建集合和索引在MongoDB事务中创建集合时可以隐式创建一个集合。例如对不存在的集合进行插入操作或对不存在的集合使用upsert: true进行update/findAndModify操作。同时可以使用create命令或其辅助程序db.createCollection()显式创建集合。同时创建集合和索引有如下几项限制。无法在跨分片写事务中创建新集合。例如如果在一个分片中写入一个现有集合并在另一个分片中隐式创建一个集合MongoDB将无法在同一事务中执行这两个操作。当以分片集合为目标时无法在事务中使用$graphLookup阶段。如果要在事务中显式创建集合或索引则事务读关注级别必须为local。3.3、计数 限制性与去重操作如果要在MongoDB事务中执行计数操作可以使用$count聚合阶段或$group带有$sum表达式聚合阶段。MongoDB驱动程序提供集合级API方法countDocuments(filter, options)作为辅助方法该方法使用$group和$sum表达式来执行计数。mongosh工具提供db.collection.countDocuments()辅助方法该方法使用$group和$sum表达式进行计数。如果要在事务中执行不同的操作对于未分片的集合可以使用db.collection.distinct()方法、distinct命令以及带有$group阶段的聚合管道。对于分片集合不能使用db.collection.distinct()方法或distinct命令。如果要查找分片集合的不同值可以改用带有$group阶段的aggregation pipeline。例如不使用db.coll.distinct(“x”)而是使用如下方法不使用db.coll.distinct(“x”, { status: “A” })而是使用如下方法管道返回一个指向文档的游标{distinctValues:[2,3,1]}以上迭代游标用于访问结果文档。MongoDB事务中有几项限制性操作具体内容如下在跨分片写事务中创建新集合。例如如果在一个分片中写入一个现有集合并在另一个分片中隐式创建一个集合那么MongoDB将无法在同一事务中执行这两项操作。使用local以外的读关注级别时显式创建集合例如db.createCollection()方法和索引例如db.collection.createIndexes()和db.collection.createIndex()方法​。listCollections和listIndexes命令及其辅助方法。其他非CRUD和非信息性操作例如createUser、getParameter和count及其辅助程序。4、读取偏好与读写关注4.1、事务和读取偏好MongoDB数据库事务中的操作使用事务级读取偏好。通过使用驱动程序设计人员可以在事务启动时设置事务级读取偏好具体内容如下如果未设置事务级别的读取偏好则事务将使用会话级别的读取偏好。如果未设置事务级别和会话级别的读取偏好则事务将使用客户端级别的读取偏好。默认情况下客户端级别的读取偏好为primary。包含读取操作的分布式事务必须使用读取偏好primary。给定事务中的所有操作都必须路由到同一节点。4.2、事务和读关注MongoDB数据库事务中的操作使用事务级读关注也就是在集合和数据库级别设置的任何读关注在事务中都会被忽略。设计人员可以在事务启动时设置事务级别的读关注具体内容如下如果未设置事务级别的读关注则事务级别的读关注默认为会话级别的读关注。如果未设置事务级的读关注和会话级的读关注则事务级的读关注默认为客户端级的读关注。默认情况下对于主节点上的读取客户端级的读关注是local。关于MongoDB数据库事务支持的读关注级别具体内容介绍如下。local读关注local返回节点中可用的最新数据但可以回滚。在副本集上即使事务使用读关注local也可能会观察到更强的读隔离性其中该操作从事务打开时的快照中读取。对于分片集群上的事务读关注local无法保证数据来自跨分片的同一快照视图。可以在事务中创建集合和索引。如果显式创建集合或索引则事务必须使用读关注local。如果隐式创建集合则可以使用任何可用于事务的读关注。majority如果事务以写关注majority提交则读关注majority返回已被多数副本集节点确认且无法回滚的数据。否则读关注majority不保证读取操作会读取多数副本集中所提交的数据。对于分片集群上的事务读关注majority无法保证数据来自跨分片的同一快照视图。snapshot如果事务使用写关注majority提交则读关注snapshot会从多数已提交数据的快照中返回数据。如果事务不使用写关注majority提交则snapshot读关注不保证读操作会使用大多数已提交数据的快照。对于分片集群上的事务数据的snapshot视图会在各分片之间同步。4.3、事务和写关注MongoDB数据库事务使用事务级的写关注来提交写入操作。事务内的写入操作必须在没有明确写关注规范的情况下执行并且必须使用默认的写关注。在提交时使用事务级的写关注来提交写入。设计人员可以在事务启动时设置事务级的写关注具体内容如下如果未设置事务级的写关注则事务级的写关注默认为提交的会话级的写关注。如果未设置事务级的写关注和会话级的写关注则事务级的写关注默认为客户端级的写关注具体定义如下。w: majority在MongoDB 5.0及更高版本中包含仲裁节点的部署与之前版本有所不同。w: 1。关于事务支持所有写关注的w值具体定义如下w: 1写关注w: 1会在提交应用于主节点后返回确认信息。使用w: 1提交时如果发生故障转移则可以回滚事务。使用w: 1写入关注提交时事务级majority读关注无法保证事务中的读操作会读取大多数已提交数据。使用w: 1写关注提交时事务级snapshot读关注无法保证事务中的读操作会使用大多数已提交数据的快照。w: “majority”在将提交应用于大多数投票节点后写关注w:majority会返回确认消息。使用w: majority写关注提交时事务级majority读关注可以保证操作已读取大多数已提交数据。对于分片集群上的事务大多数已提交数据的视图不会在各分片之间同步。使用w: majority写关注提交时事务级snapshot读关注可以保证操作已从大多数已提交数据的同步快照中读取。