Mongodb的事务通常是指多文档之间,Mongodb从4.0版本开始支持副本集的多行多文档事务,4.2版本开始支持分布式事务,增加了分片集群上多行多文档事务的支持。
关系型数据库事务属性 | mongodb支持程度 |
Atomocity 原子性 | 单表单文档 1.x开始支持 分片集群多表多行:4.2 |
Consistency一致性 | writeConcern,readConcern (3.2开始) |
Isolation 隔离性 | readConcern (3.2开始) |
Durability 持久性 | Journal and replication |
Write Concern
writeConcern决定一个写操作落到多少个节点上才算成功,writeConcern的取值包括:
- w:0 设置为0 无需关心写入成功与否
- w :1-任意节点数,最大数为复制集群节点数,默认为1 ,表示写到Primary节点就开始往客户端发送确认写入成功。
- w:majority 大多数节点成功原则,非具体数据,复制集群中的大多数。
- w:all 所有节点写入成功才算成功
- j:true 默认情况为false,写操作达到内存算完成,如果设置为j:true,写操作只有到达journal文件才算成功。
- wtimeout:写入超时实战
测试示例
writeConcern: {
w:"majority" // 大多数
j:true,
wtimeout: 6000,
}
建议:重要数据设置 w:"majority" 保证数据不丢失,普通数据设置w:1保证最优性能
Read Preference
- primary: 只选择主节点;
- primaryPreferred:优先选择主节点,如果不可用选择从节点
- secondary:只选择从节点
- secondaryPreferred:优先选择从节点,如果从节点不可用则选择主节点;
- nearest:选择最近的节点;
primary/primaryPreferred:适合于数据实时性要求较高的场景,例如,订单创建完毕直接跳转到订单详情,如果选择从节点读取,可能会造成主节点数据写入之后,从节点还未复制的情况,因为复制过程是一个异步的操作。
secondary/secondaryPreferred:适应用于数据实时性要求不高的场景,例如,报表数据、历史订单。还可以减轻对主节点的压力。
配置示例
mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/admin?replicaSet=myRepl&readPreference=secondary
Read Concern
MongoDB 3.2 引入了 readConcern 来决定读取的策略,但是与 readPreference 不同,readPreference 决定从哪个节点读取,readConcern 决定该节点的哪些数据是可读的。主要保证事务中的隔离性,避免脏读。可选值如下:
- available:读取所有可用的数据;
- local:读取所有可用且属于当前分片的数据,默认设置
- majority:读取在大多数节点上提交完成的数据;
- linearizable:可线性化读取文档;只对读取单个文档时有效;
- snapshot:读取最近快照中的数据;最高隔离级别,接近于 Seriazable,只在多文档事务中生效
使用示例:
在启动 Mongod 实例时,指定 --enableMajorityReadConcern 选项或在配置文件中配置
enableMajorityReadConcern=true
使用示例
db.user.find().readConcern("majority")
MongoDB 的 readConcern 默认情况下是脏读,例如,用户在主节点读取一条数据之后,该节点未将数据同步至其它从节点,就因为异常挂掉了,待主节点恢复之后,将未同步至其它节点的数据进行回滚,就出现了脏读。
从主节点读取数据时默认 readConcern 是 local,从从节点读取数据时默认
readConcern 是 available(向前兼容原因)。
MongoDB <=3.6 不支持对从节点使用 {readConcern: "local"};
readConcern 级别的 majority 可以保证读到的数据已经落入到大多数节点。所以说保证了事务的隔离性,所谓隔离性也是指事务内的操作,事务外是看不见的。
在复制集中 local 和 available 是没有区别的。两者的区别主要体现在分片集上。考虑以下场景:
一个 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);