Rendimiento de MongoDB vs. PostgreSQL con 5.5 millones de filas / documentos

10

¿Alguien puede ayudarme a comparar estas consultas y explicar por qué la consulta PostgreSQL se ejecuta en menos de 2000 ms y la consulta agregada MongoDB tarda casi 9000 ms y, a veces, hasta 130K ms?

PostgreSQL 9.3.2 on x86_64-apple-darwin, compiled by i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00), 64-bit

Consulta PostgreSQL

SELECT locomotive_id,
   SUM(date_trunc('second', datetime) - date_trunc('second', prevDatetime)) AS utilization_time

FROM bpkdmp 
WHERE datetime >= '2013-7-26 00:00:00.0000' 
AND   datetime <= '2013-7-26 23:59:59.9999'
GROUP BY locomotive_id
order by locomotive_id

Consulta MongoDB

db.bpkdmp.aggregate([
   {
      $match : {
          datetime : { $gte : new Date(2013,6,26, 0, 0, 0, 0), $lt : new Date(2013,6,26, 23, 59, 59, 9999) }
   }
   },
   {
      $project: {
         locomotive_id : "$locomotive_id",
         loco_time : { $subtract : ["$datetime", "$prevdatetime"] }, 
      }
   },
   {
      $group : {
         _id : "$locomotive_id",
         utilization_time : { $sum : "$loco_time" }
      }
   },
   {
      $sort : {_id : 1}
   }
])

Tanto la tabla PostgreSQL como la colección MongoDB están indexadas en datetime: 1 y locomotive_id: 1

Estas consultas se están probando en un iMac con una unidad híbrida de 2 TB y 16 GB de memoria. He recibido resultados comparables en una máquina con Windows 7 con 8 GB de memoria y un SSD de 256 GB.

¡Gracias!

** Actualización: estoy publicando los resultados de EXPLAIN (BUFFERS, ANALYZE) después de que se publique mi pregunta

"Sort  (cost=146036.84..146036.88 rows=19 width=24) (actual time=2182.443..2182.457 rows=152 loops=1)"
"  Sort Key: locomotive_id"
"  Sort Method: quicksort  Memory: 36kB"
"  Buffers: shared hit=13095"
"  ->  HashAggregate  (cost=146036.24..146036.43 rows=19 width=24) (actual time=2182.144..2182.360 rows=152 loops=1)"
"        Buffers: shared hit=13095"
"        ->  Bitmap Heap Scan on bpkdmp  (cost=12393.84..138736.97 rows=583942 width=24) (actual time=130.409..241.087 rows=559529 loops=1)"
"              Recheck Cond: ((datetime >= '2013-07-26 00:00:00'::timestamp without time zone) AND (datetime <= '2013-07-26 23:59:59.9999'::timestamp without time zone))"
"              Buffers: shared hit=13095"
"              ->  Bitmap Index Scan on bpkdmp_datetime_ix  (cost=0.00..12247.85 rows=583942 width=0) (actual time=127.707..127.707 rows=559529 loops=1)"
"                    Index Cond: ((datetime >= '2013-07-26 00:00:00'::timestamp without time zone) AND (datetime <= '2013-07-26 23:59:59.9999'::timestamp without time zone))"
"                    Buffers: shared hit=1531"
"Total runtime: 2182.620 ms"

** Actualización: Mongo explica:

Explicar de MongoDB

{
"serverPipeline" : [
    {
        "query" : {
            "datetime" : {
                "$gte" : ISODate("2013-07-26T04:00:00Z"),
                "$lt" : ISODate("2013-07-27T04:00:08.999Z")
            }
        },
        "projection" : {
            "datetime" : 1,
            "locomotive_id" : 1,
            "prevdatetime" : 1,
            "_id" : 1
        },
        "cursor" : {
            "cursor" : "BtreeCursor datetime_1",
            "isMultiKey" : false,
            "n" : 559572,
            "nscannedObjects" : 559572,
            "nscanned" : 559572,
            "nscannedObjectsAllPlans" : 559572,
            "nscannedAllPlans" : 559572,
            "scanAndOrder" : false,
            "indexOnly" : false,
            "nYields" : 1,
            "nChunkSkips" : 0,
            "millis" : 988,
            "indexBounds" : {
                "datetime" : [
                    [
                        ISODate("2013-07-26T04:00:00Z"),
                        ISODate("2013-07-27T04:00:08.999Z")
                    ]
                ]
            },
            "allPlans" : [
                {
                    "cursor" : "BtreeCursor datetime_1",
                    "n" : 559572,
                    "nscannedObjects" : 559572,
                    "nscanned" : 559572,
                    "indexBounds" : {
                        "datetime" : [
                            [
                                ISODate("2013-07-26T04:00:00Z"),
                                ISODate("2013-07-27T04:00:08.999Z")
                            ]
                        ]
                    }
                }
            ],
            "oldPlan" : {
                "cursor" : "BtreeCursor datetime_1",
                "indexBounds" : {
                    "datetime" : [
                        [
                            ISODate("2013-07-26T04:00:00Z"),
                            ISODate("2013-07-27T04:00:08.999Z")
                        ]
                    ]
                }
            },
            "server" : "Michaels-iMac.local:27017"
        }
    },
    {
        "$project" : {
            "locomotive_id" : "$locomotive_id",
            "loco_time" : {
                "$subtract" : [
                    "$datetime",
                    "$prevdatetime"
                ]
            }
        }
    },
    {
        "$group" : {
            "_id" : "$locomotive_id",
            "utilization_time" : {
                "$sum" : "$loco_time"
            }
        }
    },
    {
        "$sort" : {
            "sortKey" : {
                "_id" : 1
            }
        }
    }
],
"ok" : 1
}
Mike A
fuente
1
Para la consulta de PostgreSQL, mostrar EXPLAIN (BUFFERS, ANALYZE)salida por favor. Además, la versión PostgreSQL. (He votado para mover esto a dba.SE)
Craig Ringer
... e información sobre el plan MongoDB? docs.mongodb.org/manual/reference/method/cursor.explain
Craig Ringer
2
Aunque es difícil escapar de la exageración NoSQL, los RDBMS tradicionales son mejores y mucho más maduros en agregados cualquier día. Las bases de datos NoSQL están optimizadas para la indexación y recuperación de claves principales por clave y no para ese tipo de consultas.
Alexandros
Puede que haya dejado fuera un pequeño detalle. Hay más de 200 campos en cada documento. Esta fue una importación directa desde una base de datos PostgreSQL. Muchos de los valores de campo son nulos. Recordé que MongoDB no era particularmente aficionado a los valores nulos. Hice otra importación con <20 campos de datos relevantes y el rendimiento de la consulta es mejor. Tengo <3000 ms en una máquina con 8 GB de memoria y una HD más lenta. Voy a comenzar una nueva prueba en una máquina mucho más potente en breve.
Mike A
El índice Mongodb {datetime: 1, prevdatetime: 1}debería funcionar mejor que el índice actual, ya que mongodb se filtra en datetime y prevdatetime. Disminuiría la cantidad de documentos que deben escanearse.
rubish

Respuestas:

8

Todo lo que PostgreSQL está haciendo aquí es una exploración de montón de mapa de bits bpkdmp_datetime_ixpara encontrar bloques que pueden contener filas coincidentes, luego una exploración de montón de esos bloques para encontrar filas coincidentes bpkdmp. Luego agrupa las filas en cubos hash utilizando los hashes de la clave de agrupación, suma cada cubo y clasifica los resultados. Es un plan de consulta simple y básico: podría funcionar mejor si le echas mucho work_mem, pero también podría no hacerlo.

Tampoco hay paralelismo en ninguna parte de esa consulta; Todo sucederá en un núcleo.

Solo puedo suponer que MongoDB está utilizando un método menos eficiente o que no se beneficia de un índice apropiado. Tendría que mostrar la explainconsulta MongoDB para que un comentario útil allí sea posible; ver cursor.explain.

Craig Ringer
fuente