如何监听MongoDB集合的变化?

时间:2012-03-13 20:10:00

标签: mongodb

我正在使用MongoDB作为数据存储创建一种后台作业队列系统。在产生工作人员处理作业之前,如何“监听”对MongoDB集合的插入?我是否需要每隔几秒轮询以查看上次是否有任何更改,或者我的脚本是否有可能等待插入发生?这是我正在研究的PHP项目,但可以随意回答Ruby或语言不可知。

11 个答案:

答案 0 :(得分:108)

你在想什么听起来很像触发器。 MongoDB对触发器没有任何支持,但有些人使用一些技巧“自己动手”。这里的关键是oplog。

在副本集中运行MongoDB时,所有MongoDB操作都会记录到操作日志(称为oplog)。 oplog基本上只是对数据所做修改的运行列表。副本通过侦听此oplog上的更改然后在本地应用更改来设置功能。

这听起来很熟悉吗?

我无法详细说明整个过程,它是几页文档,但您需要的工具可用。

首先在oplog上写一些文章   - Brief description   - Layout of the local collection(包含oplog)

您还希望利用tailable cursors。这些将为您提供一种监听更改而不是轮询它们的方法。请注意,复制使用tailable游标,因此这是一个受支持的功能。

答案 1 :(得分:95)

MongoDB具有所谓的capped collectionstailable cursors,允许MongoDB将数据推送到侦听器。

capped collection本质上是一个固定大小的集合,只允许插入。以下是创建一个的样子:

db.createCollection("messages", { capped: true, size: 100000000 })

MongoDB Tailable游标(original post by Jonathan H. Wage

<强>红宝石

coll = db.collection('my_collection')
cursor = Mongo::Cursor.new(coll, :tailable => true)
loop do
  if doc = cursor.next_document
    puts doc
  else
    sleep 1
  end
end

<强> PHP

$mongo = new Mongo();
$db = $mongo->selectDB('my_db')
$coll = $db->selectCollection('my_collection');
$cursor = $coll->find()->tailable(true);
while (true) {
    if ($cursor->hasNext()) {
        $doc = $cursor->getNext();
        print_r($doc);
    } else {
        sleep(1);
    }
}

Python Robert Stewart)

from pymongo import Connection
import time

db = Connection().my_db
coll = db.my_collection
cursor = coll.find(tailable=True)
while cursor.alive:
    try:
        doc = cursor.next()
        print doc
    except StopIteration:
        time.sleep(1)

Perl Max

use 5.010;

use strict;
use warnings;
use MongoDB;

my $db = MongoDB::Connection->new;
my $coll = $db->my_db->my_collection;
my $cursor = $coll->find->tailable(1);
for (;;)
{
    if (defined(my $doc = $cursor->next))
    {
        say $doc;
    }
    else
    {
        sleep 1;
    }
}

其他资源:

Ruby/Node.js Tutorial which walks you through creating an application that listens to inserts in a MongoDB capped collection.

An article talking about tailable cursors in more detail.

PHP, Ruby, Python, and Perl examples of using tailable cursors.

答案 2 :(得分:37)

由于MongoDB 3.6会有一个名为Change Streams的新通知API,您可以将其用于此目的。见this blog post for an example。示例:

cursor = client.my_db.my_collection.changes([
    {'$match': {
        'operationType': {'$in': ['insert', 'replace']}
    }},
    {'$match': {
        'newDocument.n': {'$gte': 1}
    }}
])

# Loops forever.
for change in cursor:
    print(change['newDocument'])

答案 3 :(得分:23)

看看这个:改变流

2018年1月10日 - 发布3.6

*编辑:我写了一篇关于如何执行此操作的文章https://medium.com/riow/mongodb-data-collection-change-85b63d96ff76

https://docs.mongodb.com/v3.6/changeStreams/

mongodb 3.6 中的新功能 https://docs.mongodb.com/manual/release-notes/3.6/ 2018/01/10

$ mongod --version
db version v3.6.2

为了使用 changeStreams ,数据库必须是复制集

  

有关复制集的更多信息:   https://docs.mongodb.com/manual/replication/

默认情况下,您的数据库将是“独立”。

  

如何将独立转换为副本集:https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/

以下示例是您如何使用此实用程序的实用应用程序 * 特别针对Node。

/* file.js */
'use strict'


module.exports = function (
    app,
    io,
    User // Collection Name
) {
    // SET WATCH ON COLLECTION 
    const changeStream = User.watch();  

    // Socket Connection  
    io.on('connection', function (socket) {
        console.log('Connection!');

        // USERS - Change
        changeStream.on('change', function(change) {
            console.log('COLLECTION CHANGED');

            User.find({}, (err, data) => {
                if (err) throw err;

                if (data) {
                    // RESEND ALL USERS
                    socket.emit('users', data);
                }
            });
        });
    });
};
/* END - file.js */

有用的链接:
https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set
https://docs.mongodb.com/manual/tutorial/change-streams-example

https://docs.mongodb.com/v3.6/tutorial/change-streams-example
http://plusnconsulting.com/post/MongoDB-Change-Streams

答案 4 :(得分:15)

MongoDB版本3.6现在包含更改流,它本质上是OpLog之上的API,允许类似触发/通知的用例。

以下是Java示例的链接: http://mongodb.github.io/mongo-java-driver/3.6/driver/tutorials/change-streams/

NodeJS示例可能类似于:

 var MongoClient = require('mongodb').MongoClient;
    MongoClient.connect("mongodb://localhost:22000/MyStore?readConcern=majority")
     .then(function(client){
       let db = client.db('MyStore')

       let change_streams = db.collection('products').watch()
          change_streams.on('change', function(change){
            console.log(JSON.stringify(change));
          });
      });

答案 5 :(得分:3)

或者,您可以使用标准的Mongo FindAndUpdate方法,并在回调中,在回调运行时触发EventEmitter事件(在Node中)。

听取此事件的应用程序或体系结构的任何其他部分都将收到更新通知,并且还会发送任何相关数据。这是从Mongo获得通知的一种非常简单的方法。

答案 6 :(得分:2)

其中许多答案只会为您提供新记录而非更新和/或极其低效

唯一可靠,高效的方法是在本地db:oplog.rs集合上创建一个tailable游标,以便对MongoDB进行所有更改并按照您的意愿执行。 (MongoDB甚至在内部或多或少地支持复制!)

说明oplog包含的内容: https://www.compose.com/articles/the-mongodb-oplog-and-node-js/

Node.js库的示例,该库提供了关于可以使用oplog完成的API的API: https://github.com/cayasso/mongo-oplog

答案 7 :(得分:1)

有一个可用的java示例here

 MongoClient mongoClient = new MongoClient();
    DBCollection coll = mongoClient.getDatabase("local").getCollection("oplog.rs");

    DBCursor cur = coll.find().sort(BasicDBObjectBuilder.start("$natural", 1).get())
            .addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);

    System.out.println("== open cursor ==");

    Runnable task = () -> {
        System.out.println("\tWaiting for events");
        while (cur.hasNext()) {
            DBObject obj = cur.next();
            System.out.println( obj );

        }
    };
    new Thread(task).start();

此处的关键是QUERY OPTIONS

如果您不需要每次都加载所有数据,也可以更改查找查询。

BasicDBObject query= new BasicDBObject();
query.put("ts", new BasicDBObject("$gt", new BsonTimestamp(1471952088, 1))); //timestamp is within some range
query.put("op", "i"); //Only insert operation

DBCursor cur = coll.find(query).sort(BasicDBObjectBuilder.start("$natural", 1).get())
.addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);

答案 8 :(得分:1)

实际上,为什么在使用mongoose schema

提供的中间件插入新内容时,不会注意到输出?

您可以捕获插入新文档的事件,并在完成插入后执行某些操作

答案 9 :(得分:0)

有一组很棒的服务,称为MongoDB Stitch。查看stitch functions/triggers。请注意,这是基于云的付费服务(AWS)。在您的情况下,您可以在插入代码上调用用javascript编写的自定义函数。

enter image description here

答案 10 :(得分:0)

3.6之后,允许使用以下数据库触发器类型的数据库:

  • 事件驱动的触发器-用于自动更新相关文档,通知下游服务,传播数据以支持混合工作负载,数据完整性和审核
  • 计划的触发器-对于计划的数据检索,传播,归档和分析工作负载很有用

登录您的Atlas帐户,然后选择Triggers界面并添加新触发器:

enter image description here

展开每个部分以获取更多设置或详细信息。