前言

MongoDB虽然已经在4.2开始全面支持了多文档事务,但并不代表大家应该毫无节制地使用它。相反,对事务的使用原则应该是:能不用尽量不用。为什么?事务=锁,节点协调,额外开销,性能影响

通过合理地设计文档模型,可以规避绝大部分使用事务的必要性.

ACID

持久性

journal就是说我会先把数据写到日志文件里面,然后再提交到数据文件,这样的话保证宕机的时候可以从日志文件里恢复.这点和传统数据库没差别.

但在这基础上,MongoDB还提供了更高一层的持久化,它会把日志数据复制到另外一个节点,即时主节点的硬盘坏掉,也不用担心数据丢失.从这一点上来说,MongoDB在持久性方面比传统数据库更好.

事务使用 WriteConcern {j: ture} 时,MongoDB 一定会保证事务日志提交才返回,即使发生 crash,MongoDB 也能根据事务日志来恢复;而如果没有指定 {j: true} 级别,即使事务提交成功了,在 crash recovery 之后,事务的也可能被回滚掉。

隔离性

MongoDB支持各种不同级别的隔离.默认情况下MongoDB会出现脏读情况,但是可以通过{readConcern:majority}来实现多节点提交读.

如果事务内使用{readConcern:“snapshot”},则可以达到可重复读.

一致性

可以通过WriteConcern和ReadConcern结合来实现多节点的数据一致性.

原子性

MongoDB一直以来只实现了单表单文档的原子性.单表单文档的意思是一个文档里面有几十个字段的时候,在更新这几十个字段的时候,用户不会看到部分更新的现象,要么是看到集体被更新写入,要么就是一个都看不到.不会出现只有一部分数据更新的场景.

4.0实现了复制集内部的多表多行事务,4.2实现了分片集群的多表多行事务.

事务写机制

MongoDB的事务错误处理机制不同于关系数据库:

  • 当一个事务开始后,如果事务要修改的文档在事务外部被修改过,则事务修改这个文档时会触发Abort错误,因为此时的修改冲突了;
  • 这种情况下,只需要简单地重做事务就可以了;
  • 如果一个事务已经开始修改一个文档,在事务以外尝试修改同一个文档,则事务以外的修改会等待事务完成才能继续进行。

注意事项

  • 可以实现和关系型数据库类似的事务场景
  • 必须使用与MongoDB 4.2兼容的驱动;
  • 事务默认必须在60秒(可调)内完成,否则将被取消;
  • 涉及事务的分片不能使用仲裁节点;
  • 事务会影响chunk迁移效率。正在迁移的chunk也可能造成事务提交失败(重试即可) ;
  • 多文档事务中的读操作必须使用主节点读;
  • readConcern只应该在事务级别设置,不能设置在每次读写操作上。