mongodb 事务学习总结
一、预备工作
1.MongoDB需要4.0版本+
2.需要自己搭建MongoDB复制集,单个mongodb server 不支持事务。
事务原理:mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。mongodb各个节点常见的搭配方式为:一主一从、一主多从。主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
3.搭建复制集步骤
- 启动mongo主节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8080端口的数据库文件位于db1目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
1
mongod --replSet doudou --dbpath=E:\mongoDb\data\db1 --port=8080
- 启动mongo从节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8081端口的数据库文件位于db2目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
1
mongod --replSet doudou --dbpath=E:\mongoDb\data\db2 --port=8081
- 两个节点启动后,bin目录下打开命令窗口,连接主节点
1
mongo --port=8080
- 命令初始化
1
rs.initiate()
- 两个节点启动后,bin目录下打开命令窗口,连接主节点
1
mongo --port=8080
成功后如图:
-
命令初始化
1rs.initiate()
效果如图:
成功的结果是(ok项是1,失败是0)
查看是否是主节点 rs.isMaster()
查看复制集状态 rs.status()
- 初始化配置
1
rs.conf()
- 向主节点添加从节点
1
rs.add(
"localhost:8081"
)
- 查看主节点状态
1
rs.status()
复制集配置完成。
二、复制集中的坑点
1.需使用mongoose.connection对集合进行事务操作,其他model的CRUD方法不支持事务
1
|
mongoose.connection.collection( '集合名' ) // 注:集合名需要小写且加s,如model为Cat,集合名这里应写为cats |
2.触发Schema定义的中间件默认值需要构造model实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
const CatSchema = new Schema({ name: { type: String default : 'cat' }, created: { type: Date, default : Date.now } }) const Cat = mongoose.model( 'Cat' , CatSchema) new Cat() // 触发中间件 |
3.insertOne,findOneAndUpdate等方法对数据的新增,需上面第二点进行依赖,否则直接insertOne 插入一条数据,定义的默认值不会触发,如created字段,chema内部定义的type: Schema.ObjectId的相应字段,insertOne插入后都会变成字符串类型,不是Schema.ObjectId类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 解决方式 //新增 const Cat= new Cat(); const data = {name: 5} for ( let key in data) { Cat[key] = data[key]; } db.collection( 'cats' ).insertOne(Cat); // 查询修改 db.collection( 'cats' ) .findOneAndUpdate({_id: mongoose.Types.ObjectId(你的id)}, {$set: {name: 修改值}}) |
三、开始事务
注:以下皆为egg实例代码
- 封装获取session函数
12345678910
// 获取session,回滚事务
async getSession(opt = {
readConcern: { level:
"snapshot"
},
writeConcern: { w:
"majority"
}
}) {
const { mongoose } =
this
.app
const session = await mongoose.startSession(opt);
await session.startTransaction();
return
session
}
-
执行事务逻辑
12345678910111213141516171819202122const { mongoose } =
this
.ctx.app;
const session = await
this
.ctx.getSession();
const db = mongoose.connection;
try
{
const data =
this
.ctx.request.body;
const Cat =
new
this
.ctx.model.Cat();
for
(
let
key
in
data) {
Cat[key] = data[key]
}
await db
.collection(
'cats'
)
.insertOne(Cat, { session });
// 提交事务
await session.commitTransaction();
this
.ctx.end();
}
catch
(err) {
// 回滚事务
await session.abortTransaction();
this
.ctx.logger.error(
new
Error(err));
} finally {
await session.endSession();
}
with client.start_session() as s: s.start_transaction() collection_one.insert_one(doc_one, session=s) collection_two.insert_one(doc_two, session=s) s.commit_transaction()
try (ClientSession clientSession = client.startSession()) { clientSession.startTransaction(); collection.insertOne(clientSession, docOne); collection.insertOne(clientSession, docTwo); clientSession.commitTransaction(); }
> s = db.getMongo().startSession() session { "id" : UUID("3bf55e90-5e88-44aa-a59e-a30f777f1d89") } > s.startTransaction() > session.getDatabase("mytest").coll01.insert({x: 1, y: 1}) WriteResult({ "nInserted" : 1 }) > session.getDatabase("mytest").coll02.insert({x: 1, y: 1}) WriteResult({ "nInserted" : 1 }) > s.commitTransaction() (或者 s.abortTransaction()回滚事务)