Unser täglich MySQL-Rant gib uns heute…
Momentan bin ich beruflich leider gezwungen, mich näher mit dem Galera-Cluster (eine Multi-Master-Synchronisationslösung auf MySQL/MariaDB-Basis) zu beschäftigen. Prinzipiell hätte ich natürlich eine ernsthafte Datenbankreplikationslösung auf PostgreSQL-Basis bevorzugt, aber leider benötigen nun gleich mehrere zukünftig in unserem Rechenzentrum eingesetzte Softwareprodukte eine hochverfügbare MySQL-Lösung.
Kleiner Einschub: wenn man die konkreten Hersteller eben dieser Softwareprodukte fragt, warum sie unbedingt MySQL genommen haben, kommt man gerne Antworten, die mit
“Wir sind zwar keine Datenbankexperten, aber…”
beginnen und irgend einem Gestammel der Bauart
“…hat halt funktioniert und wir bleiben dabei.”
endet. Warum wir überhaupt derartige Softwareprodukte auswählen, soll jetzt hier nicht das Thema sein…
Anyway, ich habe mich nun einige Zeit mit der offiziellen Galera-Doku und zahlreichen weiteren Dokumenten beschäftigt und sehe mein Bauchgefühl bestätigt: Der Cluster ist eine wirklich beeindruckende Implementationsleistung – basierend auf einem dafür völlig ungeeigneten Datenbanksystem.
Der Grund für diese böse Feststellung liegt vor allem in einer Altlast, die MySQL (und auch dessen Abkömmling MariaDB) immer noch mit sich herumschleppen: die MyISAM-Storage-Engine. Diese ehemalige Standardengine stammt aus einer Zeit, als MySQL noch keine Ambitionen hatte, ein echtes Datenbanksystem zu sein und lässt daher so ziemlich alle datenbankrelevanten Eigenschaften vermissen (Transaktionen, Fremdschlüssel, …). Schon vor Jahren wurde daher eine andere Engine namens InnoDB zur neuen Standardengine ernannt. Diese weist so ziemlich alle bei MyISAM fehlenden Eigenschaften auf und machte MySQL erst zu einer (nunja zumindest etwas) ernstzunehmenden Datenbank.
Und folgerichtig (genaugenommen, weil alles andere technisch deutlich aufwendiger wäre) unterstützt Galera-Cluster ausschließlich das Replizieren von InnoDB-Datenbanken.
So weit, so gut – gäbe es da nicht ein klitzekleines Detail am Rande: in jedem MySQL-Datenbanksystem gibt es immer noch mindestens eine MyISAM-Datenbank und zwar die MySQL-Systemdatenbank! Also die Datenbank, in der ein MySQL selbst speichert, welche Datenbanken mit welchen Tabellen und welchen Spalten es gibt, welche Nutzer mit welchen Passwörtern von wo zugreifen können, usw.
Hmm, das würde ja heißen, dass die Systemdatenbank bei Galera-Cluster nicht mitrepliziert werden würde, oder? Das kann doch nicht wirklich sein?
Nunja, doch!:
Currently replication works only with the InnoDB storage engine. Any writes to tables of other types, including system (mysql.*) tables are not replicated (this limitation excludes DDL statements such as CREATE USER, which implicitly modify the mysql.* tables — those are replicated).
Beispielszenario gefällig? Gegeben sei ein Cluster aus zwei Knoten; die Datenbanknutzer werden bei jeder neuen Verbindung per Load-Balancer auf einen der beiden geleitet. Der Nutzer ändert mittels UPDATE
in der Tabelle mysql.user
sein Passwort. Folge: sein Passwort wurde auf dem aktuellen Knoten geändert, aber leider wird der zweite Knoten davon nicht unterrichtet. Ab sofort hat der Nutzer also eine 50%-Chance, dass seine nächste Anmeldung am Server funktionieren wird.
Nun klingt es vielleicht abenteuerlich, sein Passwort direkt in den Systemtabellen zu ändern statt die (im Cluster auch replizierten) Higher-Level-SQL-Befehle (in diesem Fall ALTER USER) zu verwenden, aber etliche Datenbankadministrationswerkzeuge (auch die von uns selbst geschriebenen) gehen direkt über die Systemdatenbank. In unserem Fall werden wir uns wohl einige Workarounds ausdenken müssen…
Die oben verlinkte “Mängelliste” weist übrigens noch weitere Highlights auf. Der folgende Hack klingt noch halbwegs sympathisch:
If you are using mysqldump for state transfer, and it failed for whatever reason […], you will see an SQL SYNTAX error in the server error log. Don’t let it fool you, this is just a fancy way to deliver a message (the pseudo-statement inside of the bogus SQL will actually contain the error message).
Spannender finde ich die unglaublich konkreten Angaben, wie groß Transaktionen werden dürfen (man beachte die literarisch wertvolle Verwendung von “might”, “can” und “less fortunate scenario”):
Do not use transactions of any essential size. Just to insert 100K rows, the server might require additional 200-300 Mb. In a less fortunate scenario it can be 1.5 Gb for 500K rows, or 3.5 Gb for 1M rows…
Aber dieser Punkt endet noch besser:
…See MDEV-466 for some numbers (you’ll see that it’s closed, but it’s not closed because it was fixed).
Mein persönliches Highlight ist aber das Folgende:
Locking is lax when DDL is involved. For example, if your DML transaction uses a table, and a parallel DDL statement is started, in the normal MySQL setup it would have waited for the metadata lock, but in Galera context it will be executed right away. It happens even if you are running a single node, as long as you configured it as a cluster node…
Im Prinzip steht da nicht weniger als: Wenn du echtes ACID willst (ja, DML und DDL gleichzeitig sollte deterministisch möglich sein), kümmer dich selbst drum! Aber wenigstens werden die Konsequenzen bzw. die Unkenntnis selbiger nicht beschönigt:
…See also MDEV-468. This behavior might cause various side-effects, the consequences have not been investigated yet. Try to avoid such parallelism.
Hmm, eigentlich wollte ich diesen Artikel noch mit einem schmissigen Satz ala “ich werde dennoch hochmotiviert an die neue Herausforderung herantreten” beenden, aber der Freitagnachmittag ist schon zu weit fortgeschritten…
Also…. ja, ABER:
Es gibt wohl nicht viel Software, die während des laufenden Betriebs Änderungen am Datenbankschema vornehmen wird.
Dafür muss ich meine Software eh runterfahren und dann kann das nicht passieren.
Die Datenbanknutzer werden wir wohl eher selten anfassen und wir wollen auch nicht mit irgendwelchen DBMS-Tools im HIntergrund rumpfuschen.
Ich schätze, dass unsere Transaktionen so ca. 10 Zeilen beinhalten. Da bin ich also auch knapp unter 100K.
Kurz: Es ist vielleicht nicht schön, sollte aber für unser, durchaus verbreitetes, Nutzungsszenario funktionieren.
Mutige These. Beispielsweise besteht ein klassischer Ansatz, um in MySQL Materialized Views zu simulieren darin, echte Tabellen mit dem View-Inhalt anzulegen und beim Refresh diese zu droppen und sofort neu anzulegen. Wie sich ein SELECT mit einem gleichzeitigen DROP oder nicht abgeschlossenen CREATE verhält, möchte ich mir gar nicht vorstellen…
Natürlich wollen wir mit DBMS-Tools darauf arbeiten – nennt sich Datenbankadministration. Erklär mal einem Klempner, dass er ab heute ohne Rohrzange arbeiten soll…
Für dein Projekt gebe ich dir Recht. Sollten wir aber langfristig andere MySQL-Nutzer umziehen wollen, kann ich keine Garantie abgeben.
Am Anfang wird es funktionieren…
Schön, endlich mal wieder ein Blog-Post von Dir! 🙂
Das wird aber nicht besser hier 🙂 Aus dem Bauch heraus hätte ich das so wie Enno gesehen, aber wenn ihr zu solchen Mitteln greifen müsst… – herzliches Beileid.
Das klingt wirklich nach “Daumen drücken”. Ich kann mir schon denken, wer am Wochenende bei einem Problem das System reparieren darf. 😮