0%

MongoDB事务

事务是是指一系列操作,全部执行成功则提交,有一个执行失败则全部回滚,这也是事务四大特性(ACID)之一的原子性。至于其他特性,不同的DB都有不同的实现方式。

一、基础

  1. 事务是指由一系列数据库操作组成的一个完整的逻辑过程,这个过程中的所有操作要么都成功,要么都不成功。比如:常见的例子就是银行转账的例子,一次转账操作会包含多个数据库操作,而这些数据库操作需要放到一个事务当中,保证其要么都成功,要么都不成功。

    • 事务的四个特性ACID
      • 原子性(Atomicity):一个事务要么完全执行成功,要么不做任何改变。
      • 一致性(Consistency):当多个事务并行执行时,元素的属性在每个事务中保持一致。
      • 隔离性(Isolation):当多个事务同时执行时,互不影响。WiredTiger 本身支持多种不同类型的隔离级别,如读-未提交(read-uncommitted)、读-已提交(read-committed)和快照(snapshot)隔离,MongoDB默认选择的是快照隔离。
      • 持久性(Durability):一旦提交事务,数据的更改就不会丢失。
    • 脏读、不可重复读、幻读
      • 脏读
      • 不可重复读
      • 幻读
  2. MongoDB单文档原生支持原子性,也具备事务的ACID特性。MongoDB在4.0版本支持了多文档事务,包括对复制集的多表、多行的事务支持,4.2版本对分片集多表、多行事务操作进行了支持。MongoDB在事务操作中会分别使用到readConcern、writeConcern、readPreference这几个选项,用于控制Session的行为。

    • Write Concern:决定一个写操作落到多少个节点上才算成功,这决定了MongoDB是否成功写入数据,writeConcern配置项:

      • w:0:发起写入操作,不关心是否成功(适用于性能要求高,但不关注正确性的场景)

      • w:1~任意节点数:写操作需要被复制到指定节点数才算成功

      • w:majority:写操作需要被复制到大多数节点上才算成功(适用于对数据安全性要求比较高的场景,该选项会降低写入性能)

      • w:all:复制到全部节点上才算成功

        • w设置的节点数越多等待的延迟也就越大
      • 其他选项

        • j:true:默认情况j:false,写操作到达内存算作完成;如果设置为j:true,写操作只有到达journal文件才算成功。
        • wtimeout:写入超时时间,仅w的值大于1时有效。
          • 当指定w:n时数据需要成功写入n个节点才算成功,如果写入过程中有节点故障,可能导致这个条件一直不能满足从而一直不能向客户端发送确认结果,针对这种情况客户端可设置wtimeout选项来指定超时时间,当写入过程持续超过该时间仍未结束则认为写入失败
      • 示例

        1
        2
        3
        4
        5
        writeConcern: {
        w:"majority" // 大多数原则
        j:true,
        wtimeout: 5000,
        }
    • Read Preference:决定使用哪一个节点来满足正在发起的读请求,可选值包括:

      • primary:默认,只选择主节点,保证每次读到的数据都是最新的
      • primaryPerferred:优先选择主节点,如果不可用则选择从节点
      • secondary:只选择从节点
      • secondaryPerferred:优先选择从节点,如果从节点不可用则选择主节点
      • nearest:选择最近的节点,针对多区域部署的情况
        • Tag:readPreference只能控制使用一类节点,Tag则可以将节点选择控制到一个或几个具体的节点
    • Read Concern:在readPerference选择了指定节点后,readConcern决定这个节点上的数据哪些是可读的,类似于关系型数据库的隔离级别:

      • local:默认,读取所有可用且属于当前分片的数据
      • available:读取所有可用的数据
      • majority:读取在大多数节点上提交完成的数据
      • linearizable:可线性化读取文档
      • snapshot:读取最近快照中的数据
  3. Session是3.6版本引入的概念,引入其主要目的就是为实现多文档事务做准备,它本质上就是一个「上下文」。在<3.6的版本MongoDB只管理单个操作的上下文,mongod服务进程接收到一个请求即为该请求创建一个上下文(源码里对应OperationContext),然后在服务整个请求的过程中一直使用这个上下文,内容包括请求耗时统计、请求占用的锁资源、请求使用的存储快照等信息,有了Session之后就可以让多个请求共享一个上下文,让多个请求产生关联,从而有能力支持多文档事务。

    • 每个Session包含一个唯一的标识lsid,在4.0版本里用户的每个请求可以指定额外的扩展字段,主要包括:
      • lsid:请求所在Session的ID, 也称logic session id
      • txnNmuber:请求对应的事务号,事务号在一个Session内必须单调递增
      • stmtIds:对应请求里每个操作(以insert为例,一个insert命令可以插入多个文档)操作ID
  4. journal

  5. 命令行操作

    • 获取会话:session = db.getMongo().startSession()
    • 开启事务:session.startTransaction()
    • 获得集合:user = session.getDatabase("study").user
    • 操作文档:user.insert({name:"测试"})
    • 提交事务:session.commitTransaction
    • 回滚事务:session.abortTransaction()
  6. 事务相关的数据结构WT_TXN

    • id 字段:这是事务的全局唯一标识,通过分析它与具体的操作关联,就能够知道一个事务包含哪些操作。
    • snapshot_data 字段:MongoDB 使用的是快照隔离级别的事务,这个字段用于保存事务的快照信息,具体来说它会有 snap_min和snap_max 两个属性,通过这两个属性能够计算一个事务开始时的数据范围,每个事务开始时都会构造一个这样的快照。
    • commit_timestamp 字段:表示事务提交的时间。
    • durable_timestamp 字段:表示事务修改的数据已持久化的时间,与具体操作中的 durable_ts 字段关联。
    • prepare_timestamp 字段:表示事务开始准备的时间。
    • WT_TXN_OP 字段:包含事务的修改操作,用于事务回滚和生成事务日志(Journal)。
    • logrec 字段:表示事务日志的缓存,用于在内存中保存事务日志(对于 MongoDB 来说 Journal 日志就是事务日志)。
  7. WiredTiger存储引擎支持read-uncommittedread-committed和snapshot3种事务隔离级别,MongoDB启动时默认选择snapshot隔离。

    • 事务开始时系统会创建一个快照,从已提交的事务中获取行版本数据,如果行版本数据标识的事务尚未提交,则从更早的事务中获取已提交的行版本数据作为其事务开始时的值。
    • 通过事务可以看到其他还未提交的事务修改的行版本数据,但不会看到事务 id 大于 snap_max 的事务修改的数据。

二、使用

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四
  5. 参考五