32 Punkte von xguru 2022-05-16 | 6 Kommentare | Auf WhatsApp teilen
  • Ben Johnson, der Entwickler von BoltDB (einer eingebetteten Key-Value-DB), entwickelt jetzt bei Fly.io Litestream
  • Die vernünftige Struktur einer Full-Stack-Anwendung ist n-Tier: App-Server + DB-Server
    → In dieser Architektur wurde SQLite bisher nur für Unit-Tests verwendet, kann inzwischen aber durchaus als Daten- und Persistenzschicht eingesetzt werden
  • Litestream ist Open Source und macht SQLite per Replikation in Full-Stack-Anwendungen nutzbar

Eine kurze Geschichte der Anwendungsdatenbanken

  • 50 Jahre sind keine lange Zeit, aber die Art, wie Software Daten verwaltet, hat sich enorm verändert
    → In den 1970ern gab es „Codds Regeln“, die relationale Datenbanken definierten
    → Alle Daten liegen in Tabellen, dazu CRUD, Schema, die Sprache SQL usw.
    → In den 1980ern und 1990ern gab es eine Explosion von SQL-Datenbanken wie Oracle/DB2/Postgres/MySQL
    → Die XML-Datenbanken der 2000er waren nicht besonders gut, während gleichzeitig hervorragende Column Stores entstanden
    → In den 2010ern wurden große verteilte Open-Source-DB-Projekte veröffentlicht, und heute kann praktisch jeder einen Cluster bauen und Daten im Terabyte-Bereich abfragen

  • Mit der Weiterentwicklung der Datenbanken entwickelten sich auch die Strategien, DBs mit Anwendungen zu verbinden
    → Seit Codd wurden sie in Tiers aufgeteilt
    → Zuerst gab es die Datenbank-Tier
    → Dann die Caching-Tier mit memcached und Redis
    → Dazu kamen Background-Job-Tier (Sidekiq), Routing-Tier (PgBouncer), Distribution-Tier usw.
    → Viele Tutorials sprechen von 3-Tier, aber weil man nie weiß, wie viele Schichten dazukommen, nennt man es hier „n-Tier“

  • In diesen 50 Jahren wurden CPU, Speicher und Festplatten um ein Vielfaches schneller und günstiger
    → Der Begriff, der die Datenbank-Innovationen der 2010er tatsächlich prägte, ist „Big Data“
    → Durch die Hardware-Verbesserungen war dieses Konzept 2020 aber kaum noch aufrechtzuerhalten
    → 1996 war das Verwalten einer 1-GB-Datenbank eine große Sache, 2022 kann man sie problemlos auf einem Laptop oder sogar auf einer t3.micro betreiben

  • Wenn wir über neue DB-Architekturen nachdenken, stehen wir oft unter dem Hypnoseeffekt der Skalierungsgrenzen
    → Wenn ein System keine Petabytes oder wenigstens Terabytes verarbeiten kann, wird es gar nicht ernst genommen
    → Aber die meisten Anwendungen sehen selbst bei Erfolg nie Terabytes an Daten
    → Wir verwenden also Presslufthämmer, um Nägel einzuschlagen

Die süße Versuchung von SQLite

  • Es gibt eine Datenbank, die diesen Trend gut widerspiegelt
  • Sie ist eine der bekanntesten SQL-Datenbanken der Welt, offizielles Archivformat der Library of Congress, berühmt für ihre Zuverlässigkeit, eine kaum zu ermessende Testsuite und hervorragende Performance
  • Eigentlich muss man den Namen gar nicht mehr nennen, aber für die Person dort hinten mit der erhobenen Hand: Es geht um SQLite
  • SQLite ist eine eingebettete DB. In der üblichen Architektur-Tier existiert sie gar nicht, sondern ist einfach eine Bibliothek, die in den Prozess des Anwendungsservers gelinkt wird
    → Eine „Single-Process-Anwendung“, die ohne Abhängigkeit von einem anderen Server alleine läuft
  • Weil ich selbst Datenbanken baue, interessiere ich mich für diese Art von Anwendungen
  • Ich habe BoltDB entwickelt, eine im Go-Ökosystem bekannte eingebettete Key/Value-DB
  • BoltDB ist stabil und liefert, wie man es von einer In-Process-DB erwartet, eine Performance wie ein Spielzeugauto mit Nitro
  • Aber BoltDB hat Einschränkungen
    → Das Schema wird im Go-Code definiert, daher sind DB-Migrationen schwierig. Die nötigen Werkzeuge muss man selbst bauen. Es gibt nicht einmal ein REPL
  • Wenn man vorsichtig ist, kann man mit dieser Art von DB enorme Performance erzielen
  • Für allgemeine Zwecke möchte man so eine DB aber meist nicht betreiben
  • Ich habe darüber nachgedacht, was nötig wäre, um BoltDB in mehr Anwendungen nutzbar zu machen, und mein Fazit war: „Genau dafür wurde SQLite gebaut“
  • Natürlich hat auch SQLite Einschränkungen. Die größte ist, dass eine Single-Process-Anwendung einen SPOF (Single Point of Failure) hat: Verliert man den Server, verliert man auch die Datenbank. Das ist kein Fehler von SQLite, sondern so beabsichtigt

Enter Litestream

  • Es gibt zwei große Gründe, warum viele SQLite nicht standardmäßig einsetzen
    → Erstens Resilienz gegenüber Storage-Fehlern
    → Zweitens Concurrency bei größerem Maßstab
  • Zu beiden Problemen hat Litestream etwas zu sagen
  • Litestream funktioniert, indem es den Journal-Mechanismus des WAL-Modus (Write Ahead Log) von SQLite steuert
  • Im WAL-Modus werden Schreiboperationen in eine separate Log-Datei geschrieben statt direkt in die Haupt-DB-Datei von SQLite
  • Reader prüfen sowohl die WAL-Datei als auch die Haupt-DB, um Queries zu beantworten
  • Normalerweise führt SQLite automatisch Checkpoints aus dem WAL in die Haupt-DB aus
  • Litestream greift in diesem Zwischenschritt ein, verhindert den automatischen Checkpoint durch eine unendliche Read-Transaktion, erfasst und repliziert WAL-Updates selbst und löst den Checkpoint eigenständig aus

    Das Wichtigste, was man über Litestream verstehen muss, ist: Es ist einfach SQLite. Die Anwendung verwendet Standard-SQLite; es werden keine zusätzlichen Abhängigkeiten eingeführt, keine Queries analysiert und kein Proxy-Verhalten erzwungen. Es nutzt schlicht die vorhandenen Journal- und Concurrency-Funktionen von SQLite. In den meisten Fällen merkt dein Code womöglich gar nicht, dass Litestream da ist

  • Das klingt komplex, ist in der Praxis aber extrem einfach. Wenn man es ausprobiert, merkt man: Es „just works“
    $ litestream replicate fruits.db s3://my-bukkit:9000/fruits.db
    $ litestream restore -o fruits-replica.db s3://my-bukkit:9000/fruits.db
  • In der Regel verwenden Menschen es, um SQLite-DBs zu replizieren und in S3 zu speichern
  • Das bringt große operative Vorteile. Die DB wird resilient und lässt sich leicht verschieben oder migrieren
  • Mit Litestream lässt sich aber noch mehr machen
  • In der nächsten Version wird Echtzeit-Replikation zwischen SQLite-DBs möglich, sodass sich verteilte Read Replicas und eine Write-Leader-DB aufsetzen lassen
    → Read Replicas können Writes abfangen und an den Leader weiterleiten
    → Viele Anwendungen sind read-heavy, und dieses Setup kann ihnen daher eine global skalierbare DB liefern

Diese Option – SQLite als Anwendungsdatenbank zu verwenden – solltest du ernster nehmen

  • Einer meiner ersten IT-Jobs war Anfang der 2000er als Oracle-DBA
  • Ich habe viel Zeit damit verbracht, unzählige Bücher und Dokumente zu lesen, um Oracle zu verstehen
  • Allein das Admin-Handbuch hatte fast tausend Seiten, und das war nur eines von Hunderten Dokumenten
  • Damals machte es einen großen Unterschied, wenn man lernte, wie man Queries optimiert oder Writes verbessert
  • Festplatten lasen nur einige Dutzend Megabyte pro Sekunde, also konnte ein besserer Index eine 5-Minuten-Query in eine 30-Sekunden-Query verwandeln
  • Aber DB-Optimierung wird für normale Anwendungen immer weniger wichtig
  • Mit einer 1-GB-Datenbank kann eine NVMe-Disk heute alles in weniger als einer Sekunde in den Speicher laden
  • Ich liebe SQL-Query-Optimierung, aber für viele Anwendungsentwickler wird sie zu einer aussterbenden Kunst
  • Selbst schlecht getunte Queries laufen auf vielen Datenbanken in unter einer Sekunde
  • Modernes Postgres ist ein Wunder. Ich habe über Jahre beim Lesen des Codes unglaublich viel gelernt
  • Query-Optimizer, Row-Level-Security-Policies, sechs verschiedene Index-Typen und mehr
  • Wenn man diese Features braucht, sind sie wichtig – aber die meisten brauchen sie nicht
  • Und wenn man diese Postgres-Features nicht braucht, kommt trotzdem Verantwortung mit
  • Selbst wenn man keine mehreren Accounts verwendet, muss man hostbasierte Authentifizierung konfigurieren und die Firewall öffnen
  • Mehr Features bedeuten mehr Dokumentation, was es schwieriger macht, die Software, die man tatsächlich betreibt, vollständig zu verstehen
  • Die Dokumentation von Postgre14 hat fast 3.000 Seiten
  • SQLite ist ein Subset der Postgres-Features. Aber für mich deckt es normalerweise 99,9 % von dem ab, was ich brauche
  • Hervorragende SQL-Unterstützung, Window Functions, CTEs, Volltextsuche, JSON-Unterstützung usw.
  • Falls doch einmal Funktionalität fehlt, ist der Overhead gering, weil die Daten direkt neben der Anwendung liegen und sich leicht holen und verarbeiten lassen
  • Die wirklich komplexen Probleme, die wir lösen müssen, werden ohnehin nicht durch Kernfunktionen der Datenbank gelöst
  • Stattdessen möchte ich vor allem zwei Dinge optimieren: Latenz und Developer Experience
  • Ein wichtiger Grund, SQLite ernsthaft zu erwägen, ist daher die enorme Einfachheit im Betrieb
  • Man kann sich die Zeit für das Design einer Datenbankschicht sparen und sie stattdessen direkt in Anwendungscode investieren
  • Aber es gibt noch ein anderes Problem

Licht ist viel zu langsam: The Light is Too Damn Slow

  • Wir stoßen an theoretische Grenzen. Im Vakuum legt Licht in 1 Millisekunde 186 Meilen zurück – etwa die Hin- und Rückstrecke zwischen Philadelphia und New York
  • Fügt man Network Switches, Firewalls und Schichten von Anwendungsprotokollen hinzu, wird es noch langsamer
  • Selbst innerhalb einer einzelnen AWS-Region liegt der zusätzliche Latenz-Overhead für eine Postgres-Query bei bis zu 1 Millisekunde
  • Das bedeutet nicht, dass Postgres langsam ist, sondern dass wir an die Grenzen der Datentransportgeschwindigkeit stoßen
  • Moderne Anwendungen verarbeiten HTTP-Requests und haben oft schon 10 ms verbraucht, bevor mehrere Datenbank-Queries sowie Business-Logik oder Rendering überhaupt abgeschlossen sind
  • Für Anwendungslatenz gibt es eine magische Zahl: Antworten unter 100 ms fühlen sich nahezu sofort an
  • Direkt reagierende („snappy“) Anwendungen machen Nutzer glücklich
  • 100 ms klingen nach viel, sind aber erstaunlich leicht aufgebraucht
  • Weil diese 100-ms-Schwelle so wichtig ist, prerendern Menschen Seiten und legen sie auf ein CDN, um Latenz zu senken
  • Wir sollten Daten näher an die Anwendung bringen. Wie nah? Wirklich sehr nah
  • SQLite liegt nicht nur auf derselben Maschine wie die Anwendung, sondern direkt im Prozess der Anwendung
  • Wenn die Daten direkt neben der Anwendung liegen, kann die Latenz pro Query auf 10–20 Mikrosekunden (μ) fallen
  • Das ist 50- bis 100-mal schneller als eine Postgres-Query in derselben Region
  • Aber das ist noch nicht alles. Wir haben die Latenz pro Query praktisch eliminiert. Unsere Anwendung wird dadurch nicht nur schneller, sondern auch einfacher
  • Große Queries lassen sich in kleinere, besser handhabbare Queries aufteilen, und man kann mehr Zeit darauf verwenden, neue Features zu bauen, statt das N+1-Query-Pattern zu fürchten
  • Minimale Latenz ist nicht nur für Production wichtig. Integrationstests mit klassischen Client/Server-DBs werden lokal schnell zu einer Sache von mehreren Minuten, und auch in CI bleibt der Schmerz bestehen
  • Wenn man die Feedback-Schleife zwischen Codeänderung und abgeschlossenem Test verkürzt, spart man Zeit und bleibt beim Entwickeln fokussiert
  • Eine einzeilige Änderung in SQLite lässt sich im Speicher ausführen, sodass Integrationstests in wenigen Sekunden laufen können

Klein, schnell, zuverlässig, global verteilt: Wähle vier davon

  • Litestream ist verteilt, repliziert und vor allem leicht zu verstehen
  • Im Ernst: „Probier es einfach aus“ – es gibt nicht viel, was man dafür wissen muss
  • Meine These ist diese:
    • Wenn man für SQLite eine stabile und einfach zu nutzende Replikation baut, werden Full-Stack-Anwendungen, die vollständig auf SQLite laufen, attraktiv
    • Als früher die Tutorials „Einen Blog mit Rails bauen“ geschrieben wurden, wurde diese Option übersehen. Das heutige SQLite kann aber die Write-Last der meisten Anwendungen tragen, und über Replikate lassen sich Reads über viele Instanzen load-balancen
  • Auch Litestream hat Einschränkungen
    • Weil es für Single-Node-Anwendungen gebaut wurde, funktioniert es nicht gut auf Serverless-Plattformen oder bei Rolling Deployments
    • Da alle Änderungen sequentiell wiederhergestellt werden müssen, kann ein DB-Restore einige Minuten dauern
    • Wir arbeiten an Echtzeit-Replikation, aber das Modell mit separatem Prozess hat Einschränkungen bei der feingranularen Kontrolle über Replikationsgarantien
  • Wir können es besser machen
    • Im vergangenen Jahr bestand meine Arbeit darin, den Kern von Litestream festzulegen und mich auf Korrektheit zu konzentrieren
    • Ich bin zufrieden mit dem Punkt, an dem wir jetzt angekommen sind
    • Es begann als einfaches Streaming-Backup-Tool, entwickelt sich aber Schritt für Schritt zu einer stabilen, verteilten Datenbank
    • Meine Arbeit bei Fly.io besteht darin, das schneller und nahtloser zu machen
    • Unabhängig von Fly.io wird Litestream noch viele weitere Verbesserungen bekommen
  • Litestream hat bei Fly.io ein neues Zuhause gefunden, bleibt aber weiterhin ein Open-Source-Projekt
  • Mein Plan für die kommenden Jahre ist, es nützlicher zu machen – unabhängig davon, wo Anwendungen laufen – und herauszufinden, wie weit sich das SQLite-Modell treiben lässt

6 Kommentare

 
nicewook 2022-09-27

Ich habe jetzt wieder Lust bekommen, es noch einmal gründlich zu lesen.

 
johngrib 2022-05-17

So ähnlich habe ich auch schon einmal gedacht, aber das hier ist deutlich gründlicher und ernsthafter. Ich war beim Lesen wirklich beeindruckt. Litestream würde ich auch gern einmal ausprobieren.

 
japansea 2022-05-16

Noch besser wäre es, wenn man Abfragen remote ausführen könnte ... schluchz

 
mrchypark 2022-05-16

Das erinnert mich an den Moment, in dem Elixir aufkam. Es ist ein Werkzeug, das eingebettete verteilte Datenbanken und Orchestrierung auf Sprachebene bereitstellt, aber ich bin mir nicht sicher, ob das wirklich die Zukunft ist.

 
nicewook 2022-05-16

Das habe ich mit Vergnügen gelesen!

 
xguru 2022-05-16

Ich wollte das eigentlich nur kurz lesen und zusammenfassen, aber es hat beim Schreiben so viel Spaß gemacht, dass es doch länger geworden ist.

Litestream - Streaming-Replikationstool für SQLite

Es dürfte hilfreich sein, das zusammen mit der Frage Hat jemand SQLite als Primary DB verwendet? zu lesen.

Es scheint auch einen Zusammenhang mit Cloudflare stellt D1 vor, eine SQL-Datenbank für Workers zu geben, die vor ein paar Tagen veröffentlicht wurde.

Werft auch einen Blick auf die Kommentare bei HN: https://news.ycombinator.com/item?id=31318708