Blog

MongoDB-Abfrageleistung: Grundlegendes zum Abfrageplaner und zu erweiterten Indextypen

Mark Thomas
May 11, 2020

Die Beziehung jedes Ingenieurs zu einer Datenbank besteht aus mehreren Phasen. Sie könnten so aussehen:

  • Schock: „Was ist das Neue? Das ist schlimm! Daran bin ich nicht gewöhnt!“
  • Euphorie: „ICH KANN ALLES MACHEN“
  • Selbstzweifel: „Aber ich dachte, ich könnte all die Dinge tun?“
  • der Held taucht auf: „Ich muss nur dieses und jenes abstimmen und das richtige Tool für meinen Anwendungsfall verwenden“

Die Arbeit mit MongoDB ist keine Ausnahme. MongoDB gehört im Großen und Ganzen zur „NoSQL“ -Datenbankfamilie und kann etwas gewöhnungsbedürftig sein, wenn Sie an Postgres, MySQL oder ähnliche RDBMs gewöhnt sind. Sie beginnen wahrscheinlich in der Phase der „Selbstzweifel“, wenn Sie vor Kurzem in der Produktion mit MongoDB gearbeitet haben und eine erhöhte Anwendungsnutzung feststellen. Die Dinge sind möglicherweise nicht so einfach wie das Indizieren einiger offensichtlicher Felder. Wir bei FloQast nutzen MongoDB seit mehr als ein paar Jahren intensiv und haben festgestellt, dass Sie, wie jede produktionstaugliche Technologie, sie wirklich verstehen müssen, wenn Sie die Bedürfnisse vieler tausend Benutzer erfüllen wollen. In diesem Beitrag werden wir uns einige Bereiche ansehen, die für die MongoDB-Leistung „über die Grundlagen hinausgehen“. Sie erhalten einige hilfreiche Tools, z. B. die Arbeit mit dem Abfrageplaner und das Wissen, wann spezielle Indizes verwendet werden müssen.

Was abfragen?

Viele ausgereifte Datenbanken haben unglaublich komplexe Architekturen. Sie bestehen oft aus Dingen wie Speicher-Engines, Abfrageplanern und Optimierern, Replikationsschemata, Sharding-Ansätzen, Festplattenspeicherformaten, On-Wire-Protokollen usw. Die meisten davon sollten keine Dinge sein, über die ein Anwendungsentwickler viel Zeit nachdenken muss, wenn überhaupt. Aber eine davon kann unglaublich nützlich sein: der Abfrageplaner. Die meisten Datenbanken mit einer Abfragesprache (SQL für relationale Datenbanken, die MongoDB-Abfragesprache für MongoDB) haben oder werden irgendwann eine habenAbfrageplanerundAbfrageoptimierer. Diese beiden Komponenten arbeiten zusammen, um Anfragen von Programmen und Benutzern entgegenzunehmen und sie so effizient und schnell wie möglich zu gestalten. Dies sind hochkomplexe Bereiche, die aber ziemlich selbsterklärend sind:

  • Abfrageplaner: nimmt eine optimierte Abfrage entgegen und bestimmt, wie Daten aus der Speicher-Engine der Datenbank abgerufen werden. Gibt an, welche Schritte zum Abrufen von Daten unternommen werden. Dies kann Schritte wie das Transformieren, Abrufen oder Sortieren von Daten beinhalten
  • Abfrageoptimierer: Bevor der Planer eine Abfrage ausführt, sucht er nach Möglichkeiten, eine Abfrage zu verbessern. Stellt Fragen wie „Kann ich einen Index verwenden? Kann ich Indizes kombinieren? Welcher Plan ist der beste?“ Kostenorientierte Optimierer bewerten oft so viele Pläne, wie Zeit für eine bestimmte Abfrage zur Verfügung steht, und wählen dann den kostengünstigsten aus.

Lassen Sie uns einige Abfragen planen

Hier ist das Beispielschema, das wir beim Lernen verwenden werden (falls Sie es noch nicht gelesen haben)einer unserer anderen Beiträge über die Leistung von MongoDB). Wir entwickeln eine Anwendung für einen imaginären Mitfahrdienst: FloQars.

{
    "_id": "5cf0029caff5056591b0ce7d",
    "name": "A. Person",
    "email": "a.person@example.com",
    "avatarURL": "https://static.image.com/1234",
    "password": "$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK",
    "rides": [
        {
            "_id": "5cf0029caff5056591b0ce2f",
            "date": "01/01/2006",
            "driverID": "5cf0029caff5056591b0ce20",
            "from": {
                "lat": -70.55044,
                "lng": 165.39229
            },
            "to": {
                "lat": -29.9244,
                "lng": 88.2593
            }
        },
        {
            "_id": "5cf0029caff5056591b0ce2a",
            "date": "01/02/2006",
            "from": {
                "lat": -70.55044,
                "lng": 165.39229
            },
            "to": {
                "lat": -29.9244,
                "lng": 88.2593
            }
        }
    ],
    "location": {
        "current": {
            "lat": -70.55044,
            "lng": 165.39229
        }
    }
}

Richten wir eine Abfrage für ein Textfeld ein, damit unsere fiktiven Benutzer ihre Fahrtnotizen durchsuchen können. Wir könnten so etwas versuchen, um anzufangen: {RideNotes: /receive/}.

Einige Erfahrungen mit Datenbanken könnten uns zeigen, dass dies eine schlechte Idee sein wird: Wir schränken die Abfrage nicht anhand anderer Kriterien ein und verwenden einen regulären Ausdruck. Dies bedeutet wahrscheinlich, dass jedes Dokument in einer Sammlung geprüft wird. Wir können das benutzen.erkläre ()Befehl, um unsere Annahmen zu überprüfen. DaserklärenEin Befehl in einer Abfrage macht die Ausgabe der Abfrageplaner-/Optimierungskomponenten verfügbar, damit wir mehr über unsere Abfrage erfahren können. Lass es uns versuchen!

db.riders.explain().find({ rideNotes: /receive/ })

ergibt:

{
  "queryPlanner" : {
    "plannerVersion" : 1,
    "namespace" : "floqars.riders",
    "indexFilterSet" : false,
    "parsedQuery" : {
      "rideNotes" : {
        "$regex" : "receive"
      }
    },
    "queryHash" : "8A9E186F",
    "planCacheKey" : "8A9E186F",
    "winningPlan" : {
      "stage" : "COLLSCAN",
      "filter" : {
        "rideNotes" : {
          "$regex" : "receive"
        }
      },
      "direction" : "forward"
    },
    "rejectedPlans" : [ ]
  },
  "ok" : 1,
  "$clusterTime" : {
    "clusterTime" : Timestamp(1588804213, 35),
    "signature" : {
      "hash" : BinData(0,"+2onXLjgqb4/oXSs1b+80prrEKk="),
      "keyId" : NumberLong("6759458550222684161")
    }
  },
  "operationTime" : Timestamp(1588804213, 35)
}

Es gibt einige wichtige Bereiche, die es zu beachten gilt:Gewinnplanund die Bühne (n) darin. Dies wird uns sagen, was der Abfrageoptimierer als die beste Methode zur Ausführung unserer Abfrage erachtet hat und welche Phasen sie umfasst. Du solltest hoffen zu sehenIXSCAN(Index-Scan) so viel wie möglich hier undCOLLSCAN(Sammlungsscan) so wenig wie möglich. Je mehr MonogDB Ihre Indizes nutzen kann, desto besser. Fügen wir unserer Abfrage ein beliebiges Sortierfeld hinzu und sehen, welche anderen Phasen an unserer Abfrage beteiligt sein könnten:

‍db.riders.explain().find({ rideNotes: /receive/ }, { _id: -1 })
{
  "queryPlanner" : {
    "plannerVersion" : 1,
    "namespace" : "floqars.riders",
    "indexFilterSet" : false,
    "parsedQuery" : {
      "rideNotes" : {
        "$regex" : "receive"
      }
    },
    "queryHash" : "8A9E186F",
    "planCacheKey" : "8A9E186F",
    "winningPlan" : {
      "stage" : "COLLSCAN",
      "filter" : {
        "rideNotes" : {
          "$regex" : "receive"
        }
      },
      "direction" : "forward"
    },
    "rejectedPlans" : [ ]
  },
  "ok" : 1,
  "$clusterTime" : {
    "clusterTime" : Timestamp(1588804213, 35),
    "signature" : {
      "hash" : BinData(0,"+2onXLjgqb4/oXSs1b+80prrEKk="),
      "keyId" : NumberLong("6759458550222684161")
    }
  },
  "operationTime" : Timestamp(1588804213, 35)
}

Beachten Sie hier, dass wir eine Sortierbedingung hinzugefügt haben _id der Abfrage wurde eine zusätzliche Stufe hinzugefügt. Das liest sich irgendwie rückwärts: Mongo scannt zuerst alles und wendet einen Regex-Filter an. Dann gibt er Ergebnisse zurück, die nach unserem Schlüssel sortiert sind. Dieser Plan unterscheidet sich geringfügig von der „normalen“ Sortierung mit einem anderen Feld, weil_idist bereits ein indiziertes Feld.

Einige der anderen Phasen umfassen:

  • PROJEKTION: wählt bestimmte Datenattribute aus und verwirft den Rest
  • HOLEN: verwendet IDs, die in einer früheren Phase erstellt wurden, um Dokumente abzurufen
  • LIMIT: prüft eine begrenzte Anzahl von Dokumenten und leitet diese nur an die nächste Stufe weiter
  • COLLSCAN: liest Dokumente nacheinander und geht zur nächsten Stufe über
  • IXSCAN: liest einen Index und übergibt die Ergebnisse an die nächste Stufe
  • ÜBERSPRINGEN: überspringt N dokumentiert und leitet den Rest an die nächste Stufe weiter
  • SCHLÜSSELGENERATOR SORTIEREN: vor einer Sortierphase; generiert einen Schlüssel zum Sortieren
  • SORTIEREN: sortiert Dokumente mit einem Sortierschlüssel
  • SHARD_MERGE und SHARDING_FILTER: zum Kombinieren/Filtern von Dokumenten aus Abfragen nach Shard-Clustern

Von der Theorie zur Praxis

Es ist großartig und alles, was wir uns mit Filtern ansehen können, aber vielleicht ist Ihnen etwas aufgefallen: Unsere Abfragepläne zeigen nicht die „echten“ Ergebnisse unserer langsamen Suchanfragen. DaserklärenDer Befehl wird schnell ausgeführt und sagt uns nur, was der Planerwürdetun. Wir sollten einen Verbosity-Modus wählen fürerklärendamit wir mehr Details darüber sehen können, wie sich unsere Abfragen tatsächlich verhalten werden. Sie können das tun, indem Sie einen Ausführlichkeitsmodus an dieerklärenAufruf in Ihrer Abfrage wie folgt:

db.riders.explain('allPlansExecution').find({ rideNotes: /receive/ }, { avatarURL: -1 }) 

Dies ergibt viel mehr Details:

{
    "queryPlanner": {
        "plannerVersion": 1,
        "namespace": "floqars.riders",
        "indexFilterSet": false,
        "parsedQuery": {
            "rideNotes": {
                "$regex": "receive"
            }
        },
        "winningPlan": {
            "stage": "PROJECTION_SIMPLE",
            "transformBy": {
                "avatarURL": -1
            },
            "inputStage": {
                "stage": "COLLSCAN",
                "filter": {
                    "rideNotes": {
                        "$regex": "receive"
                    }
                },
                "direction": "forward"
            }
        },
        "rejectedPlans": []
    },
    "executionStats": {
        "executionSuccess": true,
        "nReturned": 29848,
        "executionTimeMillis": 2906,
        "totalKeysExamined": 0,
        "totalDocsExamined": 1615003,
        "executionStages": {
            "stage": "PROJECTION_SIMPLE",
            "nReturned": 29848,
            "executionTimeMillisEstimate": 1419,
            "works": 1615005,
            "advanced": 29848,
            "needTime": 1585156,
            "needYield": 0,
            "saveState": 12617,
            "restoreState": 12617,
            "isEOF": 1,
            "transformBy": {
                "avatarURL": -1
            },
            "inputStage": {
                "stage": "COLLSCAN",
                "filter": {
                    "rideNotes": {
                        "$regex": "receive"
                    }
                },
                "nReturned": 29848,
                "executionTimeMillisEstimate": 1400,
                "works": 1615005,
                "advanced": 29848,
                "needTime": 1585156,
                "needYield": 0,
                "saveState": 12617,
                "restoreState": 12617,
                "isEOF": 1,
                "direction": "forward",
                "docsExamined": 1615003
            }
        },
        "allPlansExecution": []
    },
    "ok": 1,
    "$clusterTime": {
        "clusterTime": Timestamp(1588806878, 53),
        "signature": {
            "hash": BinData(0, "rPDyp8XIs+c/T4nZVFPC22kskN0="),
            "keyId": NumberLong("6759458550222684161")
        }
    },
    "operationTime": Timestamp(1588806878, 53)
}

Wow! Das sind viel mehr Details. Eine Sache, die sofort auffallen könnte, ist Ausführungszeit in Millisdas sind fast 3 Sekunden. Das ist eine lange Zeit für eine Datenbankabfrage, insbesondere für eine benutzerorientierte. Wir möchten, dass unsere Benutzer Zeit sparen und nicht darauf warten müssen, dass Dinge geladen werden und das nicht fällig wird. Schauen wir uns eine der Phasen genauer an:

"inputStage" : {
        "stage" : "COLLSCAN",
        "filter" : {
          "rideNotes" : {
            "$regex" : "receive"
          }
        },
        "nReturned" : 29848,
        "executionTimeMillisEstimate" : 1400,
        "works" : 1615005,
        "advanced" : 29848,
        "needTime" : 1585156,
        "needYield" : 0,
        "saveState" : 12617,
        "restoreState" : 12617,
        "isEOF" : 1,
        "direction" : "forward",
        "docsExamined" : 1615003
      }

Hier gibt es einiges zu beachten. Zunächst können Sie sehen, in wie vielen Dokumenten diese Phase zurückgegeben wurden Ist zurückgekehrt. Abhängig von Ihrer Anfrage kann dies eine Zahl sein, mit der Sie zufrieden sind oder auch nicht. In einer perfekten Welt gibt jede Phase so wenige Dokumente wie möglich zurück. In einigen Fällen sucht ein Benutzer jedoch möglicherweise wirklich nach einer großen Datenmenge, und in diesen Fällen benötigen Sie eine Paginierungsstrategie oder ähnliches. Als Nächstes sollten Sie das nützlichste Feld im Abfrageplaner beachten:Geprüfte Dokumente. So viele Dokumente wurden in einer bestimmten Phase geprüft, und Sie möchten, dass diese Zahl niedrig ist. Noch wichtiger ist, dass Sie sich das Verhältnis von ansehen möchtenGeprüfte Dokumenteundn Ist zurückgekehrt. Anhand dieser Zahlen können Sie feststellen, „wie viel Arbeit MongoDB leistet, um mir nützliche Daten zurückzugeben?“. Wir können hier sehen, dass unsere „Trefferquote“ 29848/1615003 oder ~ 1,8% beträgt. Das ist ziemlich schlimm - wir arbeiten mit einem abscheulichen Effizienzniveau. Wenn Ihr Cluster eine große Anzahl von Dokumenten im Vergleich zu denen untersucht, die er zurückgibt, werden Sie wahrscheinlich einige Dinge passieren sehen:

  • insgesamt längere Abfragezeiten
  • bessere Auslastung der CPU- und Speicherressourcen Ihres Clusters
  • Aufenthalt und Räumung des Chopier-Caches
  • gesperrte und blockierende Abfragen unter Last

All dies führt zu einer schlechteren Leistung, was zu mehr Aufwand und frustrierten Benutzern führt. Die gute Nachricht ist, dass Sie diese Fälle entweder in der Vorproduktion erkennen können, wenn Sie Abfragen testen, oder indem Sie sie einschalten der MongoDB-Profiler damit langsame Abfragen protokolliert werden und Benachrichtigungen generiert werden.

Spezielle Indextypen

Die Indextypen von MongoDB sind im Allgemeinen für die meisten Anwendungsanforderungen ausreichend. Für die meisten Anwendungsfälle können Sie Einzelfeld-, Verbund- und Mehrschlüsselindizes verwenden. Wenn Sie einen Gutachter für diese benötigen, Schauen Sie sich einen unserer anderen Beiträge über die Leistung von MonogDB an. Aber manchmal braucht man etwas Spezielleres. Unsere obige Textsuchabfrage ist wahrscheinlich ein guter Fall; Regex-Suchen werden aufgrund ihrer Natur ziemlich langsam sein, fast egal, was wir tun. Wir könnten unserer Abfrage eine weitere Einschränkung hinzufügen, um nur die Daten eines bestimmten Benutzers abzufragen oder vielleicht unsere Indexierungsstrategie zu verbessern. Aber was könnten wir tun, wenn das nicht möglich wäre? Mongo hat eine Antwort: Wir können wahrscheinlich den Volltextsuchindextyp von Mongo verwenden. Ich sage „wahrscheinlich“, weil die Volltextsuche eine Domäne für sich ist und es Systeme wie ElasticSearch gibt, die Sie für eine extrem anspruchsvolle Suche möglicherweise in Betracht ziehen müssen. Der Textindextyp von Mongo sollte uns zumindest weiter bringen, als es unsere Regex derzeit tut. Fügen wir den Volltextindex zumHinweise zur FahrtFeld:

‍db.riders.createIndex({ rideNotes: "text", description: "my first full-text index"})

Ein paar Dinge, die Sie zum Volltextindextyp beachten sollten, bevor wir versuchen, damit abzufragen:

  • Sie können nur einen Volltextindex pro Sammlung haben. Es kann jedoch für mehrere Felder gelten
  • Dieser Indextyp nimmt aufgrund der bei der Volltextsuche verwendeten Datenstrukturen (Beitragslisten, Stoppwörter usw.) in der Regel viel mehr Platz ein als andere. Dieser Index nimmt ~200 MB gegenüber 16,2 MB für den ein _id Index in dieser speziellen Sammlung.
  • Sie müssen eine bestimmte Abfragesyntax verwenden, um die Volltextsuche nutzen zu können
  • Wie bei jedem Index kann es bei einem zusätzlichen Index zu geringfügigen Leistungseinbußen bei der Schreibleistung kommen

Aber genug von den Kompromissen - lassen Sie uns dieses Ding auf die Probe stellen. Dies ist ein guter Zeitpunkt, um Sie zu warnen, dass die Ausgabe des Abfrageplaners ziemlich groß sein kann! Machen Sie sich aber keine Sorgen - Sie können es Schritt für Schritt angehen.

{
    "queryPlanner": {
        "plannerVersion": 1,
        "namespace": "floqars.riders",
        "indexFilterSet": false,
        "parsedQuery": {
            "$text": {
                "$search": "receive",
                "$language": "english",
                "$caseSensitive": false,
                "$diacriticSensitive": false
            }
        },
        "winningPlan": {
            "stage": "PROJECTION_SIMPLE",
            "transformBy": {
                "_id": -1
            },
            "inputStage": {
                "stage": "TEXT",
                "indexPrefix": {},
                "indexName": "rideNotes_text_description_text",
                "parsedTextQuery": {
                    "terms": ["receiv"],
                    "negatedTerms": [],
                    "phrases": [],
                    "negatedPhrases": []
                },
                "textIndexVersion": 3,
                "inputStage": {
                    "stage": "TEXT_MATCH",
                    "inputStage": {
                        "stage": "FETCH",
                        "inputStage": {
                            "stage": "OR",
                            "inputStage": {
                                "stage": "IXSCAN",
                                "keyPattern": {
                                    "_fts": "text",
                                    "_ftsx": 1
                                },
                                "indexName": "rideNotes_text_description_text",
                                "isMultiKey": true,
                                "isUnique": false,
                                "isSparse": false,
                                "isPartial": false,
                                "indexVersion": 2,
                                "direction": "backward",
                                "indexBounds": {}
                            }
                        }
                    }
                }
            }
        },
        "rejectedPlans": []
    },
    "executionStats": {
        "executionSuccess": true,
        "nReturned": 33820,
        "executionTimeMillis": 299,
        "totalKeysExamined": 33820,
        "totalDocsExamined": 33820,
        "executionStages": {
            "stage": "PROJECTION_SIMPLE",
            "nReturned": 33820,
            "executionTimeMillisEstimate": 222,
            "works": 33821,
            "advanced": 33820,
            "needTime": 0,
            "needYield": 0,
            "saveState": 264,
            "restoreState": 264,
            "isEOF": 1,
            "transformBy": {
                "_id": -1
            },
            "inputStage": {
                "stage": "TEXT",
                "nReturned": 33820,
                "executionTimeMillisEstimate": 208,
                "works": 33821,
                "advanced": 33820,
                "needTime": 0,
                "needYield": 0,
                "saveState": 264,
                "restoreState": 264,
                "isEOF": 1,
                "indexPrefix": {},
                "indexName": "rideNotes_text_description_text",
                "parsedTextQuery": {
                    "terms": ["receiv"],
                    "negatedTerms": [],
                    "phrases": [],
                    "negatedPhrases": []
                },
                "textIndexVersion": 3,
                "inputStage": {
                    "stage": "TEXT_MATCH",
                    "nReturned": 33820,
                    "executionTimeMillisEstimate": 206,
                    "works": 33821,
                    "advanced": 33820,
                    "needTime": 0,
                    "needYield": 0,
                    "saveState": 264,
                    "restoreState": 264,
                    "isEOF": 1,
                    "docsRejected": 0,
                    "inputStage": {
                        "stage": "FETCH",
                        "nReturned": 33820,
                        "executionTimeMillisEstimate": 206,
                        "works": 33821,
                        "advanced": 33820,
                        "needTime": 0,
                        "needYield": 0,
                        "saveState": 264,
                        "restoreState": 264,
                        "isEOF": 1,
                        "docsExamined": 33820,
                        "alreadyHasObj": 0,
                        "inputStage": {
                            "stage": "OR",
                            "nReturned": 33820,
                            "executionTimeMillisEstimate": 31,
                            "works": 33821,
                            "advanced": 33820,
                            "needTime": 0,
                            "needYield": 0,
                            "saveState": 264,
                            "restoreState": 264,
                            "isEOF": 1,
                            "dupsTested": 33820,
                            "dupsDropped": 0,
                            "inputStage": {
                                "stage": "IXSCAN",
                                "nReturned": 33820,
                                "executionTimeMillisEstimate": 26,
                                "works": 33821,
                                "advanced": 33820,
                                "needTime": 0,
                                "needYield": 0,
                                "saveState": 264,
                                "restoreState": 264,
                                "isEOF": 1,
                                "keyPattern": {
                                    "_fts": "text",
                                    "_ftsx": 1
                                },
                                "indexName": "rideNotes_text_description_text",
                                "isMultiKey": true,
                                "isUnique": false,
                                "isSparse": false,
                                "isPartial": false,
                                "indexVersion": 2,
                                "direction": "backward",
                                "indexBounds": {},
                                "keysExamined": 33820,
                                "seeks": 1,
                                "dupsTested": 33820,
                                "dupsDropped": 0
                            }
                        }
                    }
                }
            }
        }
    },
    "ok": 1,
    "$clusterTime": {
        "clusterTime": Timestamp(1588808094, 73),
        "signature": {
            "hash": BinData(0, "Y6IyUuYrIT7kMyn9Oob5OHZ9OWE="),
            "keyId": NumberLong("6759458550222684161")
        }
    },
    "operationTime": Timestamp(1588808094, 73)
}

Hier gibt es einiges zu beachten, aber fangen wir mit der guten Nachricht an: Das ging viel, viel schneller! Hier sind die wichtigsten Ausführungsstatistiken, die uns am wichtigsten sind:

"nReturned" : 33820,
"executionTimeMillis" : 299,
"totalKeysExamined" : 33820,
"totalDocsExamined" : 33820,

Der Abfrageplan war auch viel komplizierter als zuvor und Sie haben vielleicht einige Dinge bemerkt, die wir zuvor nicht behandelt haben, wieODERoderTEXTStufen. Nehmen Sie sich eine Minute Zeit, um die Etappen durchzulesen und sie in etwa einem Satz für sich selbst zu beschreiben. Versuchen Sie, Dinge wie „ein Index wird gescannt und gibt IDs zurück“ oder „eine FETCH-Stufe ruft Dokumente von IDs ab“ ins Visier. Sie sollten dies immer häufiger tun, wenn Sie mit dem Abfrageplaner und Optimierer arbeiten. Mit der Zeit werden Sie anfangen zu „sehen“, was Mongo tut, und darüber nachdenken, wie die Dinge verbessert werden könnten. Sie werden vielleicht auch überrascht sein, was der Optimierer macht, und das ist in Ordnung — probieren Sie verschiedene Kombinationen aus, um zu sehen, was Ihnen die besten Ergebnisse liefert. Der Abfrageoptimierer einer Datenbank wird oft, naja, im Laufe der Zeit vom Kernteam optimiert und wird in der Regel „kostenlos“ besser und intelligenter, wenn Sie auf neuere Versionen der Datenbank aktualisieren. Wie cool ist das! Es kommt nicht oft vor, dass Dinge intelligenter und schneller werden, ohne dass ein Benutzer direkt etwas unternehmen muss.

Wir können wirklich sehen, dass dieser Index besser für unseren Anwendungsfall geeignet ist: Er war ungefähr zehnmal schneller (~300 ms), lieferte mehr Ergebnisse und wir haben nur das zurückgegeben, was wir untersucht haben (denken Sie daran, dass wichtign Ist zurückgekehrt/Geprüfte DokumenteVerhältnis). All dies zusammen bedeutet, dass dies eine viel effizientere Abfrage ist, und die Zeit, die dafür benötigt wird, bestätigt dies wirklich. Darüber hinaus kann der Benutzer flexiblere Suchanfragen anwenden, als es uns die Regex zuvor ermöglicht hätte. Huzzah! Vergessen Sie jedoch nicht die Kompromisse: Dieses schnellere Lesen ist mit einer leichten Schreibstrafe verbunden und nimmt viel Speicherplatz in Anspruch.

Einpacken

Wir haben uns die Arbeit mit dem Abfrageplaner und Optimierer angesehen, um unsere MongoDB-Abfragen zu verstehen und zu verbessern. Wir haben uns auch einige spezielle Indextypen angesehen, die MongoDB für Fälle anbietet, in denen reguläre Indizes nicht ausreichen. Ich hoffe, Sie fühlen sich besser gewappnet, um langsame Abfragen zu bewältigen, und dass sich die Arbeit mit MongoDB etwas weniger nach einer dunklen Kunst anfühlt. Viel Spaß beim Abfragen!