- Im letzten Jahr wurde intensiv daran gearbeitet, genau zu verstehen, wie sich Rails-Anwendungen mit SQLite performant und stabil betreiben lassen
- Dabei wurden verschiedene Erkenntnisse gewonnen, die hier geteilt werden sollen
- Es wird erklärt, woher die Probleme kommen und wie sie gelöst werden können
Probleme mit SQLite und Rails
- Standardmäßig ist eine Rails-Anwendung mit SQLite nicht sofort ohne Weiteres einsatzbereit
- Mit etwas Anpassung und Feintuning lässt sich jedoch eine performante und stabile Anwendung erstellen
- Für Rails 8 ist das Ziel, dass bereits die Standardeinstellungen produktionsreif sind
Demo-Anwendung "Lorem News"
- Zur Erläuterung von Problemen und Lösungen wird die Demo-Anwendung "Lorem News" verwendet
- Diese Anwendung ist ein Klon von Hacker News, in dem Nutzer Beiträge und Kommentare verfassen können
Performance-Tests
- Die Performance wird mit der
oha-Load-Test-CLI und einem Benchmark-Pfad innerhalb der Anwendung getestet
- Die Leistung wird sowohl mit einzelnen als auch mit gleichzeitigen Anfragen gemessen
Hauptproblem: SQLITE_BUSY-Ausnahmen
- SQLite verwendet Schreibsperren, um jeweils nur einen Schreibvorgang gleichzeitig zuzulassen
- Wenn mehrere Verbindungen gleichzeitig versuchen, die Schreibsperre zu erhalten, treten
SQLITE_BUSY-Ausnahmen auf
- Zur Lösung dieses Problems sollten Immediate Transactions verwendet werden
Immediate Transactions
- Standardmäßig verwendet SQLite den verzögerten Transaktionsmodus
- Mit Immediate Transactions wird sofort versucht, die Schreibsperre zu erhalten, und bei einem Fehlschlag kann ein erneuter Versuch erfolgen
- Mit dem
sqlite3-ruby-Gem lässt sich der Standard-Transaktionsmodus auf den Immediate-Modus setzen
Timeout-Einstellungen
- Über Timeout-Einstellungen in der Datei
database.yml lassen sich SQLITE_BUSY-Ausnahmen reduzieren
- Mit der
busy_timeout-Einstellung von SQLite können Wiederholungsversuche für Schreibsperren konfiguriert werden
GVL-Problem (Global VM Lock)
- Das
sqlite3-ruby-Gem gibt den GVL beim Aufruf des SQLite-C-Codes nicht frei
- Das verschlechtert die Parallelitäts-Performance
- Mit
busy_handler lässt sich der GVL freigeben und die Performance verbessern
Neuimplementierung von busy_timeout
busy_timeout wird neu implementiert, damit alle Abfragen mit derselben Frequenz erneut versucht werden
- Dadurch wird verhindert, dass ältere Abfragen durch ein Timeout fehlschlagen
Performance-Verbesserungen
- Zur Verbesserung der Performance sollten folgende Einstellungen angewendet werden
- Immediate Transactions verwenden
- Timeout konfigurieren
busy_handler verwenden
- WAL-Modus (Write-Ahead Logging) verwenden
- Getrennte Verbindungspools für Lese- und Schreibzugriffe verwenden
Zusammenfassung von GN⁺
- Es geht um Performance-Probleme von Rails-Anwendungen mit SQLite und deren Lösungen
- Mit Methoden wie Immediate Transactions, Timeout-Konfiguration, Freigabe des GVL, WAL-Modus und getrennten Lese-/Schreib-Verbindungspools lässt sich die Performance verbessern
- Der Artikel ist sehr nützlich für Entwickler, die SQLite und Rails verwenden
- Als andere Projekte mit ähnlichen Funktionen werden PostgreSQL und MySQL empfohlen
1 Kommentare
Hacker-News-Kommentare
Vorstellung von Oldmoes Litestack-Projekt
Dank für den ausführlichen Artikel
Empfehlung für alle, die mit SQLite arbeiten
Frage zu einem FOSS-Analytics-System
GVL-Problem des sqlite3-ruby-Gems
Setup für einen persönlichen Webservice
PRAGMA journal_mode = WALPRAGMA busy_timeout = 5000PRAGMA synchronous = NORMALPRAGMA cache_size = 1000000000PRAGMA foreign_keys = truePRAGMA temp_store = memoryBEGIN IMMEDIATE-TransaktionenFrage zu Django
Frage zur Standardeinstellung von
busy_timeoutbusy_timeouteine Verzögerung hat, die ältere Queries benachteiligtMeinung zur Verwendung von SQLite und Rails
Dank für das Lösen von Rails-Integrationsproblemen