- Betrieb eines NoSQL-DB-Clusters (ScyllaDB), der 2 Millionen Nachrichten pro Sekunde verarbeitet
- Den größten Einfluss auf die DB-Performance hat die Latenz der physischen Festplattenhardware
→ Bei geringem Query-Aufkommen spielt das keine große Rolle, aber ab einem bestimmten Punkt führen schon 1–2 ms Lesezeit zu Warteschlangen beim Lesen von der Platte und schließlich zu Timeouts bei den Queries selbst
- Die Festplattenlatenz liegt normalerweise im Mikrosekundenbereich – warum dauern Disk-Operationen dann 1–2 ms?
- Discord betreibt den Großteil seiner Hardware auf Google Cloud
- Lokale SSDs auf NVMe-Basis werden unterstützt, aber interne Tests zeigten Stabilitätsprobleme, weshalb sie sich für wichtige Datenspeicher nicht gut anfühlten
- Persistent Disk kann in Echtzeit an Server angehängt und von ihnen getrennt werden, lässt sich ohne Downtime vergrößern, ermöglicht jederzeit Snapshots und ist standardmäßig auf Replikation ausgelegt
→ Das Problem ist, dass sie nicht direkt am Server hängt, sondern über das Netzwerk angebunden ist
- Selbst wenn die Latenz einer lokalen Netzwerkverbindung sehr niedrig ist, kommt sie nicht unter PCI/SATA
→ Netzwerk: 1–2 ms, direkt angeschlossene Festplatten: 0,5 ms
- Bei lokalen SSDs kann man wie bei HDDs die Daten auf dieser Platte verlieren, wenn ein Hardwareproblem auftritt; fällt der Host selbst aus, sind nicht einmal Snapshots möglich, sodass die Daten vollständig verloren gehen können
→ Deshalb verwendet Discord keine Local SSDs, sondern Persistent Disk
Problemanalyse
- Am besten wäre ein Speichermedium, das nur die Vorteile von lokaler SSD und Persistent Disk vereint – so etwas gibt es aber nicht. Was ist, wenn man zumindest einige Vorteile kombiniert?
- Für Discord ist die Schreib-Latenz nicht das Problem. Leistungskritisch ist die „Lese-Latenz“
- „Festplattenvergrößerung ohne Downtime“ ist keine zwingende Funktion. Die Größe lässt sich im Voraus abschätzen
- Die endgültigen Anforderungen waren
- Weiterhin auf GCP bleiben
- Point-in-Time-Snapshots für Datensicherung nutzen
- Minimierung der Lese-Latenz als oberste Priorität
- Dabei nicht die bestehende Datenbank-Uptime opfern
- Ideal wäre es, für Lesezugriffe die Local SSDs von GCP und für Schreibzugriffe Persistent Disk zu verwenden
→ Lässt sich auf Software-Ebene so eine Super-Disk bauen?
Bau einer Super-Disk
- Die Anforderungen liefen im Grunde auf einen Write-Through-Cache hinaus. Die lokalen SSDs von GCP sollten als Cache dienen, PD als Storage-Layer
- Da auf den DB-Servern Ubuntu verwendet wird, ließ sich Caching auf Festplattenebene im Linux-Kernel anwenden (Module wie
dm-cache, lvm-cache, bcache)
- Experimente zeigten jedoch, dass bei Bad Sectors auf der Cache-Disk der gesamte Lesevorgang fehlschlägt
- Bei Bad Sectors müsste aus dem Storage-Layer gelesen und darübergeschrieben werden, aber keines der evaluierten Disk-Caching-Systeme bot diese Funktion
- Bei Bad Sectors schaltete sich die Datenbank wegen Problemen mit der Datenintegrität ab
- Dadurch kam als zusätzliche Anforderung hinzu: „Das System muss auch bei Bad Sectors auf der lokalen SSD weiterleben“
- Deshalb wurde das Linux-Kernel-Subsystem
md untersucht
md unterstützt die Erstellung von Software-RAID
- Einfach SSD und PD zu spiegeln löst das Problem nicht, weil dann mehr als die Hälfte der Lesezugriffe von der PD kämen
md besitzt mit write-mostly eine Funktion, die es in klassischem RAID nicht gibt
- Wenn eine bestimmte Platte als
write-mostly markiert wird, wird sie bei normalen Lesezugriffen ausgeschlossen und nur genutzt, wenn es keine andere Option gibt. „Nützlich für langsam angebundene Geräte“
- Das heißt: Wenn SSD und PD als RAID1 zusammengefasst und die PD auf
write-mostly gesetzt wird, lassen sich die Anforderungen erfüllen
- Das letzte verbleibende Problem war, dass die Local SSDs von GCP exakt 375 GB groß sind
- Für bestimmte Anwendungen braucht Discord jedoch mehr als 1 TB pro DB-Instanz
- Deshalb wurden mehrere SSDs per RAID0 zusammengefasst
- Das finale Setup war
- vier lokale SSDs als RAID0, konfiguriert als
md0
md0 und Persistent Disk wiederum als RAID1 zusammengefasst, konfiguriert als md1
DB-Performance
- Das Ergebnis entsprach genau den Erwartungen
- Selbst unter Spitzenlast stauen sich Disk-Operationen nicht in Warteschlangen, und die Query-Latenz verändert sich nicht
- Die Performance stieg, sodass jeder Server mehr Queries verarbeiten konnte
- Wer schon einmal RAID eingesetzt hat, fragt sich vielleicht: „Kann das wirklich einfach so funktionieren?“ Tatsächlich gab es allerlei praktische Herausforderungen, die der Rest des Artikels separat im Detail behandeln soll
3 Kommentare
Früher war man offenbar mit der Performance von Golang nicht zufrieden, und wenn man dann sieht, dass sie sogar Server in Rust schreiben, wirkt die Geekiness des Unternehmens Discord schon ziemlich beeindruckend.
Es fühlt sich an, als würde man etwas, das mit Schleim verstopft werden könnte, mit einer Hacke verhindern wollen.
Auf HN gibt es zwar auch Stimmen, die sagen: Ist das nicht einfach nur ein GCP-Problem?..
https://news.ycombinator.com/item?id=32474093
Aber ich denke, es ist trotzdem gut, so etwas als einen möglichen Ansatz im Hinterkopf zu behalten.