Як слухати зміни до колекції MongoDB?


200

Я створюю якусь систему фонової черги завдань з MongoDB як сховищем даних. Як я можу "прослухати" вставки до колекції MongoDB перед нерестуючими працівниками для обробки завдання? Чи потрібно мені опитувати кожні кілька секунд, щоб побачити, чи є зміни за останній час, чи існує спосіб, коли мій сценарій може чекати, коли відбудеться вставка? Це проект PHP, над яким я працюю, але не соромтесь відповідати на Ruby або мовою агностиком.


1
Змінні потоки додано в MongoDB 3.6 для вирішення вашого сценарію. docs.mongodb.com/manual/changeStreams Також якщо ви використовуєте MongoDB Atlas, ви можете використовувати тригери Stitch, які дозволяють виконувати функції у відповідь на вставлення / оновлення / видалення / тощо. docs.mongodb.com/stitch/triggers/overview Більше не потрібно аналізувати опис.
Роберт Уолтерс

Відповіді:


111

Те, що ви думаєте, звучить дуже схоже на тригери. У MongoDB немає підтримки для тригерів, проте деякі люди "скочували свої", використовуючи деякі хитрощі. Ключовим тут є оплог.

Коли ви запускаєте MongoDB у наборі реплік, всі дії MongoDB реєструються в журналі операцій (відомий як оплог). Оплог - це лише запущений список змін, внесених у дані. Репліки Набори функціонують, прослуховуючи зміни в цьому оплогу, а потім застосовуючи зміни локально.

Це звучить знайомо?

Я не можу деталізувати весь процес тут, це кілька сторінок документації, але потрібні інструменти є.

Спочатку кілька записів на oplog - Короткий опис - Макет localколекції (який містить oplog)

Ви також хочете використовувати курсори, доступні для доступу . Вони дадуть вам можливість слухати зміни, а не опитувати їх. Зауважте, що для реплікації використовуються доступні курсори, тому це підтримувана функція.


1
хм ... не зовсім те, що я мав на увазі. У мене зараз працює лише один екземпляр (немає рабів). То може бути більш базове рішення?
Андрій

17
Ви можете запустити сервер за допомогою --replSetпараметра, і він створить / заповнить oplog. Навіть без другорядних. Це, безумовно, єдиний спосіб "прослухати" зміни в БД.
Гейтс ВП

2
Це хороший опис, як налаштувати oplog для реєстрації змін у БД локально: loosexaml.wordpress.com/2012/09/03/…
johndodo

Cooooool! Це дійсно те, що я хочу. І я знайшов бібліотеку з назвою "mongo-oplog" у npm. Тож щасливий ~
pjincz

Я погоджуюся до моменту написання цієї відповіді, тригери можуть бути недоступними, але для всіх, хто приземлився тут. Зараз є можливість, перевірити MongoDB Stitch ( docs.mongodb.com/stitch/#stitch ) та триггери Stitch ( документи. mongodb.com/stitch/triggers ) ..
whoami

102

У MongoDB є те, що називається, capped collectionsі tailable cursorsце дозволяє MongoDB надсилати дані слухачам.

По capped collectionсуті, це збірка, яка має фіксований розмір і дозволяє лише вставляти. Ось як було б створити таке:

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

Показані курсори MongoDB ( оригінальна публікація Джонатана Х. Заробітної плати )

Рубін

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

Пітон ( Роберт Стюарт)

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, який пропонує вам створити додаток, який слухає вставки в колекцію з обмеженими можливостями MongoDB.

Стаття, що розповідає про доступні курсори більш детально.

Приклади використання PHP, Ruby, Python та Perl прикладів використання доступних курсорів.


70
спати 1? справді? за виробничим кодом? як це не опитування?
rbp

2
@rbp ха-ха, я ніколи не казав, що це виробничий код, але ти маєш рацію, спати на секунду - не надто хороша практика. Досить впевнений, що я отримав цей приклад десь із іншого. Не впевнений, як це переробляти.
Андрій

14
@kroe, оскільки ці невідповідні деталі будуть введені у виробничий код новішими програмістами, які можуть не зрозуміти, чому це погано.
Сом

3
Я розумію вашу думку, але очікувати, що деякі нові програмісти додадуть "сон 1" у виробництво, майже образливо! Я маю на увазі, я не був би здивований ... Але якщо хтось вкладе це у виробництво, принаймні навчиться важкому шляху і назавжди .. ха-ха
kroe

19
що поганого у виконанні time.sleep (1) у виробництві?
Аль-Джонрі

44

З MongoDB 3.6 з'явиться новий API сповіщень під назвою Change Streams, який ви можете використовувати для цього. Для прикладу дивіться цю публікацію в блозі . Приклад з нього:

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
Чому? Чи можете ви докладно? Це стандартний спосіб зараз?
Мітар

1
як? не використовуйте опитування - вам потрібен рівномірний підхід замість циклів і т.д.
Олександр Міллз

3
Де ви бачите тут опитування?
Мітар

Я думаю, що він / вона має на увазі останню петлю. Але я думаю, що PyMongo це лише підтримує. Мотор може мати реалізацію стилю асинхрон / події для слухача
Шейн Хсу

41

Перевірте це: Змініть потоки

10 січня 2018 р. - випуск 3.6

* EDIT: Я написав статтю про те, як це зробити 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 тепер включає потоки змін, які, по суті, є API поверх OpLog, що дозволяє застосовувати тригер / повідомлення, як подібні випадки.

Ось посилання на приклад 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 (у вузлі), коли запускається зворотний виклик.

Будь-які інші частини програми чи архітектури, які слухають цю подію, будуть повідомлені про оновлення, а також будь-які відповідні дані, надіслані там. Це дійсно простий спосіб отримати сповіщення від Монго.


це дуже неефективно .. ви заблокуєте db для кожного FindAndUpdate!
Яш Гупта

1
Я здогадуюсь, що Алекс відповідав дещо іншим (не конкретно на адресу вставок), але пов’язаний із цим питанням, як увімкнути якесь повідомлення клієнтам, коли стан змін у черзі, які, як ми вважаємо, повинні відбутися, коли породжуються завдання , завершити успішно або не вдалося. Якщо клієнти, підключені за допомогою веб-сокетів до вузла, всі вони можуть бути сповіщені про зміни за допомогою трансляції події на зворотному звороті FIndAndUpdate, яку можна було б викликати при отриманні повідомлень про зміну стану. Я б сказав, що це неефективно, оскільки оновлення потрібно робити.
Пітер Скотт

3

Багато з цих відповідей дадуть вам лише нові записи, а не оновлення та / або вкрай малоефективні

Єдиний надійний, ефективний спосіб зробити це - створити курсор, розміщений у локальній колекції db: oplog.rs, щоб отримати ВСІ зміни в MongoDB і зробити з ним все, що вам заманеться. (MongoDB навіть робить це все більш-менш для підтримки реплікації!)

Пояснення того, що містить опилог: https://www.compose.com/articles/the-mongodb-oplog-and-node-js/

Приклад бібліотеки Node.js, яка надає API щодо того, що можна зробити з oplog: https://github.com/cayasso/mongo-oplog


2

Існує приголомшливий набір послуг під назвою MongoDB Stitch . Перегляньте функції / тригери зшивання . Зауважте, що це хмарна платна послуга (AWS). У вашому випадку на вставці ви можете викликати користувацьку функцію, написану на JavaScript.

введіть тут опис зображення


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

Ключ - ЗАПИТАННЯ ВАРТІЙ, наведених тут.

Також ви можете змінити пошук запиту, якщо вам не потрібно кожного разу завантажувати всі дані.

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інтерфейс та додайте новий тригер:

введіть тут опис зображення

Розгорніть кожен розділ для отримання додаткових налаштувань або деталей.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.