1

第十九天

今天要学习的章节是《22 | 事务开发:多文档事务》,主要讲解多文档事务管理。

说明

  • MongoDB 在 4.2 开始全面支持了多文档事务.
  • 对事务的使用原则应该是:能不用尽量不用。
  • 通过合理地设计文档模型,可以规避绝大部分使用事务的必要性
  • 为什么?事务 = 锁,节点协调,额外开销,性能影响

MongoDB ACID 多文档事务支持

  • Atomocity(原子性)
    单表单文档 : 1.x 就支持,文档中的字段要么多更新,要么不更新,不会出现一部分更新。
    复制集多表多行:4.0开始支持
    分片集群多表多行:4.2开始支持
  • Consistency(一致性)
    writeConcern, readConcern (3.2版本开始支持)
  • Isolation(隔离性)
    readConcern (3.2版本开始支持)
  • Durability(持久性)
    Journal and Replication

事务的隔离级别

  • 事务完成前,事务外的操作对该事务所做的修改不可访问
  • 如果事务内使用 {readConcern: “snapshot”},则可以达到可重复读Repeatable Read

事务隔离

即事务内的变化没提交前不影响事务外的数据

db.tx.insertMany([{ x: 1 }, { x: 2 }]);
var session = db.getMongo().startSession();
session.startTransaction();
var coll = session.getDatabase('test').getCollection("tx");
coll.updateOne({x: 1}, {$set: {y: 1}});
coll.findOne({x: 1}); // 返回 {x:1, y:1}
db.tx.findOne({x: 1}); // 返回 {x:1}
session.abortTransaction()

可重复读

事务外虽然数据已经更新,在事务内结束前,读取的数据一致是相同的。

var session = db.getMongo().startSession();
session.startTransaction({
readConcern: {level: "snapshot"},
writeConcern: {w: "majority"}});
var coll = session.getDatabase('test').getCollection("tx");
coll.findOne({x: 1}); // 返回:{x: 1}
db.tx.updateOne({x: 1}, {$set: {y: 1}});
db.tx.findOne({x: 1}); // 返回:{x: 1, y: 1}
coll.findOne({x: 1}); // 返回:{x: 1}
session.abortTransaction();

事务写机制

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

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

实验:写冲突

var session = db.getMongo().startSession();
session.startTransaction({ readConcern: {level: "snapshot"},
writeConcern: {w: "majority"}});
var coll = session.getDatabase('test').getCollection("tx");

继续使用上个实验的tx集合,开两个 mongo shell 均执行下述语句
窗口1:
coll.updateOne({x: 1}, {$set: {y: 1}}); // 正常结束
窗口2:
coll.updateOne({x: 1}, {$set: {y: 2}}); // 异常 – 解决方案:重启事务

实验:写冲突 (续)

窗口1:第一个事务,正常提交
coll.updateOne({x: 1}, {$set: {y: 1}});
窗口2:另一个事务更新同一条数据,异常
coll.updateOne({x: 1}, {$set: {y: 2}});
窗口3:事务外更新,需等待
db.tx.updateOne({x: 1}, {$set: {y: 3}})

注意事项

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

xiaopohair
68 声望26 粉丝

把这辈子活的热气腾腾!