整理mongodb文档:事务(一)
原文链接,个人博客 求关注,本文主要讲下怎么在mongose下使用事务,建议电脑端看
本文的开发环境为Nodejs,在‘单机模式’讲解最基本的事务概念。并没有涉及分片以及集群,后续会在介绍完副本集、分片集群之后补充。
事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
单单看概念,是很难有啥理解的。因此,我们先看看事务的四大特征,也就是经常看到的"ACID"。
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
ACID四个概念,大概理解就可以了。即使是面试,也不一定会拿出来问,但是在这儿,能让我们更好的去理解事务。
基于ACID的四个内容,我们可以举一个经典的例子:转账。
比如a给b转6块钱,步骤就分为了从a的账号扣除6块钱,从b的账户增加6块钱,增加一个从a转1块钱到b的记录。这就是一个事务的流程,也满足了上面所说的四个特征。
由于本博客的编程语言是Javascript,所以本次示范会在Nodejs的环境下开发,对于其他的编程语言,建议在官网上看下
Transactions are new in MongoDB 4.0 and Mongoose 5.2.0. Transactions let you execute multiple operations in isolation and potentially undo all the operations if one of them fails.
这段是在Mongose的网站上搬运下来的,翻译就是:
事务在MongoDB 4.0和Mongoose 5.2.0中新增的。事务允许您独立地执行多个操作。如果其中一个操作失败,则可能会撤消所有操作。
"mongoose": "^7.5.0"
首先,按照我们上面所述,我们需要准备一张表,并且将a还有b的钱准备好,各自都准备十块钱给他们
接下来,我们要写好两条sql。分别是a的账号中减少6块钱,以及b的账号增加6块钱的sql。
正常境况下,这儿可以用聚合,直接使用$add来更改钱数。但是为了演示的方便,手动将其设置为4以及16,不过下方会先人为的制造一个错误。请注意看第二十四行,本应该是16的,但我修改为了’aaa’,这就会导致出现一个错误,但是第一个sql执行成功了,a的数据被更改为了4
const sql = async () => {
// 链接db
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27018/test')
// 定义schema
const schema = new mongoose.Schema(
{ name: String, count: Number },
{ collection: 'test' })
// find方法查询
await mongoose
.model('test', schema)
.updateOne(
{ name: 'a' },
{
"$set": {
count: 4
}
})
await mongoose
.model('test', schema)
.updateOne(
{ name: 'b' },
{
"$set": { count: 'aaa' }
})
}
sql();
接下来,在我们恢复数据之后,使用事务来修改下我们的代码。
其中,我们依旧保留了28行的错误,以验证事务的ACID。修改的代码如下。
const sql = async () => {
// 链接db
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27018/test')
// 创建一个会话
const mySession = await mongoose.startSession();
//开始事务
mySession.startTransaction()
// 定义schema
const schema = new mongoose.Schema(
{ name: String, count: Number },
{ collection: 'test' })
try {
await mongoose
.model('test', schema)
.updateOne(
{ name: 'a' },
{
"$set": {
count: 4
}
}).session(mySession);
await mongoose
.model('test', schema)
.updateOne(
{ name: 'b' },
{
"$set": { count: 'aaa' }
}).session(mySession);
// 提交事务
await mySession.commitTransaction();
console.log('事务结束');
} catch (error) {
console.log("事务出错了,即将回滚");
// 事务回滚
await mySession.abortTransaction();
}
mySession.endSession();
}
sql();
一开始,我们创建了一个会话,并开始它的事务。
// 创建一个会话
const mySession = await mongoose.startSession();
//开始事务
mySession.startTransaction()
接着在每一个我们需要绑定的sql中添加到会话去
await mongoose
.model('test', schema)
.updateOne(
{ name: 'b' },
{
"$set": { count: 'aaa' }
}).session(mySession);
在完成了我们的事务后,我们需要将其提交上去
await mySession.commitTransaction();
如果失败后,我们需要回滚事务,取消掉我们的事务
await mySession.abortTransaction();
最后要有始有终,结束我们的会话
mySession.endSession();
最终的效果如下
在Mongoose下,事务便是如此,而不会将成功的sql应用到数据库中的原因是事务中的某个文档后来失败,所以该文档中的更改不会保存到MongoDB。并且该函数通知 Mongoose 更改跟踪已回滚,并将事务中更改的所有字段标记为已修改。
至此,在mongodb中的事务,我们已经通过session来实现了。建议自己敲下代码,理解下。
1.mongodb的事务是4.0以上才支持的,如果项目是很老很老的,建议小心测试再上线。部分老玩家玩mongodb很久的了,会说mongodb没有事务,也是因为4.0以上才有的.
2.在MongoDB 4.0中,仅使用WiredTiger存储引擎的副本集支持事务,如果有条件,个人建议是在4.2以上的版本开发测试。
3.分布式事务是指分片集群和副本集上的多文档事务,从MongoDB 4.2开始,多文档事务(无论是在分片集群上还是副本集上)也称为分布式事务。