早期的mongodb 在使用中,对于事务和隔离级别的问题并未被经常提及,随着MONGODB 的版本的不断的迭代,事务和隔离级别在MONGODB 中被提及,并有一种越来越多的趋势。诚然MOGNODB 本身已经支持跨库,跨表的事务。
MONGODB 拥有事务的功能以及隔离级别,是从2018年夏天发布的 MOGNODB 4.0 开始的,之前的版本是没有事务与隔离级别以及达到传统数据库的 ACID。为什么MOGNODB 开始有了事务,主要原因
业务的场景需要,由于MOGNODB 已经侵占了传统数据库的业务的市场,在传统设计中的多表查询和多表数据的写入的原子性的问题,在MONGODB 的设计中,虽然可以通过在一个 document 通过嵌套的方式来完成,但这限制了mongodb 在更多关键业务中的使用的范围,以及设计的难度,所以MONGODB 也在 4.0 后加入了事务。
但mongodb 也有与传统数据库不同的区别与在使用中的挑战,主要的成因在于MONGODB 是一个分布式数据库,他需要考虑从库的数据问题,以及从库与主库的数据的一致性的问题,与单体数据库根本不考虑从库的事务以及数据的隔离性与主库是否有关,MONGODB 需要考虑这个问题,这也导致分布式的MOGNODB 在这里面的 ISOLATION 也需要考虑部分基于分布式协议的成型事务与隔离级别的方式。
但需要注意的是,任何考虑使用事务的情况下,尤其又陷入了分布式的事务的情况下,性能都是一个不能不正视的问题,也就是使用了事务,并且在MONGODB 不加控制的使用事务,对于性能是一个挑战。
首先在使用事务之前,需要问一个问题,到底你需要一个事务加入到mongodb中吗?
1 在你的应用中,在MOGNODB 使用的过程中,是否运行脏读和幻读的存在,换句话说,在MONGODB 中的你的业务是否有顺序性,和逻辑的顺序性。
如果你的回答是YES ,那么此时你有两个选择
1 对于逻辑的顺序性有强要求,如银行的业务,存钱,取现的顺序,不能有任何的马虎,那就需要考虑你的顺序性货逻辑顺序性是在 你的APP 中实现,还是在MONGODB 中实现。
2 在你对事务是强需求的情况下,事务的量的问题,你是频繁要使用事务的方式,还是仅仅是在少量的情况下,利用MONGODB的事务。大部分会牵扯你的写入和读取的顺序,也就是我们之前提到的 ,脏读和幻读,但就算是ORACLE 默认的ISOLATION 也是 READ COMMIT ,也就是能解决脏读的问题。
基于read Committed 情况,需要考虑如下事宜,
1 写库在 read committed 事务 committed 后,主库的数据状态
2 从库在主库 read committed 后,从库的数据状态
3 这些事务在主库从库数据执行时的 linearizable 的特性的保持
4 事务在多个节点的committed 情况与事务无法在多点执行后的回滚问题。
以上的问题已经超出了传统数据库的对于read committed 的设计范畴了,
而这还没有完, 由于MONGODB 在设计时包含了, read concern 和 write concern 这两个concerns, 所以还需要考虑 read committed 在read concern 和 write concern 之间。
1 read concern 针对 read committed 的实现还要针对事务内部和外部划分
2 write concern 针对read committed 的实现还要针对事务内部和外部划分
下面是一个关于上方的描述的关系
1 readconcern outside of a transaction (事务的执行在readconcern的外部) 以下的方式都能符合 大多数节点实现 read committed , 支持 linearizable 特性
2 readconcern inside a transaction (事务内部对于 readconcern的支持),此时我们仅仅支持大多数节点实现 read committed , 不支持 linearizable 特性
实际上很好理解,针对readconcern 外部是有序的,内部是无序的,并且在使用 snapshot isolation时都支持 RR 级别的事务。
说完readconcern ,下面就是我们的 writeconcern , 针对与writeconcern mongodb 的事务处理方式,与单个操作是一直的, 只是一个事务中会包含处理多个文档的信息。保证了 ACID中的所有选项并且完全支持事务在内部的顺序性。
下面是一个关于MONGODB 事务的展示,先申请一个事务,并且定义事务在复制集中的conern, 然后插入数据,然后查看在其他session 中从库和 主库的其他session的状态,这里面其他的session 都是无法查看到这个插入的数据的。
从库
主库的其他线程
var session = db.getMongo().startSession();
session.startTransaction({readConcern: { level: 'majority' },writeConcern: { w: 'majority' }});
var coll = session.getDatabase('test').getCollection('user');
coll.update({name: 'Jack'}, {$set: {age: 18}})
//提交
session.commitTransaction();
//不提交
session.abortTransaction();
虽然mongodb 在4.0 已经开始支持事务,但针对事务的操作的消耗和判断在高并发的数据写入中是非常消耗资源的,所以MOGNODB虽然支持事务了,也是要注意使用的范围和避免相关的事务操作中,锁的形成的。回到主题,mongodb 事务十分有必要,他节省了开发针对MONGODB 事务中的操作节省了由于MOGNODB 不支持事务而早的全部依赖应用程序来满足事务的要求。