Performances de MongoDB par rapport à PostgreSQL avec 5,5 millions de lignes / documents

10

Quelqu'un peut-il m'aider à comparer ces requêtes et à expliquer pourquoi la requête PostgreSQL s'exécute en un peu moins de 2000 ms et que la requête d'agrégation MongoDB prend près de 9 000 ms et parfois jusqu'à 130 000 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

Requête 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

MongoDB Query

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}
   }
])

La table PostgreSQL et la collection MongoDB sont indexées sur datetime: 1 et locomotive_id: 1

Ces requêtes sont en cours de test sur un iMac avec un lecteur hybride de 2 To et 16 Go de mémoire. J'ai reçu des résultats comparables sur une machine Windows 7 avec 8 Go de mémoire et un SSD de 256 Go.

Merci!

** Mise à jour: je publie les résultats EXPLAIN (BUFFERS, ANALYZE) après la publication de ma question

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

** Mise à jour: Mongo explique:

Expliquez 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
la source
1
Pour la EXPLAIN (BUFFERS, ANALYZE)sortie d' affichage des requêtes PostgreSQL, veuillez. Aussi, version PostgreSQL. (J'ai voté pour le déplacer vers dba.SE)
Craig Ringer
... et des informations sur le plan MongoDB? docs.mongodb.org/manual/reference/method/cursor.explain
Craig Ringer
2
Bien qu'il soit difficile d'échapper au battage médiatique NoSQL, les SGBDR traditionnels sont meilleurs et beaucoup plus matures en agrégats chaque jour. Les bases de données NoSQL sont optimisées pour l'indexation et la récupération des clés primaires par clé et non pour ce type de requêtes.
Alexandros
J'ai peut-être omis un léger détail. Il y a plus de 200 champs dans chaque document. Il s'agissait d'une importation directe à partir d'une base de données PostgreSQL. De nombreuses valeurs de champ sont nulles. J'ai rappelé que MongoDB n'aimait pas particulièrement les valeurs nulles. J'ai fait une autre importation avec <20 champs de données pertinentes et les performances de la requête sont meilleures en amplitude. J'obtiens moins de 3000 ms sur une machine avec 8 Go de mémoire et une HD plus lente. Je vais bientôt commencer un nouveau test sur une machine beaucoup plus puissante.
Mike A
L'index Mongodb {datetime: 1, prevdatetime: 1}devrait fonctionner mieux que l'index actuel, car mongodb filtre sur datetime et prevdatetime. Cela diminuerait le nombre de documents à numériser.
rubish

Réponses:

8

Tout ce que PostgreSQL fait ici est une analyse de tas bitmap bpkdmp_datetime_ixpour trouver des blocs qui pourraient contenir des lignes correspondantes, puis une analyse de tas de ces blocs pour trouver des lignes correspondantes bpkdmp. Il regroupe ensuite les lignes dans des compartiments de hachage à l'aide des hachages de la clé de regroupement, additionne chaque compartiment et trie les résultats. C'est un plan de requête simple et basique - il pourrait mieux fonctionner si vous en jetez beaucoup work_mem, mais il pourrait ne pas le faire aussi.

Il n'y a aucun parallélisme nulle part dans cette requête non plus; tout se passera sur un seul cœur.

Je ne peux que supposer que MongoDB utilise une méthode moins efficace ou ne bénéficie pas d'un index approprié. Vous auriez besoin d'afficher le explainpour la requête MongoDB pour qu'un commentaire utile soit possible; voir cursor.explain.

Craig Ringer
la source