Change Streams
一、Change Stream是什么?
Change Stream是MongoDB从3.6开始支持的新特性。
Change Stream即变更流,是MongoDB向应用发布数据变更的一种方式。即当数据库中有任何数据发生变化,应用端都可以得到通知。我们可以将其理解为在应用中执行的触发器。至于应用想得到什么数据,以什么形式得到数据,则可以通过聚合框架加以过滤和转换。
参考地址:https://mongoing.com/change-stream
官方文档地址:https://docs.mongodb.com/manual/changeStreams/
二、Change Stream的原理
我们先来看一下MongoDB复制集大致是如何工作的:
- 应用通过驱动向数据库发起写入请求;
- 在同一个事务中,MongoDB完成oplog和集合的修改;
- oplog被其他从节点拉走;
- 从节点应用得到的oplog,同样在一个事务中完成对oplog和集合的修改;
至此,复制集同步完成。可以发现,整个同步过程是依赖于oplog来进行的。如果观测oplog的变化,就能够得到所有变更的数据。change stream正是基于这个原理实现的。
三、Change Stream的使用
启动复制集,打开一个shell,订阅你需要关注的集合,并在这个shell中不断遍历这个游标以获取新数据:
rs_test:SECONDARY> var cursor = db.score.watch()
rs_test:SECONDARY> while(true){if(cursor.hasNext()){print(JSON.stringify(cursor.next()))}}
接下来打开另一个shell做CRUD的基本操作,以测试不同的Change Events输出的变更内容:
1、insert
Event
rs_test:PRIMARY> db.score.insert({uid:"uid20001",grade:5,class:1,subject:"math",score:90,createTime:Date()})
插入数据时,输出变更数据如下:
{
"_id":{
"_data":"825F02F125000000012B022C0100296E5A10044B22BC6860404A9C96301E8CEC60899E46645F696400645F02F125E7BA0AAA65B2E6F90004"
},
"operationType":"insert",
"clusterTime":{
"$timestamp":{
"t":1594028325,
"i":1
}
},
"fullDocument":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
},
"uid":"uid20001",
"grade":5,
"class":1,
"subject":"math",
"score":90,
"createTime":"Mon Jul 06 2020 17:38:45 GMT+0800"
},
"ns":{
"db":"aggtest",
"coll":"score"
},
"documentKey":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
}
}
}
2、update
Event
rs_test:PRIMARY> db.score.update({uid:"uid20001"},{$set:{class:3,updateTime:Date()}})
更新数据时,输出变更数据如下:
{
"_id":{
"_data":"825F02F615000000012B022C0100296E5A10044B22BC6860404A9C96301E8CEC60899E46645F696400645F02F125E7BA0AAA65B2E6F90004"
},
"operationType":"update",
"clusterTime":{
"$timestamp":{
"t":1594029589,
"i":1
}
},
"ns":{
"db":"aggtest",
"coll":"score"
},
"documentKey":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
}
},
"updateDescription":{
"updatedFields":{
"class":3,
"updateTime":"Mon Jul 06 2020 17:59:49 GMT+0800"
},
"removedFields":[
]
}
}
3、replace
Event
rs_test:PRIMARY> db.score.replaceOne({uid:"uid20001"},{uid:"uid20002",grade:6,class:6,subject:"chinese",score:98,createTime:Date(),updateTime:Date()})
替换文档时,输出变更数据如下:
{
"_id":{
"_data":"825F02F828000000012B022C0100296E5A10044B22BC6860404A9C96301E8CEC60899E46645F696400645F02F125E7BA0AAA65B2E6F90004"
},
"operationType":"replace",
"clusterTime":{
"$timestamp":{
"t":1594030120,
"i":1
}
},
"fullDocument":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
},
"uid":"uid20002",
"grade":6,
"class":6,
"subject":"chinese",
"score":98,
"createTime":"Mon Jul 06 2020 18:08:40 GMT+0800",
"updateTime":"Mon Jul 06 2020 18:08:40 GMT+0800"
},
"ns":{
"db":"aggtest",
"coll":"score"
},
"documentKey":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
}
}
}
4、delete
Event
rs_test:PRIMARY> db.score.deleteOne({uid:"uid20002"})
删除文档时,输出变更数据如下:
{
"_id":{
"_data":"825F02F8EB000000012B022C0100296E5A10044B22BC6860404A9C96301E8CEC60899E46645F696400645F02F125E7BA0AAA65B2E6F90004"
},
"operationType":"delete",
"clusterTime":{
"$timestamp":{
"t":1594030315,
"i":1
}
},
"ns":{
"db":"aggtest",
"coll":"score"
},
"documentKey":{
"_id":{
"$oid":"5f02f125e7ba0aaa65b2e6f9"
}
}
}
5、drop
Event
rs_test:PRIMARY> db.score.drop()
删除集合时,输出变更数据如下:
{
"_id":{
"_data":"825F02FA07000000012B022C0100296E5A10044B22BC6860404A9C96301E8CEC60899E04"
},
"operationType":"drop",
"clusterTime":{
"$timestamp":{
"t":1594030599,
"i":1
}
},
"ns":{
"db":"aggtest",
"coll":"score"
}
}
//invalidate Event
{
"_id":{
"_data":"825F02FA07000000012B022C0100296F5A10044B22BC6860404A9C96301E8CEC60899E04"
},
"operationType":"invalidate",
"clusterTime":{
"$timestamp":{
"t":1594030599,
"i":1
}
}
}
6、rename
Event
rs_test:PRIMARY> db.score.renameCollection("score_new")
集合重命名时,输出变更数据如下:
{
"to":{
"db":"aggtest",
"coll":"score_new"
},
"_id":{
"_data":"825F02FC74000000012B022C0100296E5A1004377F141A69914F99ACDDFB9D89863EAE04"
},
"operationType":"rename",
"clusterTime":{
"$timestamp":{
"t":1594031220,
"i":1
}
},
"ns":{
"db":"aggtest",
"coll":"score"
}
}
//invalidate Event
{
"_id":{
"_data":"825F02FC74000000012B022C0100296F5A1004377F141A69914F99ACDDFB9D89863EAE04"
},
"operationType":"invalidate",
"clusterTime":{
"$timestamp":{
"t":1594031220,
"i":1
}
}
}
7、dropDatabase
Event
rs_test:PRIMARY> db.dropDatabase()
删除当前数据库时,输出变更数据如下:
{
"_id":{
"_data":"825F02FE76000000012B022C0100296E5A1004377F141A69914F99ACDDFB9D89863EAE04"
},
"operationType":"drop",
"clusterTime":{
"$timestamp":{
"t":1594031734,
"i":1
}
},
"ns":{
"db":"aggtest",
"coll":"score_new"
}
}
//invalidate Event
{
"_id":{
"_data":"825F02FE76000000012B022C0100296F5A1004377F141A69914F99ACDDFB9D89863EAE04"
},
"operationType":"invalidate",
"clusterTime":{
"$timestamp":{
"t":1594031734,
"i":1
}
}
}
8、invalidate
Event
对于针对集合打开的变更流,影响监视的集合的 drop event, rename event 或者 dropDatabase event 将导致 invalidate event 。
对于针对数据库打开的变更流,影响监视数据库的 dropDatabase event 将导致 invalidate event 。
invalidate event 将关闭更改流游标 。
resumeAfter
在 invalidate event (例如,集合删除或重命名)关闭流之后,不能用来恢复更改流。从MongoDB 4.2开始,您可以使用 startAfter 在 invalidate event 之后启动新的更改流 。