sql >> Databasteknik >  >> NoSQL >> MongoDB

MongoDB-prestanda:Kör MongoDB-aggregeringar på sekundärer

Aggregeringsoperationer i MongoDB låter dig bearbeta dataposter, gruppera dem och returnera deras beräknade resultat. MongoDB stöder tre typer av aggregeringsoperationer:

  1. Kommandon för enstaka ändamål
  2. Map-Reduce
  3. Aggregationspipeline

Du kan använda detta MongoDB-jämförelsedokument för att se vilken som passar dina behov.

Aggregation Pipeline

Aggregationspipeline är ett MongoDB-ramverk som tillhandahåller dataaggregation via en databehandlingspipeline. Det vill säga dokument skickas genom en flerstegspipeline, där dokumenten filtreras, grupperas och på annat sätt omvandlas i varje steg. Det tillhandahåller SQL "GROUP BY ...." typ av konstruktioner för MongoDB som körs på själva databasen. Aggregationsdokumentation ger användbara exempel på att skapa sådana pipelines.

Varför köra aggregationer på den sekundära?

Aggregationspipelines är resursintensiva operationer – det är meningsfullt att överföra aggregeringsjobb till sekundärer av en MongoDB-replikuppsättning när det är ok att arbeta på lite inaktuella data. Detta är vanligtvis sant för "batch"-operationer eftersom de inte förväntar sig att köras på de senaste data. Om utdata behöver skrivas till en samling körs aggregeringsjobben bara på den primära eftersom endast den primära är skrivbar i MongoDB.

I det här inlägget kommer vi att visa dig hur du säkerställer att aggregeringspipelines exekveras på sekundären både från mongo-skalet och Java.

Exekvera aggregationspipelines på sekundären från Mongo Shell och Java i MongoDBClick To Tweet

Obs:Vi använder exempeldatauppsättningen som tillhandahålls av MongoDB i deras postnummeraggregationsexempel för att visa upp våra exempel. Du kan ladda ner den enligt anvisningarna i exemplet.

Aggregationspipeline på replikuppsättningar

MongoDB-skal

Ställer in läspreferensen till sekundär gör susen när du kör ett aggregeringsjobb från mongoskalet. Låt oss försöka hämta alla stater med en befolkning på över 10 miljoner (första sammanställningen i postnummerexemplet). Både skalet och servern kör MongoDB version 3.2.10.

mongo -u admin -p <pwd> --authenticationDatabase admin --host RS-repl0-0/server-1.servers.example.com:27017,server-2.servers.example.com:27017
RS-repl0-0:PRIMARY> use test
switched to db test
RS-repl0-0:PRIMARY> db.setSlaveOk() // Ok to run commands on a slave
RS-repl0-0:PRIMARY> db.getMongo().setReadPref('secondary') // Set read pref
RS-repl0-0:PRIMARY> db.getMongo().getReadPrefMode()
secondary
RS-repl0-0:PRIMARY> db.zips.aggregate( [
...    { $group: { _id: "$state", totalPop: { $sum: "$pop" } } },
...    { $match: { totalPop: { $gte: 10*1000*1000 } } }
... ] )
{ "_id" : "CA", "totalPop" : 29754890 }
{ "_id" : "FL", "totalPop" : 12686644 }
{ "_id" : "PA", "totalPop" : 11881643 }
{ "_id" : "NY", "totalPop" : 17990402 }
{ "_id" : "OH", "totalPop" : 10846517 }
{ "_id" : "IL", "totalPop" : 11427576 }
{ "_id" : "TX", "totalPop" : 16984601 }

En titt i MongoDB-loggarna (med loggning aktiverad för kommandon) på den sekundära visar att aggregering verkligen kördes på den sekundära:

...
2016-12-05T06:20:14.783+0000 I COMMAND  [conn200] command test.zips command: aggregate { aggregate: "zips", pipeline: [ { $group: { _id: "$state", totalPop: { $sum: "$pop" } } }, { 
$match: { totalPop: { $gte: 10000000.0 } } } ], cursor: {} } keyUpdates:0 writeConflicts:0 numYields:229 reslen:338 locks:{ Global: { acquireCount: { r: 466 } }, Database: { acquire
Count: { r: 233 } }, Collection: { acquireCount: { r: 233 } } } protocol:op_command 49ms
...

Java

Från MongoDB Java-drivrutinen gör det igen susen att ställa in läspreferensen. Här är ett exempel med drivrutinsversion 3.2.2:

public class AggregationChecker {

    /*
     * Data and code inspired from:
     * https://docs.mongodb.com/v3.2/tutorial/aggregation-zip-code-data-set/#return-states-with-populations-above-10-million
     */
    private static final String MONGO_END_POINT = "mongodb://admin:[email protected]:27017,server-2.servers.example.com:27017/admin?replicaSet=RS-repl0-0";

    private static final String COL_NAME = "zips";
    private static final String DEF_DB = "test";

    public AggregationChecker() {
    }

    public static void main(String[] args) {
        AggregationChecker writer = new AggregationChecker();
        writer.aggregationJob();
    }

    private void aggregationJob() {
        printer("Initializing...");
        Builder options = MongoClientOptions.builder().readPreference(ReadPreference.secondary());
        MongoClientURI uri = new MongoClientURI(MONGO_END_POINT, options);
        MongoClient client = new MongoClient(uri);
        try {
            final DB db = client.getDB(DEF_DB);
            final DBCollection coll = db.getCollection(COL_NAME);
            // Avg city pop by state: https://docs.mongodb.com/manual/tutorial/aggregation-zip-code-data-set/#return-average-city-population-by-state
            Iterable iterable = coll.aggregate(
                    Arrays.asList(
                            new BasicDBObject("$group", new BasicDBObject("_id", new BasicDBObject("state", "$state").append("city", "$city")).append("pop",
                                    new BasicDBObject("$sum", "$pop"))),
                                    new BasicDBObject("$group", new BasicDBObject("_id", "$_id.state").append("avgCityPop", new BasicDBObject("$avg", "$pop"))))).results();

            for (DBObject entry : iterable) {
                printer(entry.toString());
            }
        } finally {
            client.close();
        }
        printer("Done...");
    }
...
}

Loggar på den sekundära:

...
2016-12-01T10:54:18.667+0000 I COMMAND  [conn4113] command test.zips command: aggregate { aggregate: "zipcodes", pipeline: [ { $group: { _id: { state: "$state", city: "$city" }, pop: { $sum: "$pop" } } }, { $group: { _id: "$_id.state", avgCityPop: { $avg: "$pop" } } } ] } keyUpdates:0 writeConflicts:0 numYields:229 reslen:2149 locks:{ Global: { acquireCount: { r: 466 } }, Database: { acquireCount: { r: 233 } }, Collection: { acquireCount: { r: 233 } } } protocol:op_query 103ms
...

Ingen operation registrerades på den primära.

Aggregationspipeline på delade kluster

Aggregationsrörledningar stöds på fragmenterade kluster. Detaljerat beteende förklaras i dokumentationen. Implementeringsmässigt är det liten skillnad mellan replikuppsättning och fragmenterad kluster när du använder en aggregeringspipeline.

Hur man ställer in en aggregationspipeline på delade kluster i MongoDBClick To Tweet

MongoDB-skal

Innan du importerar data till det fragmenterade klustret, aktivera delning på samlingen.

mongos> sh.enableSharding("test")
mongos> sh.shardCollection("test.zips", { "_id" : "hashed" } )

Därefter är operationerna samma som replikuppsättningen:

mongos> db.setSlaveOk()
mongos> db.getMongo().setReadPref('secondary')
mongos> db.getMongo().getReadPrefMode()
secondary
mongos> db.zips.aggregate( [
...    { $group: { _id: "$state", totalPop: { $sum: "$pop" } } },
...    { $match: { totalPop: { $gte: 10*1000*1000 } } }
... ] )
{ "_id" : "TX", "totalPop" : 16984601 }
{ "_id" : "PA", "totalPop" : 11881643 }
{ "_id" : "CA", "totalPop" : 29754890 }
{ "_id" : "FL", "totalPop" : 12686644 }
{ "_id" : "NY", "totalPop" : 17990402 }
{ "_id" : "OH", "totalPop" : 10846517 }
{ "_id" : "IL", "totalPop" : 11427576 }

Loggar från en av sekundärerna:

...
2016-12-02T05:46:24.627+0000 I COMMAND  [conn242] command test.zips command: aggregate { aggregate: "zips", pipeline: [ { $group: { _id: "$state", totalPop: { $sum: "$pop" } } } ], fromRouter: true, cursor: { batchSize: 0 } } cursorid:44258973083 keyUpdates:0 writeConflicts:0 numYields:0 reslen:115 locks:{ Global: { acquireCount: { r: 4 } }, Database: { acquireCount: { r: 2 } }, Collection: { acquireCount: { r: 2 } } } protocol:op_query 0ms
2016-12-02T05:46:24.641+0000 I COMMAND  [conn131] getmore test.zips query: { aggregate: "zips", pipeline: [ { $group: { _id: "$state", totalPop: { $sum: "$pop" } } } ], fromRouter: true, cursor: { batchSize: 0 } } planSummary: PIPELINE_PROXY cursorid:44258973083 ntoreturn:0 keysExamined:0 docsExamined:0 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:112 nreturned:51 reslen:1601 locks:{ Global: { acquireCount: { r: 230 } }, Database: { acquireCount: { r: 115 } }, Collection: { acquireCount: { r: 115 } } } 13ms
...

Java

Samma kod som är tillämplig i replikuppsättningen fungerar bra med ett fragmenterat kluster. Byt bara ut replikuppsättningens anslutningssträng med den för det sönderdelade klustret. Loggar från en sekundär visar att jobbet verkligen kördes på sekundärerna:

...
2016-12-02T05:39:12.339+0000 I COMMAND  [conn130] command test.zips command: aggregate { aggregate: "zips", pipeline: [ { $group: { _id: { state: "$state", city: "$city" }, pop: { $sum: "$pop" } } } ], fromRouter: true, cursor: { batchSize: 0 } } cursorid:44228970872 keyUpdates:0 writeConflicts:0 numYields:0 reslen:115 locks:{ Global: { acquireCount: { r: 4 } }, Database: { acquireCount: { r: 2 } }, Collection: { acquireCount: { r: 2 } } } protocol:op_query 0ms
2016-12-02T05:39:12.371+0000 I COMMAND  [conn131] getmore test.zips query: { aggregate: "zips", pipeline: [ { $group: { _id: { state: "$state", city: "$city" }, pop: { $sum: "$pop" } } } ], fromRouter: true, cursor: { batchSize: 0 } } planSummary: PIPELINE_PROXY cursorid:44228970872 ntoreturn:0 keysExamined:0 docsExamined:0 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:112 nreturned:12902 reslen:741403 locks:{ Global: { acquireCount: { r: 230 } }, Database: { acquireCount: { r: 115 } }, Collection: { acquireCount: { r: 115 } } } 30ms
...

Var detta innehåll till hjälp? Låt oss veta genom att twittra på oss @scaledgridio och som alltid, om du har några frågor låt oss veta i kommentarerna nedan. Åh och! Glöm inte att kolla in våra MongoDB-värdprodukter som kan spara upp till 40 % på långsiktiga MongoDB®-värdkostnader.


  1. Native MongoDB-maskering (tredje metoden)

  2. Hur undviker man transparent_hugepage/defrag-varning från mongodb?

  3. Introduktion till MongoDB-datatyper

  4. Regex för MongoDB ObjectID