Bir MongoDB koleksiyonundaki değişiklikleri nasıl dinleyebilirim?


200

Veri deposu olarak MongoDB ile bir tür arka plan iş kuyruk sistemi oluşturuyorum. Çalışanları işi işlemek için yumurtlamadan önce bir MongoDB koleksiyonundaki ekleri nasıl "dinleyebilirim"? Son seferde herhangi bir değişiklik olup olmadığını görmek için birkaç saniyede bir yoklama yapmam gerekir mi yoksa komut dosyamın eklerin gerçekleşmesini bekleyebileceği bir yol var mı? Bu, üzerinde çalıştığım bir PHP projesidir, ancak Ruby veya dil bilinciyle cevap vermekten çekinmeyin.


1
Senaryoyu ele almak için MongoDB 3.6'ya Değişiklik Akışları eklendi. docs.mongodb.com/manual/changeStreams Ayrıca MongoDB Atlas kullanıyorsanız, ekleme / güncelleme / silme / vb. işlevlerine yanıt olarak işlevleri yürütmenizi sağlayan Dikiş Tetikleyicilerinden yararlanabilirsiniz. docs.mongodb.com/stitch/triggers/overview Oplog'u ayrıştırmaya gerek yok.
Robert Walters

Yanıtlar:


111

Sesleri düşündüğünüz şey tetikleyicilere çok benzer. MongoDB'nin tetikleyiciler için herhangi bir desteği yoktur, ancak bazı insanlar bazı hileler kullanarak "kendi başlarına" yuvarladılar. Buradaki anahtar oplog.

MongoDB'yi bir çoğaltma kümesinde çalıştırdığınızda, tüm MongoDB eylemleri bir işlem günlüğüne (oplog olarak bilinir) kaydedilir. Oplog temel olarak verilerde yapılan değişikliklerin sadece çalışan bir listesidir. Kopyalar Bu oplogdaki değişiklikleri dinleyerek ve ardından değişiklikleri yerel olarak uygulayarak işlevi ayarlar.

Bu size tanıdık geliyor mu?

Burada tüm süreci detaylandıramıyorum, birkaç sayfa belge var, ancak ihtiyacınız olan araçlar mevcut.

İlk olarak oplog üzerine bazı yazılar - Kısa açıklama - Koleksiyonun düzenilocal (oplog içeren)

Ayrıca, uyarlanabilir imleçlerden yararlanmak istersiniz . Bunlar, yoklama yapmak yerine değişiklikleri dinlemek için bir yol sağlayacaktır. Çoğaltmanın uyarlanabilir imleçler kullandığını unutmayın, bu nedenle bu desteklenen bir özelliktir.


1
hmm ... tam olarak aklımdakiler değil. Bu noktada sadece bir örnek çalıştırıyorum (köle yok). Belki daha basit bir çözüm?
Andrew

17
Sunucuyu bu --replSetseçenekle başlatabilirsiniz ve sunucuyu oluşturur / doldurur oplog. İkincil olmadan bile. Bu kesinlikle DB değişiklikleri "dinlemek" için tek yoldur.
Gates VP

2
Bu, yerel olarak DB'deki
johndodo

Cooooool! Gerçekten istediğim bu. Ve npm'de 'mongo-oplog' adında bir kütüphane buldum. So happy ~
pjincz

Bu cevap tetikleyicilerini yazarken, buraya gelebilecek herkes için uygun olabilir, şimdi mevcut bir seçenek var, MongoDB Stitch'e göz atın ( docs.mongodb.com/stitch/#stitch ) & Stitch tetikleyicileri ( dokümanlar). mongodb.com/stitch/triggers ) ..
whoami

103

MongoDB denilene sahiptir capped collectionsve tailable cursorsMongoDB'nin dinleyicilere veri aktarmasına izin verir.

A capped collectionaslında sabit boyutlu ve sadece eklemeye izin veren bir koleksiyondur. İşte bir tane oluşturmak nasıl görünecek:

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

MongoDB Uyarlanabilir imleçler ( özgün yazı Jonathan H. Wage tarafından )

yakut

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 tarafından )

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 ( Maks. )

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

Ek kaynaklar:

Ruby / Node.js MongoDB kapaklı koleksiyondaki ekleri dinleyen bir uygulama oluşturarak sizi yönlendiren öğretici.

Tailable imleçler hakkında daha ayrıntılı bir makale.

Tailable imleç kullanma PHP, Ruby, Python ve Perl örnekleri.


70
uyku 1? Gerçekten mi? üretim kodu için? bu nasıl yoklama değil
rbp

2
@rbp haha, bunun üretim kodu olduğunu söylemedim, ama haklısın, bir saniye uyumak iyi bir uygulama değil. Bu örneği başka bir yerden aldığımdan eminim. Yine de nasıl refactor emin değilim.
Andrew

14
@kroe çünkü bu alakasız ayrıntılar, neden kötü olduğunu anlamayabilecek yeni programcılar tarafından üretim koduna aktarılacak.
Yayın

3
Anlıyorum, ancak bazı yeni programcıların üretime "uyku 1" eklemesini beklemek neredeyse rahatsız edici! Demek istediğim, şaşırmazdım ... Ama eğer birisi bunu üretime
sokarsa

19
üretimde time.sleep (1) yapmanın nesi yanlış?
Al

44

MongoDB 3.6'dan beri, bunun için kullanabileceğiniz Değişiklik Akışları adı verilen yeni bir bildirim API'si olacaktır. Örnek için bu blog yayınına bakın . Örnek:

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
Neden? Detaylandırabilir misin? Bu şimdi standart bir yol mu?
Mitar

1
Nasıl? yoklama kullanmayın - while döngüleri yerine olaylı bir yaklaşıma ihtiyacınız vardır.
Alexander Mills

3
Burada oylamayı nerede görüyorsunuz?
Mitar

Bence son döngüye atıfta bulunuyor. Ama PyMongo'nun bunu desteklediğini düşünüyorum. Motorun zaman uyumsuz / olay dinleyici tarzı bir uygulaması olabilir.
Shane Hsu

41

Şuna göz atın: Akışları Değiştir

10 Ocak 2018 - Sürüm 3.6

* DÜZENLEME: Bunun nasıl yapılacağı hakkında bir makale yazdım https://medium.com/riow/mongodb-data-collection-change-85b63d96ff76

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


Bu yenilikler MongoDB 3.6 https://docs.mongodb.com/manual/release-notes/3.6/ 2018/01/10

$ mongod --version
db version v3.6.2

ChangeStreams'i kullanmak için veritabanı bir Çoğaltma Kümesi olmalıdır

Çoğaltma Kümeleri hakkında daha fazla bilgi: https://docs.mongodb.com/manual/replication/

Veritabanınız varsayılan olarak " Bağımsız " olacaktır .

Tek Başına Çoğaltma Kümesine Dönüştürme: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/


Aşağıdaki örnek , bunu nasıl kullanabileceğinize dair pratik bir uygulamadır.
* Özellikle Düğüm için.

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

Yararlı bağlantılar:
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


Tüm düzenlemeler için özür dilerim, SO "Bağlantılarım" ı beğenmedi (yanlış biçimlendirilmiş kod olduklarını söyledi.)
Rio Weber

1
veritabanını sorgulamak zorunda değilsiniz, bence watch () veya benzeri ile, yeni veriler dinleyen sunucuya gönderilebilir
Alexander Mills

22

MongoDB sürüm 3.6, artık OpLog'un üstünde bir tetikleyici / bildirim benzeri kullanım durumlarına izin veren bir API olan değişiklik akışlarını içeriyor.

Java örneğine bir bağlantı: http://mongodb.github.io/mongo-java-driver/3.6/driver/tutorials/change-streams/

Bir NodeJS örneği şöyle görünebilir:

 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, bu verileri Android Studio'da (Android Uygulaması) almak için çok önemlidir ..
DragonFire

3

Alternatif olarak, standart Mongo FindAndUpdate yöntemini kullanabilir ve geri arama çalıştırıldığında, geri arama çalıştırıldığında bir EventEmitter olayını (Düğümde) tetikleyebilir.

Uygulamanın veya mimarinin bu etkinliği dinleyen diğer kısımları güncellemeden haberdar edilir ve ilgili veriler de oraya gönderilir. Bu, Mongo'dan bildirim almanın gerçekten basit bir yoludur.


Bu çok verimsiz .. Her FindAndUpdate için db kilitliyorsunuz!
Yash Gupta

1
Benim tahminim Alex'in biraz farklı (özellikle ekleri ele almayan) yanıtlamasıydı, ancak işler ortaya çıktıkça gerçekleşmesi gerektiğini düşündüğümüz kuyruğa alınmış bir işin durumu değiştiğinde müşterilere bir tür bildirimi nasıl tetikleyeceğiyle ilgili soru. , başarıyla tamamlayın veya başarısız olun. Websockets kullanılarak düğüme bağlanan istemcilerle, FIndAndUpdate geri çağrısında bir yayın olayı ile yapılan değişikliklerin durum değişikliği iletileri alındığında çağrılabilecekleri bildirilebilir. Güncellemelerin yapılması gerektiği için bunun verimsiz olmadığını söyleyebilirim.
Peter Scott

3

Bu cevapların birçoğu size sadece yeni kayıtlar verecek, güncelleme vermeyecek ve / veya son derece verimsiz olacaktır

Bunu yapmanın tek güvenilir ve performanslı yolu, MongoDB'deki TÜM değişiklikleri almak ve ne yapacağınızı yapmak için yerel db: oplog.rs koleksiyonunda tailable imleç oluşturmaktır. (MongoDB, çoğaltmayı desteklemek için bunu dahili olarak aşağı yukarı yapar!)

Oplog'un içeriğinin açıklaması: https://www.compose.com/articles/the-mongodb-oplog-and-node-js/

Oplog ile yapılabilecekler hakkında bir API sağlayan bir Node.js kütüphanesi örneği: https://github.com/cayasso/mongo-oplog



1

Burada bulunan çalışan bir java örneği var .

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

Anahtar, burada verilen QUERY OPTIONS .

Ayrıca, her seferinde tüm verileri yüklemeniz gerekmiyorsa bulma sorgusunu değiştirebilirsiniz.

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


0

3.6 sonra bir veritabanı kullanmak için izin verilir aşağıdaki veritabanı türleri tetikler:

  • olay güdümlü tetikleyiciler - ilgili belgeleri otomatik olarak güncellemek, aşağı yönlü hizmetleri bildirmek, karışık iş yüklerini desteklemek için veri yaymak, veri bütünlüğü ve denetimi için yararlıdır
  • zamanlanmış tetikleyiciler - zamanlanmış veri alma, yayılma, arşivleme ve analiz iş yükleri için yararlıdır

Atlas hesabınıza giriş yapın ve Triggersarayüzü seçin ve yeni tetikleyici ekleyin:

resim açıklamasını buraya girin

Daha fazla ayar veya ayrıntı için her bölümü genişletin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.