分享好友 站长动态首页 网站导航

一起学mongodb第五卷之事务

2022-03-31 09:45 · 头闻号数据库

前言

事务是 mongoDB 中非常核心的一个功能,在 4.0 版本以前,mongoDB 只支持单个文档的事务,在 4.0 和 4.2 版本之后,分别支持了复制集事务和分片事务,也可以说在大多数的数据库中都是非常重要的一个功能,值得我们单独拉一章去讲解。

那「怎么样在 mongoDB 中合理的使用事务来保证数据安全呢」?

后续我将会从读、写和多文档事务这三个方向去阐述。

写事务

使用 writeConcern 保证数据准确落盘。

writeConcern 中有两个选项。

w(决定一条数据落到写到多少个节点才算真正成功)。

j(决定怎样才算真正成功)。

db.collection.insert({a:1},{writeConcen:{w:"majority",j:true});

对于一些「普通数据可以使用 w:1 来确保最佳性能」,对于「重要数据可以用 w:majority 来保证数据安全」。

读事务

readPreference 来确定从哪里读。

readPreference 有几个属性。

「primiry 和 primaryPreferred 适用于对延迟敏感读较高」的数据,比如订单信息。

「secondary 和 secondaryPreferred 适用于对延迟敏感度要求较低」的数据,比如日志信息。

「nearest 适用于业务域较广的应用」,比如将业务信息同步到全球各地的节点,「中国用户会访问中国的节点,俄罗斯用户会访问俄罗斯的节点」, nearest 的判断也是比较简单的,直接是使用应用到 mongo 服务器的的 ping time 来决定。

当然,还有一种是给「服务器打标签(tag) 的方式」,比如要将读取操作定向到标记有 "name": "a"和"key": "person"的辅助节点集:

db.collection.find({}).readPref( "secondary", [ { "name": "a", "key": "person" } ] )

readConcern 来确定可以读什么样的数据。

readConcern 有几个属性。

「通过快照来维护多个不同的版本,使用 MVCC 实现」,每个被大多数节点确认过的数据就是一个快照。

有时会被阻塞,其保证如果一个线程已经完成了写入并且告知了其他线程,那么这其他的线程就可以看到这些改动。如果某一瞬间你的副本集出现了两个主节点(有一个还未来得及降级)然后你从这个老的主节点上进行读取,与此同时新的主节点上已经有了新的数据,你读到的数据就是旧数据。

loacl 和 available 的区别体现在分片集群中的 chunk 迁移上,如果读 shard2 ,loacl 不能读到 x ,但是 available 可以读到。

多文档事务

也就是说是说,mongoDB 在 4.2 版本的是有拥有了和 mysql 这种关系型数据库一样的事务能力,这对于业务的选择角度来讲,又给 mongoDB 添加了一笔浓重的色彩。

在整个数据库的分布式事务当中,还需要重点提一嘴的就是时间问题,我们先来看看会有什么问题存在。

比如有两个操作发向 a、b 两个节点。

最后我们就发现,a、b 两个节点的数据不一致了,那么 mongo 是怎么解决的呢,一般是两种方式:

mongo 采用的是「混合逻辑时钟」:

在这个混合逻辑时钟中,将物理时钟和逻辑时钟混合起来做一个全局的时间出来处理。我们的混合逻辑时钟会采用一种本地的推进方式,这个就是刚才说的一个接受的时候,他会比较本地的时间戳,然后在本地时间戳、本地真实的物理时间和收到最短 request 的时间,「三者取最大的时间,作为本地时间的一个推进」,需要说明的是,这个时间戳的分配是取决于 oplog 的时间戳。只有「当 oplog 真正写入数据的时候,本地的逻辑时钟才会向前推进」。在整个混合逻辑时钟,在整个集群中采用动态推进的方式,「每一条发送和接收的请求,都会依据请求中的时间来推进本地的时钟」,这样在全局的情况下,每个节点的混合逻辑时钟最终会趋同,趋向同一个地址,趋向同一个时间。这样的话,刚才说的时间偏差就已经不存在了,才可以在集群中做分布式事务。

再说说 mongo 提交事务的过程吧。

mongoDb 的分布式事务和 mysql 一样,也是基于「两阶段协议」。

coordinator 和所有的 shard 之间的通讯会促使所有的事务参与者得到一个协调一致的 HLC。在这种逻辑时钟一致的情况下,commit timestamp 就是全局顺序一致的。

需要关注一点,就是在对具有 prepare timestamp 的事务进行读取的时候,如果当前的事务是处于 prepare 状态的,并不确定自身的读时间戳和 prepare 状态的大小的话,需要去一直等待这个事务,等到事务提交或者 abort 以后才去会处理,这个就是刚才所说的。

mongoDB 整个事务实现的方式都是按照「读提交」这种关系来设计的,也就是说,在客户端读取数据的时候,只能读到该事务节点前已经做了 commit 的数据。

免责声明:本平台仅供信息发布交流之途,请谨慎判断信息真伪。如遇虚假诈骗信息,请立即举报

举报
反对 0
打赏 0
更多相关文章

评论

0

收藏

点赞