MongoDB 콜렉션의 변경 사항을 청취하는 방법은 무엇입니까?


200

MongoDB를 데이터 저장소로 사용하여 일종의 백그라운드 작업 대기열 시스템을 만들고 있습니다. 작업자가 작업을 처리하기 전에 MongoDB 컬렉션에 삽입물을 "듣기"하려면 어떻게해야합니까? 마지막 시간부터 변경 사항이 있는지 또는 스크립트가 삽입을 기다릴 수있는 방법이 있는지 확인하기 위해 몇 초마다 폴링해야합니까? 이것은 내가 작업중 인 PHP 프로젝트이지만 루비 또는 언어에 구애받지 않고 자유롭게 대답하십시오.


1
시나리오를 해결하기 위해 Change Streams가 MongoDB 3.6에 추가되었습니다. docs.mongodb.com/manual/changeStreams 또한 MongoDB Atlas를 사용하는 경우 삽입 / 업데이트 / 삭제 / 등에 대한 응답으로 함수를 실행할 수있는 스티치 트리거를 활용할 수 있습니다. docs.mongodb.com/stitch/triggers/overview 더 이상 oplog 를 구문 분석 할 필요가 없습니다.
Robert Walters

답변:


111

당신이 생각하는 것은 방아쇠처럼 들립니다. MongoDB는 트리거를 지원하지 않지만 일부 사람들은 몇 가지 트릭을 사용하여 "자신을 굴 렸습니다". 여기서 핵심은 oplog입니다.

복제 세트에서 MongoDB를 실행하면 모든 MongoDB 작업이 작업 로그 (oplog)에 기록됩니다. oplog는 기본적으로 데이터 수정 사항의 실행 목록입니다. 복제본이 oplog의 변경 사항을 청취하고 변경 사항을 로컬로 적용하여 기능을 설정합니다.

익숙한가요?

여기에서 전체 프로세스를 자세히 설명 할 수는 없지만 여러 페이지의 문서이지만 필요한 도구를 사용할 수 있습니다.

oplog에 대한 일부 글쓰기- 간단한 설명 - 컬렉션의 레이아웃local (oplog를 포함)

테일러 블 커서 를 활용하는 것도 좋습니다 . 이를 통해 폴링 대신 변경 사항을 청취 할 수 있습니다. 복제는 테일러 블 커서를 사용하므로 지원되는 기능입니다.


1
흠 ... 정확히 생각한 것이 아닙니다. 이 시점에서 하나의 인스턴스 만 실행 중입니다 (슬레이브 없음). 아마도 더 기본적인 해결책일까요?
Andrew

17
--replSet옵션을 사용 하여 서버를 시작할 수 있으며 서버를 만들거나 채 웁니다 oplog. 2 차 없이도. 이것이 DB의 변화를 "듣기"위한 유일한 방법입니다.
Gates VP

2
변경 사항을 로컬로 DB에 로깅하기 위해 oplog를 설정하는 방법에 대한 좋은 설명입니다 : loosexaml.wordpress.com/2012/09/03/…
johndodo

Cooooool! 그것이 정말로 내가 원하는 것입니다. 그리고 npm에서 'mongo-oplog'라는 라이브러리를 발견했습니다. 너무 행복해 ~
pjincz

이 답변을 작성하는 시점에 동의 할 수는 없지만 여기에 도착하는 모든 사람에게 가능합니다. 현재 사용할 수있는 옵션이 있습니다 .MongoDB 스티치 ( docs.mongodb.com/stitch/#stitch ) 및 스티치 트리거 ( 문서 )를 확인하십시오 . mongodb.com/stitch/triggers ) ..
whoami

102

MongoDB를이 이른바 가지고 capped collectionstailable cursors그 청취자에게 푸시 데이터에 MongoDB를 할 수 있습니다.

A capped collection는 기본적으로 고정 된 크기의 컬렉션이며 삽입 만 허용합니다. 다음은 하나를 만드는 모습입니다.

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

MongoDB 테일러 블 커서 ( 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);
    }
}

파이썬 ( 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)

( 최대 )

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 학습서-MongoDB 제한 콜렉션의 삽입을 청취하는 애플리케이션 작성을 안내합니다.

테일러 블 커서에 대해 자세히 설명하는 기사.

테일러 블 커서 사용에 대한 PHP, Ruby, Python 및 Perl 예제


70
수면 1? 정말? 생산 코드? 어떻게 폴링하지 않습니까?
rbp

2
@rbp haha, 나는 그것이 프로덕션 코드라고 말한 적이 없지만, 잠깐만 자고있는 것은 좋은 습관이 아닙니다. 다른 곳에서 그 예를 얻었을 것입니다. 그래도 리팩토링하는 방법을 모르겠습니다.
앤드류

14
@kroe는 관련이없는 세부 정보가 왜 나쁜지 이해하지 못하는 새로운 프로그래머가 프로덕션 코드에 넣을 것이기 때문입니다.
메기

3
나는 당신의 요점을 이해하지만 일부 새로운 프로그래머가 "sleep 1"을 프로덕션에 추가 할 것을 기대하는 것은 거의 모욕적입니다! 내 말은, 나는 놀라지 않을 것입니다 ... 그러나 누군가가 이것을 생산에 넣는다면 적어도 어려운 길과 영원히 배울 것입니다 .. hahaha
kroe

19
프로덕션에서 time.sleep (1)을 수행하는 데 어떤 문제가 있습니까?
Al Johri

44

MongoDB 3.6부터 Change Streams라는 새로운 알림 API가 있으며이를 사용할 수 있습니다. 예를 보려면 이 블로그 게시물을 참조하십시오 . 그것의 예 :

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'])

4
왜? 정교하게 할 수 있습니까? 이것은 지금 표준적인 방법입니까?
Mitar

1
어떻게? 폴링을 사용하지 마십시오. while 루프 대신 이벤트 방식이 필요합니다.
Alexander Mills

3
여기서 여론 조사는 어디에서 보입니까?
Mitar

나는 그 / 그녀가 마지막 루프를 참조하고 있다고 생각합니다. 그러나 PyMongo는 그것을 지원한다고 생각합니다. 모터에는 비동기 / 이벤트 리스너 스타일 구현이있을 수 있습니다.
Shane Hsu

41

이것을 확인하십시오 : 스트림 변경

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/


다음 예제 는 이것을 사용하는 방법에 대한 실용적인 응용 프로그램입니다.
* 노드 전용.

/* 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


모든 수정 사항에 대해 죄송합니다. 따라서 "링크"가 마음에 들지 않았습니다 (형식이 잘못된 코드 임)
Rio Weber

1
당신은 데이터베이스를 쿼리 할 필요가 없습니다. 나는 watch () 또는 이와 유사한 것으로 생각합니다. 새로운 데이터는 듣고있는 서버로 전송 될 수 있습니다
Alexander Mills

22

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));
          });
      });

JSON.stringify는 Android Studio (Android App)에서이 데이터를 수신하는 데 매우 중요합니다.
DragonFire

3

또는 표준 Mongo FindAndUpdate 메소드를 사용하고 콜백 내에서 콜백이 실행될 때 노드에서 EventEmitter 이벤트를 발생시킬 수 있습니다.

이 이벤트를 수신하는 응용 프로그램 또는 아키텍처의 다른 부분은 업데이트 및 관련 데이터도 함께 알림을받습니다. 이것은 몽고의 알림을 얻는 간단한 방법입니다.


이것은 매우 비효율적입니다. 각 FindAndUpdate에 대해 db를 잠그고 있습니다!
Yash Gupta

1
내 생각에 Alex는 약간 다른 (삽입물 주소 지정이 아님) 대답하지만 대기중인 작업 상태가 작업이 생성 될 때 발생해야한다고 변경 될 때 클라이언트에게 어떤 종류의 알림을 보내는 방법과 관련하여 관련 질문에 답했습니다. , 완료 또는 실패 웹 소켓을 사용하여 노드에 연결된 클라이언트를 사용하면 상태 변경 메시지를 수신 할 때 호출 될 수있는 FIndAndUpdate 콜백의 브로드 캐스트 이벤트를 통해 변경 사항을 모두 통보받을 수 있습니다. 업데이트가 필요하므로 비효율적이지 않다고 말하고 싶습니다.
Peter Scott

3

이러한 답변 중 다수는 새로운 기록 만 제공하며 업데이트가 아니거나 매우 비효율적입니다.

이를 수행 할 수있는 신뢰할 수 있고 성능이 좋은 유일한 방법은 로컬 db : oplog.rs 콜렉션에 꼬리 커서를 작성하여 MongoDB에 대한 모든 변경 사항을 가져오고 원하는 작업을 수행하는 것입니다. (MongoDB는 복제를 지원하기 위해 내부적으로 어느 정도까지 수행합니다!)

oplog에 포함 된 내용에 대한 설명 : https://www.compose.com/articles/the-mongodb-oplog-and-node-js/

oplog로 수행 할 수있는 작업에 대한 API를 제공하는 Node.js 라이브러리 예 : https://github.com/cayasso/mongo-oplog


2

MongoDB Stitch 라는 멋진 서비스 세트가 있습니다 . 스티치 기능 / 트리거를 살펴보십시오 . 이는 클라우드 기반 유료 서비스 (AWS)입니다. 귀하의 경우 삽입에서 자바 스크립트로 작성된 사용자 정의 함수를 호출 할 수 있습니다.

여기에 이미지 설명을 입력하십시오


stackoverflow.com/users/486867/manish-jain- 데이터가 테이블에 삽입되었음을 REACT 응용 프로그램에 알리는 데 스티치를 사용하는 방법에 대한 예가 있습니까?
MLissCetrus

1

여기 에서 찾을 수있는 작동중인 Java 예제가 있습니다 .

 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);

1

실제로 출력을 보는 대신 몽구스 스키마 에서 제공 한 미들웨어를 사용하여 새로운 내용을 삽입 할 때 통지를받지 못하는 이유

새 문서를 삽입하는 이벤트를 포착하고 삽입이 완료된 후 무언가를 수행 할 수 있습니다.


내 잘못이야. 죄송합니다
Duong Nguyen

0

3.6 이후에는 데이터베이스를 사용할 수 있으며 다음 데이터베이스 트리거 유형이 있습니다.

  • 이벤트 중심 트리거-관련 문서를 자동으로 업데이트하고, 다운 스트림 서비스에 알리고, 데이터를 전파하여 혼합 된 워크로드, 데이터 무결성 및 감사를 지원하는 데 유용합니다.
  • 예약 된 트리거-예약 된 데이터 검색, 전파, 보관 및 분석 워크로드에 유용

Atlas 계정에 로그인하고 Triggers인터페이스를 선택 하고 새 트리거를 추가하십시오.

여기에 이미지 설명을 입력하십시오

추가 설정 또는 세부 사항을 보려면 각 섹션을 펼치십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.