Blog

Grundlagen der MongoDB-Leistung: Indizierung und Indizes

Mark Thomas
December 24, 2019

MongoDB ist eine der wichtigsten Technologien, die wir bei FloQast eingeführt haben. Wir sind der Meinung, dass dies eine unserer Kernkompetenzen ist, genau wie React, JavaScript/TypeScript/Node.js und Amazon Web Services (AWS). Was als Teil unseres ersten MVP begann, hat sich zu einer zentralen Technologie für FloQast und unsere Kunden entwickelt. Unsere Datenbank ist ein zentraler Bestandteil davon, wie wir unseren Benutzern helfen, die Bücher schneller und genauer zu schließen. Unsere Benutzer verwenden FloQast, um ihren Abschluss zu verwalten und Zeit zu sparen, indem sie Funktionen wie AutoRec und Flux verwenden — wichtige Bestandteile ihrer täglichen Aufgaben als Accountant. Das bedeutet, dass Verlangsamungen unserer Datenbankleistung es ihnen erschweren, ihre Arbeit zu erledigen. Es ist eine Sache, wenn sich jemand ein paar Stunden lang keine Katzenmemes ansehen kann. Aber wenn du deinen Job nicht machen kannst? Das ist nicht gut ‍ ♀.

Your app on a slow database

Wir wissen, wie frustrierend langsame Apps sind und legen großen Wert auf die Zeit unserer Nutzer. Wir haben im Laufe der Jahre gelernt, mit MongoDB zu arbeiten, um sicherzustellen, dass die Zeit unserer Benutzer nicht verschwendet wird, und wollten einige dieser Lektionen hier teilen. In diesem Beitrag lernen wir zunächst mehr über MongoDB-Indizes.

You app on a fast database

Ihre App auf einer langsamen DB im Vergleich zu Ihrer App auf einer performanten DB

Leistung der Datenbank

Datenbankdesign und -leistung sind ein komplexes Feld. Sie müssen über Konsistenz, Verfügbarkeit, wie gut es mit Netzwerkpartitionen umgeht, nachdenken und Mütze alles aus, die Datenbank muss einfach zu bedienen und zu bedienen sein. Lassen Sie uns das auf ein paar grundlegende Fragen reduzieren:

  1. „Wie schnell kann ich etwas lesen?“
  2. „Wie schnell kann ich etwas aktualisieren?“
  3. „Wie zuverlässig ist mein Lesen/Schreiben?“
  4. „Wie dauerhaft ist mein Update?“ In diesem Beitrag konzentrieren wir uns auf Frage #1: Wie schnell kann ich Daten lesen?

Wie schnell kann ich etwas lesen?

Sie werden wahrscheinlich auf Verlangsamungen in der Datenbank stoßen liest bevor Sie bei Einfügungen oder Aktualisierungen auf Verlangsamungen stoßen. Das liegt daran, dass viele gängige Datenbankanwendungsfälle proportional leseintensiv im Gegensatz zu schreibintensiv sind. Viele Datenbanken werden in kleinem bis mittlerem Maßstab „einfach funktionieren“, ohne dass zusätzliche Anpassungen oder Aufwand erforderlich sind. Die meisten Datenbanken werden seit über 10 Jahren entwickelt und moderne Hardware ist in der Regel für viele Anforderungen mehr als ausreichend. Das ändert sich wirklich, wenn Sie mehr hinzufügen X. X kann Volumen, Datengröße, Lesehäufigkeit, Schreibfrequenz sein — was auch immer. Wenn Sie mit der Skalierung Ihrer Datenbank beginnen, müssen Sie einige Arbeiten ausführen, um sicherzustellen, dass sie wie erwartet funktioniert. Als FloqAST zum ersten Mal gestartet wurde, konnten wir MongoDB sofort verwenden, ohne ein Tuning vornehmen zu müssen. Mongo hat großartige interne Caching-Mechanismen und ist im Allgemeinen ziemlich schnell. Aber da wir gewachsen sind und mehr Funktionen und Nutzer hinzugefügt haben und das Gesamtvolumen gestiegen ist, mussten wir Maßnahmen ergreifen, um sicherzustellen, dass unsere Nutzer die Bücher schließen können. schnell und effizient. Das Erste, was Sie bei der Skalierung Ihrer Datenbank tun müssen, ist, Ihr Datenmodell und Ihre Datenzugriffsmuster zu verstehen. Wenn Sie diese nicht heruntergeladen haben, wird die Skalierung frustrierend schwierig oder unmöglich sein. Wir mussten unsere Datenbanken im Laufe der Jahre viele Male indexieren und neu indizieren, da sich die Arbeitslast geändert hat und sich unsere Anforderungen weiterentwickelt haben. In diesem Beitrag werden wir uns einige grundlegende Techniken ansehen, mit denen Sie die Leistung Ihrer MongoDB-Datenbank verbessern können. Stellen wir uns zur Veranschaulichung eine fiktive neue Abteilung von FloQast vor, die sich auf fahrerlose Autos und Mitfahrgelegenheiten konzentriert: FloQARS. FloQars ist eine sehr leseintensive Anwendung und wird langsam langsamer, also müssen wir herausfinden, wie wir die Leistung verbessern können. Unten finden Sie ein Beispielschema für eine Reiterim FloQARS-System. Ein paar Dinge, die es zu beachten gilt:

  • das Schema verwendet Denormalisierung; es gibt Fahrten eingebettet in Reiter Modell als Array von Unterdokumenten
  • Die Geolokalisierungsinformationen des Benutzers werden als Lat/Lng-Koordinaten gespeichert
  • Einige wichtige Informationen (E-Mail, ID, Name) sind ebenfalls im Dokument gespeichert
{
    "_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
        }
    }
}

Für diese Serie haben wir einen MongoDB-Demo-Cluster eingerichtet und ihn mit rund 1,2 Millionen Rider-Dokumenten gefüllt, die alle zufällig generiert wurden. Wir werden dieses Schema und die Daten in den folgenden Beispielen verwenden.

Indizes sind die Rettung!

FloQars-Benutzer haben unserem großartigen Support-Team langsame In-App-Erfahrungen gemeldet, und die HTTP-Antwortzeiten sehen in unserem Grafana-Metrik-Dashboard viel höher aus. Wir müssen also auf jeden Fall einige Änderungen an unserer Datenbank vornehmen — und zwar schnell! Was ist zu tun? Wir kennen unser Datenmodell und unsere Datenzugriffsmuster bereits. Was kommt als Nächstes? Indizes sind die Rettung!

Live-Blick auf Indizes, die uns helfen werden, Langsamkeit zu überwindenWas ist ein Index? In Bezug auf Datenbanken ist ein Index eine Datenstruktur, die die Geschwindigkeit des Datenzugriffs verbessert, jedoch zu geringen Kosten. In unserer datenintensiven Welt hat fast jede Datenbank eine Indexierungsstrategie, und diese ist normalerweise ein wichtiger Teil des gesamten Funktionsumfangs der Datenbank. Wenn Sie noch nie von Indizes gehört haben, haben Sie sie wahrscheinlich im wirklichen Leben gesehen. Wenn Sie zum Ende eines Buches blättern, wird ein Index angezeigt, in dem angegeben ist, an welcher Stelle Begriffe oder Themen im Buch vorkommen. Die von Datenbanken verwendeten Indizes sind sich tatsächlich sehr ähnlich. Sie bieten der Datenbank eine schnellere Möglichkeit, nach Elementen in einer Abfrage zu suchen, ähnlich wie Sie den Index schnell durchsuchen könnten, um zu sehen, wo ein Begriff vorkommt, bevor Sie das Buch vollständig durchsuchen.

Ein Index in Aktion. Ohne Index muss MongoDB jedes Dokument untersuchen, um eine Abfrage zu erfüllen. Mit einem Index kann es jedoch sehr schnell eingrenzen, wonach es sucht.

Zu den gängigen MongoDB-Indextypen gehören:

  • Einzeln: Indizes für ein einzelnes Feld (z. B. „E-Mail“)
  • Mehrschlüssel: Indizes für Felder, die einen Array-Wert enthalten
  • Verbindung: Indizes für mehrere Felder in einem Dokument

Einzelfeld

Es gibt eine FloQars-Funktion, die nach Benutzern sucht, die ihre E-Mail-Adresse verwenden, und wir haben festgestellt, dass sie langsam ist (von unseren Benutzern und unseren Dashboards). Lassen Sie uns eine Beispielabfrage durchführen, um eine E-Mail zu finden, von der wir bereits wissen, dass sie existiert:

db.riders.findOne({ email: "Odessa Loweantonettelarson@sanford.net"})

Dies ist eine direkte Suche und sollte sei schnell, zumal wir tatsächlich wissen, wonach wir in der Datenbank suchen. Die Ausführungsstatistiken zeigen jedoch etwas anderes:

  • Zurückgegebene Dokumente: 1
  • Geprüfte Indexschlüssel: 0
  • Geprüfte Dokumente: 1188100
  • Aktuelle Ausführungszeit der Abfrage: 667 ms

Beachten Sie, dass wir uns jedes Element in der Datenbank angesehen haben, was zu der 667 ms Abfragezeit. Das mag nicht nach einer langen Zeit erscheinen, aber datenbanktechnisch gesehen ist es eine Ewigkeit. Unsere Benutzer werden auf jeden Fall frustriert sein, wenn Dienste diese Abfrage verwenden müssen. Lassen Sie uns das Problem mit einem Einzelfeldindex beheben:

db.riders.createIndex( { email: 1 }, { background: true })

Wir erstellen einen Index für das E-Mail-Feld und zwar im Hintergrund, um den Betrieb der Produktionssysteme nicht zu stören (erinnern Sie sich daran, wie wichtig die Zeit unserer Benutzer ist?). Das Erstellen von Indizes im Hintergrund dauert länger, aber das Warten lohnt sich. Haben Sie auch bemerkt, dass wir die Index-Sortierrichtung als aufsteigend (aufsteigend) festgelegt haben:1? Dies ist bei Einzelfeldindizes nicht so wichtig, für zusammengesetzte Indizes jedoch von entscheidender Bedeutung. Nachdem wir den Index hinzugefügt haben, führen wir dieselbe Abfrage erneut aus:

db.riders.findOne({ email: "Odessa Loweantonettelarson@sanford.net"})
  • Zurückgegebene Dokumente: 1
  • Geprüfte Indexschlüssel: 1
  • Geprüfte Dokumente: 1
  • Tatsächliche Abfrageausführungszeit: 2 ms

Viel besser!

Eindeutige Indizes

Der Index, den wir gerade erstellt haben, wird nicht nur zur Leistung beitragen. Wenn wir den Index löschen und neu erstellen mit einzigartig Option, MongoDB verhindert das Einfügen von Dokumenten mit doppelten Feldern. Die meisten Indextypen in MongoDB können eindeutig sein. Zum Beispiel:

db.riders.createIndex( { email: 1 }, { background: true, unique: true })

Dies ist eine hervorragende Möglichkeit, Eindeutigkeitsprüfungen schnell durchzuführen, und bietet zusätzliche Schutzmaßnahmen gegen doppelte Daten, die zu schwer zu behebenden Fehlern oder Sicherheitslücken führen können.

Mehrschlüssel

Nachdem wir die Leistung dieser Abfrage verbessert haben, erhalten wir einen weiteren Bericht von unseren Benutzern: Abfragen, die Fahrten anzeigen, die mit einer bestimmten Autokennung für selbstfahrende Fahrzeuge unternommen wurden, dauern sehr lange, bis sie geladen sind. Die Abfrage sieht ungefähr wie folgt aus:

db.riders.findOne({ "rides.driverID": ObjectId('5dee0e1467adb71ef2362050') })

Die Abfrageleistung ist noch schlechter als in unserem ersten Beispiel, da Sie auch ein Array nach einem Wert durchsuchen müssen:

  • Zurückgegebene Dokumente: 1
  • Geprüfte Indexschlüssel: 0
  • Geprüfte Dokumente: 1188100
  • Tatsächliche Ausführungszeit der Abfrage: 1854 ms

Wir untersuchen noch einmal jeden Dokument in der Sammlung (ein „Sammlungsscan“), was ein Worst-Case-Szenario ist. Fügen wir dem DriverID-Feld im Rides-Array einen Index hinzu:

db.riders.createIndex( { 'rides.driverID': 1 }, { background: true })

Nachdem wir einen Einzelfeld-Mehrschlüsselindex erstellt haben, schauen wir uns an, wie unsere Leistung aussieht:

  • Zurückgegebene Dokumente: 1
  • Geprüfte Indexschlüssel: 1
  • Geprüfte Dokumente: 1
  • Tatsächliche Ausführungszeit der Abfrage: 1 ms

Viel schneller! Da wir einen Einzelfeldindex für ein Array-Element erstellt haben, hat MongoDB für jedes Element im Array einen Indexeintrag erstellt. Dies ist zum Teil der Grund, warum wir eine nahezu identische Leistung zwischen diesem und dem E-Mail senden abfrage. Multikey-Indizes weisen jedoch einige Nuancen auf, und die MongoDB-Dokumentation deckt alle Macken hervorragend ab.

Verbindung

Nachdem wir unsere gelöst haben E-Mail senden und Fahrer-ID Bei Anfragen stellen wir — wieder einmal — fest, dass wir eine schlecht funktionierende Abfrage haben: Ein Admin-Bereich der FloQars-App zeigt Support-Benutzern eine Ansicht der Fahrten, die innerhalb eines bestimmten Zeitraums unternommen wurden, alphabetisch nach E-Mail sortiert, sodass sie in der App einfacher angezeigt werden können. Die Abfrage sieht ungefähr so aus:

db.riders
    .find({
        "rides.date": {
            $lte: ISODate("12/01/2019"),
            $gte: ISODate("11/01/2019")
        }
    })
    .sort({ email: -1 });

Wie erwartet untersucht unsere Anfrage eine große Anzahl von Dokumenten, um sie zurückzugeben

  • Zurückgegebene Dokumente: 1068298
  • Geprüfte Indexschlüssel: 1188100
  • Geprüfte Dokumente: 1188100
  • Tatsächliche Ausführungszeit der Abfrage: 3901 ms

Hier bieten sich zusammengesetzte Indizes wirklich an. Sie haben sich vielleicht früher gedacht: „Aber was ist, wenn ich nicht etwas so Einfaches tun wie per E-Mail nachzuschlagen?“ Zusammengesetzte Indizes sind oft das, wonach Sie suchen würden. Sie ermöglichen die Erstellung von Indizes mit mehreren Schlüsseln in einer bestimmten Reihenfolge, um sowohl die Suche als auch die Sortierreihenfolge zu verbessern. Während Einzelfeldindizes von MongoDB in beide Richtungen durchquert werden können, müssen Sie sorgfältig über die Indexsortierreihenfolge für zusammengesetzte Indizes nachdenken. MongoDB erstellt einen zusammengesetzten Index unter Verwendung der von Ihnen angegebenen Reihenfolge und Sortierrichtung der Felder. Das bedeutet, dass Sie nicht nur über die Daten nachdenken sollten, auf die Sie zugreifen (z. B. „E-Mail“), sondern auch über das „Wann“ der Daten (wie weit muss MongoDB scannen, um zu einem Dokument zu gelangen?) und wie Sie diese Daten sortieren. In unserem Fall wollen wir so schnell wie möglich die E-Mail-Adressen der möglichen Benutzer eingrenzen, um zu vermeiden, dass alle Benutzer-E-Mails global sortiert werden. Wir können das benutzen rides.date Feld dafür und legen Sie eine absteigende Sortierrichtung fest. Wir werden auch eine aufsteigende Sortierreihenfolge für das E-Mail-Feld angeben, da unsere Support-Benutzer in Zukunft alphabetisch sortiert werden.

‍db.riders.createIndex({ 'rides.date': -1, email: 1 }, { background: true });

Huzzah! Die Felder unserer Dokumente arbeiten zusammen und die Dinge gehen viel schneller. Wir prüfen immer noch eine gute Anzahl von Dokumenten, aber sie liegen alle innerhalb des angegebenen Zeitraums, sodass wir angesichts unserer bereichsbasierten Abfrage möglicherweise nicht viel besser abschneiden können.

  • Zurückgegebene Dokumente: 6703
  • Geprüfte Indexschlüssel: 6795
  • Geprüfte Dokumente: 6771
  • Tatsächliche Ausführungszeit der Abfrage: 143 ms

Was kommt als Nächstes?

Jetzt haben Sie eine Handvoll Tools, um Ihre Abfragen in MongoDB mithilfe verschiedener Arten von Indizes zu verbessern. Hoffentlich decken diese die meisten Ihrer typischen Anwendungsfälle ab — das haben sie für uns bei FloQast getan. MongoDB verfügt auch über Indextypen zur Verarbeitung von Textdaten für Volltextsuchanwendungen und geografische Abfragen für standortbezogene Datentypen.