Desempenho do MongoDB vs. PostgreSQL com 5,5 milhões de linhas / documentos

10

Alguém pode me ajudar a comparar essas consultas e explicar por que a consulta do PostgreSQL é executada em pouco menos de 2000ms e a consulta agregada do MongoDB leva quase 9000ms e, às vezes, até 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 do 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}
   }
])

A tabela do PostgreSQL e a coleção do MongoDB são indexadas no datetime: 1 e locomotive_id: 1

Essas consultas estão sendo testadas em um iMac com uma unidade híbrida de 2 TB e 16 GB de memória. Recebi resultados comparáveis ​​em uma máquina Windows 7 com 8 GB de memória e um SSD de 256 GB.

Obrigado!

** Atualização: estou postando os resultados EXPLAIN (BUFFERS, ANALYZE) depois que minha pergunta foi publicada

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

** Atualização: Mongo explica:

Explique do 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
fonte
11
Para a consulta do PostgreSQL, mostre a EXPLAIN (BUFFERS, ANALYZE)saída, por favor. Além disso, versão PostgreSQL. (Eu votei para mover este para dba.SE)
Craig Ringer
... e informações sobre o plano do MongoDB? docs.mongodb.org/manual/reference/method/cursor.explain
Craig Ringer
2
Embora seja difícil escapar do hype do NoSQL, os RDBMS tradicionais são melhores e muito mais maduros em agregados a qualquer dia. Os bancos de dados NoSQL são otimizados para indexação e recuperação de chave primária por chave e não para esse tipo de consulta.
Alexandros
Eu posso ter deixado de fora um pequeno detalhe. Existem mais de 200 campos em cada documento. Esta foi uma importação direta de um banco de dados PostgreSQL. Muitos valores do campo são nulos. Lembrei que o MongoDB não gostava particularmente de valores nulos. Fiz outra importação com <20 campos de dados relevantes e o desempenho da consulta é melhor com magnitudes. Estou recebendo <3000ms em uma máquina com 8 GB de memória e um HD mais lento. Vou iniciar um novo teste em uma máquina muito mais poderosa em breve.
Mike A
O índice Mongodb {datetime: 1, prevdatetime: 1}deve ter um desempenho melhor que o índice atual, pois o mongodb filtra em data e hora anteriores. Reduziria o número de documentos que precisam ser digitalizados.
rubish

Respostas:

8

Tudo o que o PostgreSQL está fazendo aqui é uma varredura de heap de bitmap bpkdmp_datetime_ixpara encontrar blocos que podem conter linhas correspondentes e, em seguida, uma varredura de heap desses blocos para encontrar linhas correspondentes bpkdmp. Em seguida, agrupa as linhas em buckets de hash usando hashes da chave de agrupamento, soma cada bucket e classifica os resultados. É um plano de consulta simples e básico - ele pode ter um desempenho melhor se você jogar muito work_memnele, mas também não.

Também não há paralelismo em nenhuma parte dessa consulta; tudo vai acontecer em um núcleo.

Só posso assumir que o MongoDB está usando um método menos eficiente ou não está se beneficiando de um índice apropriado. Você precisaria mostrar a explainconsulta do MongoDB para que um comentário útil fosse possível; veja cursor.explain.

Craig Ringer
fonte