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 之後啓動新的更改流 。