sql >> Base de Datos >  >> NoSQL >> MongoDB

Rendimiento de MongoDB:ejecución de agregaciones de MongoDB en secundarios

Las operaciones de agregación en MongoDB le permiten procesar registros de datos, agruparlos y devolver sus resultados calculados. MongoDB admite tres tipos de operaciones de agregación:

  1. Comandos de agregación de propósito único
  2. Mapa-Reducir
  3. Canalización de agregación

Puede usar este documento de comparación de MongoDB para ver cuál se ajusta a sus necesidades.

Canalización de agregación

La canalización de agregación es un marco de MongoDB que proporciona la agregación de datos a través de una canalización de procesamiento de datos. Es decir, los documentos se envían a través de una canalización de varios pasos, filtrando, agrupando y transformando los documentos en cada paso. Proporciona SQL "GROUP BY..." tipo de construcciones para MongoDB que se ejecutan en la propia base de datos. La documentación de agregación proporciona ejemplos útiles de cómo crear dichas canalizaciones.

¿Por qué ejecutar agregaciones en el secundario?

Las canalizaciones de agregación son operaciones que consumen muchos recursos:tiene sentido descargar los trabajos de agregación a los secundarios de un conjunto de réplicas de MongoDB cuando está bien para operar con datos ligeramente obsoletos. Esto suele ser cierto para las operaciones por lotes, ya que no esperan ejecutarse con los datos más recientes. Si la salida debe escribirse en una colección, los trabajos de agregación solo se ejecutan en el principal, ya que solo se puede escribir en el principal en MongoDB.

En esta publicación, le mostraremos cómo asegurarse de que las canalizaciones de agregación se ejecuten en el secundario tanto desde mongo Shell como desde Java.

Ejecute canalizaciones de agregación en el secundario desde Mongo Shell y Java en MongoDBHaga clic para twittear

Nota:Usamos el conjunto de datos de muestra proporcionado por MongoDB en su ejemplo de agregación de códigos postales para mostrar nuestros ejemplos. Puede descargarlo como se indica en el ejemplo.

Canalización de agregación en conjuntos de réplicas

Shell MongoDB

Establecer la preferencia de lectura en secundaria hace el truco cuando se ejecuta un trabajo de agregación desde el shell mongo. Intentemos obtener todos los estados con una población superior a 10 millones (primera agregación en el ejemplo de códigos postales). Tanto el shell como el servidor ejecutan MongoDB versión 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 }

Una mirada a los registros de MongoDB (con el registro habilitado para los comandos) en el secundario muestra que la agregación efectivamente se ejecutó en el secundario:

...
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

Desde el controlador MongoDB Java, nuevamente establecer la Preferencia de lectura hace el truco. Aquí hay un ejemplo usando la versión 3.2.2 del controlador:

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

Inicia sesión en el secundario:

...
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
...

No se registró ninguna operación en el primario.

Canalización de agregación en clústeres fragmentados

Las canalizaciones de agregación se admiten en clústeres fragmentados. El comportamiento detallado se explica en la documentación. En cuanto a la implementación, hay poca diferencia entre el conjunto de réplicas y el clúster fragmentado cuando se usa una canalización de agregación.

Cómo configurar una canalización de agregación en clústeres fragmentados en MongoDBClick To Tweet

Shell MongoDB

Antes de importar datos al clúster fragmentado, habilite la fragmentación en la colección.

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

Después de eso, las operaciones son las mismas que las del conjunto de réplicas:

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 }

Registros de uno de los secundarios:

...
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

El mismo código aplicable en el conjunto de réplicas funciona bien con un clúster fragmentado. Simplemente reemplace la cadena de conexión del conjunto de réplicas con la del clúster fragmentado. Los registros de un secundario indican que el trabajo se ejecutó en los secundarios:

...
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
...

¿Fue útil este contenido? Háganos saber enviándonos un tweet a @scaledgridio y, como siempre, si tiene alguna pregunta, háganoslo saber en los comentarios a continuación. ¡Oh y! No olvide consultar nuestros productos de alojamiento MongoDB que pueden ahorrar hasta un 40 % en costos de alojamiento MongoDB® a largo plazo.