《MongoDB高手课》学习记录(第十八天)

第十八天

今天要学习的章节是《21 | 事务开发:读操做事务之二》,继续昨天的话题,昨天讲的是从哪读readPreference,今天讲的是要读什么样的数据readConcern。数据库

什么是 readConcern?

在 readPreference 选择了指定的节点后,readConcern 决定这个节点上的数据哪些是可读的,相似于关系数据库的隔离级别。可选值包括:安全

  • available:读取全部可用的数据;
  • local:读取全部可用且属于当前分片的数据,这个是默认值;
  • majority:读取在大多数节点上提交完成的数据;
  • linearizable:可线性化读取文档;
  • snapshot:读取最近快照中的数据,这个在第三章中讲;

readConcern: local 和 available

image.png
在复制集中 local 和 available 是没有区别的。二者的区别主要体如今分片集上。考虑如下场景:oracle

  • 一个 chunk x 正在从 shard1 向 shard2 迁移;
  • 整个迁移过程当中 chunk x 中的部分数据会在 shard1 和 shard2 中同时存在,但源分片 shard1仍然是chunk x 的负责方:
  • 全部对 chunk x 的读写操做仍然进入 shard1;
  • config 中记录的信息 chunk x 仍然属于 shard1;
  • 此时若是读 shard2,则会体现出 local 和 available 的区别:
  • local:只取应该由 shard2 负责的数据(不包括 x);
  • available:shard2 上有什么就读什么(包括 x);

readConcern: local 和 available

注意事项:分布式

  • 虽然看上去老是应该选择 local,但毕竟对结果集进行过滤会形成额外消耗。在一些可有可无的场景(例如统计)下,也能够考虑 available;
  • MongoDB <=3.6 不支持对从节点使用 {readConcern: "local"};
  • 从主节点读取数据时默认 readConcern 是 local,从从节点读取数据时默认readConcern 是 available(向前兼容缘由)。

readConcern: majority

说白了,就是超过一半的节点数据提交成功,才会读取出
image.png学习

readConcern: majority 的实现方式

image.png
考虑 t3 时刻的 Secondary1,此时:spa

  • 对于要求 majority 的读操做,它将返回 x=0;
  • 对于不要求 majority 的读操做,它将返回 x=1;

如何实现?
节点上维护多个 x 版本,MVCC 机制
MongoDB 经过维护多个快照来连接不一样的版本:code

  • 每一个被大多数节点确认过的版本都将是一个快照;
  • 快照持续到没有人使用为止才被删除;

实验: readConcern : ”majority” vs “local”server

  • 安装 3 节点复制集。
  • 注意配置文件内 server 参数 enableMajorityReadConcern

image.png

  • 将复制集中的两个从节点使用 db.fsyncLock() 锁住写入(模拟同步延迟)

readConcern 验证

  • db.test.save({“A”:1})
  • db.test.find().readConcern(“local”)
  • db.test.find().readConcern(“majority”)
  • 在某一个从节点上执行 db.fsyncUnlock()
  • 结论:
  • 使用 local 参数,则能够直接查询到写入数据
  • 使用 majority,只能查询到已经被多数节点确认过的数据
  • update 与 remove 与上同理

readConcern: majority 与脏读

这个其实就和oracle的事务一个样
MongoDB 中的回滚:blog

  • 写操做到达大多数节点以前都是不安全的,一旦主节点崩溃,而从节还没复制到该次操做,刚才的写操做就丢失了;
  • 把一次写操做视为一个事务,从事务的角度,能够认为事务被回滚了。

因此从分布式系统的角度来看,事务的提交被提高到了分布式集群的多个节点级别的“提交”,而再也不是单个节点上的“提交”。
在可能发生回滚的前提下考虑脏读问题:事务

  • 若是在一次写操做到达大多数节点前读取了这个写操做,而后由于系统故障该操做回滚了,则发生了脏读问题;

使用 {readConcern: “majority”} 能够有效避免脏读

readConcern: 如何实现安全的读写分离

image.png
考虑以下场景:

向主节点写入一条数据;
当即从从节点读取这条数据。

如何保证本身可以读到刚刚写入的数据?

下述方式有可能读不到刚写入的订单

db.orders.insert({ oid: 101, sku: ”kite", q: 1})
db.orders.find({oid:101}).readPref("secondary")

使用 writeConcern + readConcern majority 来解决

db.orders.insert({ oid: 101, sku: "kiteboar", q: 1}, {writeConcern:{w: "majority”}})
db.orders.find({oid:101}).readPref(“secondary”).readConcern("majority")

readConcern: linearizable

只读取大多数节点确认过的数据。和 majority 最大差异是保证绝对的操做线性顺序,在写操做天然时间后面的发生的读,必定能够读到以前的写

  • 只对读取单个文档时有效;
  • 可能致使很是慢的读,所以老是建议配合使用 maxTimeMS;

image.png

readConcern: snapshot

{readConcern: “snapshot”} 只在多文档事务中生效。将一个事务的 readConcern
设置为 snapshot,将保证在事务中的读:

  • 不出现脏读;
  • 不出现不可重复读;
  • 不出现幻读。

由于全部的读都将使用同一个快照,直到事务提交为止该快照才被释放。

readConcern: 小结

  • available:读取全部可用的数据
  • local:读取全部可用且属于当前分片的数据,默认设置
  • majority:数据读一致性的充分保证,可能你最须要关注的
  • linearizable:加强处理 majority 状况下主节点失联时候的例外状况
  • snapshot:最高隔离级别,接近于 Seriazable
相关文章
相关标签/搜索