Linux – mysqldump sieht Leerzeichen als leeren Datenbanknamen an

Erinnert Ihr Euch noch an diesen Beitrag : Solution – mysqldump – No database selected when selecting the database aus dem Jahr 2016 ?

MariaDB & mysqldump

Damals stolperte ich über einen Fehler im Kommandozeilenparsing von Mysqldump.

Wie sich herausgestellt hat, konnte der Betreuer Michael Scholm bei Redhat die Ursache für das Problem entdecken. Kommt Ihr nie drauf: Das  war Schuld ! Oh ?! Ihr habt es nicht gesehen, hier nochmal  . Vielleicht mit der Lupe, da: > <. Ein Space.  Hier … zwischen dem „usr/bin/mysqldump“ und dem „–addlocks“ ist ein “ “ zuviel drin:

/usr/bin/mysqldump  --add-locks -e --force -R ....

Da es keine Option ist, wird es tatsächlich als valider Versuch, einen Datenbanknamen anzugeben gewertet.IMHO, ein Parser-Logik-Bug. Redhat sieht das anders: „not a bug“. Da jetzt raus ist, was es verursacht, kann man es ja leicht beheben.

etwas Kontext

Die Parser für Linux sind, sagen wir mal, ein bisschen eigen und deren Interpretation auch. Als Trenner zweier Argumente einer Kommandozeile gilt ein freistehendes Leerzeichen. Die Bash-Shell entfernt doppelte Leerzeichen und setzte ein Leerzeichen an die Stelle. So ein Leerzeichen zu viel ist schnell getippt und würde zu vielen „Dicke Finger-Syndrom“-Problemen führen, weil ..

… Ja, weil der Parser ohne diesen Trick, zwei Leerzeichen in Folge so übersetzen :

ls -la  foo => ls -la "" foo

Weil „Leerzeichen“ sind Trenner, also steht da oben eigentlich: „ls{Trenner}-la{Trenner}{Trenner}foo .

Wenn man aber zwischen den Trennern nichts hat, dann kommt ein leeres Argument raus. Was ein leeres Argument bedeutet, zeigt sich hier im Beispiel:

[marius ~]$ ls -la "" foo
ls: Zugriff auf '' nicht möglich: No such file or directory
ls: Zugriff auf 'foo' nicht möglich: No such file or directory

Wie man sehen kann versucht ls tatsächlich auf aka. „“ zuzugreifen. Wie das gehen soll ? Gar nicht. Ist ein Logikbug im Kontext von ls . Es wiederspricht der Natur eines ls , etwas aufzulisten, das es nicht gibt. Ergo müßte das Argument ignoriert werden. Jeder Mensch würde das so machen. Computerprogramme sind in der Beziehung blöde. Deswegen gibt es die Bash, die das Doppel-Leerzeichen wegnormaliziert und so vorhersehbare Fehler vermeidet, weil ..

Fehler vermeiden

wie man im obigen ls Beispiel gesehen hat, kann man es ja explizit angeben, wenn man muß => „“ oder “

MariaDB zieht sich jetzt auf den Standpunkt zurück, daß mysqldump sich ja nur wie ls verhalten würde. Tut es natürlich nicht, weil mysqldump komplett failed, wo ls einfach weitermacht und einen Fehler ausgibt.  Naja, Nobodys perfect.

Update

… oh, Redhat hat die Meinung geändert und einen Featurerequest bei MariaDB für einen besseren Parser erstellt. \o/ Das gibt einen dicken Daumen rauf! Mal sehen wies bei MariaDB ausgeht.

Wie man die TMP Ramdisk entfernt

Auf normalen Desktopsystemen ist es eine gute Sache, wenn der /tmp/ Ordner im RAM liegt. Auf /tmp/ wird sehr häufig und meistens eher kleinteilig zugegriffen, so das man diese Zugriffe  am besten von der Festplatte oder der SSD fern hält. Auf einem Server kann das aber auch von Nachteil sein.

[root@server ~]# df -h
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
devtmpfs        5,0G       0  5,0G    0% /dev
tmpfs           5,0G       0  5,0G    0% /dev/shm
tmpfs           5,0G    532K  5,0G    1% /run
tmpfs           5,0G       0  5,0G    0% /sys/fs/cgroup
/dev/xvda1      245G    178G   56G   77% /
tmpfs           5,0G     38M  5,0G    1% /tmp
tmpfs          1012M       0 1012M    0% /run/user/0

Im obigen Beispiel von einem unserer Server, kann man sehen, daß für die „tmpfs“ Laufwerke 5 GB maximale Größe angegeben ist. DEV, RUN, SYS werden das niemals erreichen, die sind eher im KB Bereich angesiedelt. Über die Sinnhaftigkeit, dann 5 GB als MAX Größe zu nehmen, kann man sicher streiten. Ist aber für die Betrachtung egal, denn es handelt sich um eine dynamische Speicherbelegung, deswegen auch „maximale Größe“. In Real sind die genau so groß, wie die Daten darin das brauchen. Lege ich dort 1 MB ab, ist es 1 MB und ein bisschen was für die Verwaltung groß.

An der „Verwendung“ in Prozent bzw. „Benutzt“ kann man auch sehen, das oben keins der TmpFS Ramdrives übermäßig belegt war. Die Ramdrives haben also bei dem Stand zusammen grade mal 39 MB echten Speicher belegt.

So weit, so gut.

Das obige Serversystem hat 10 GB Speicher zur Verfügung, was es üblicherweise auch braucht. d.h. es sind permanent mehrere GB an RAM in realer Benutzung.

Datenbankserver wie MariaDB erlauben es den Benutzern bei Abfragen sogenannte TEMP-Tables zu erstellen. Das wird vorzugsweise im RAM gemacht. Wenn aber das RAM nicht reicht, weil jemand einen TEMP-Table zusammen baut, der mehrere GB groß ist, dann wird das in den /tmp/ Ordner ausgelagert. Und man glaubt gar nicht wie unsensible mache Anwendungsentwickler im Umgang mit solchen Temp-Tables sind. „Killer SQL-Anweisungen“ in Shops, die „ein bisschen mehr und schneller“  gewachsen sind, als die Hersteller das erwartet haben, sind keine Seltenheit. Schlechtes Datenbankdesign sowieso nicht 😉  Und damit fängt der Ärger dann üblicherweise auch an.

Was bei einem Killer-SQL passieren kann …

Der Hauptspeicher des Datenbankserver hatte schon nicht ausgereicht um den Temp-Table anzulegen, und über die Ramdisk wird jetzt versucht den Speicher zusätzlich nochmal zu belegen, der vorher schon nicht ausreichend da war. Der Kernel wird jetzt versuchen diese Datenmengen zu swappen und kann das vielleicht nicht, weil die SWAP Partition zu klein ist. Nun kommt es zum „OOM“ dem Out-of-Memory-Error. d.h. der Kernel fängt an, scheinbar wahllos Prozesse zu killen, die viel Speicher belegen, aber noch nicht lange laufen. Eine genauere Analyse nimmt der Kernel leider nicht vor.

Wie kommt man jetzt aus der Falle wieder raus ?

Verantwortlich für das Erzeugen der Ramdisk ist diese Systemd Unit : /usr/lib/systemd/system/tmp.mount

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Temporary Directory
Documentation=man:hier(7)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
ConditionPathIsSymbolicLink=!/tmp
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target

[Mount]
What=tmpfs
Where=/tmp
Type=tmpfs
Options=mode=1777,strictatime

Die kann man mit einem kurzen Befehl an den Systemd abschalten, allerdings erst ab dem nächsten Bootvorgang:

# systemctl mask tmp.mount
Created symlink from /etc/systemd/system/tmp.mount to /dev/null.
# ls -la /etc/systemd/system/tmp.mount
lrwxrwxrwx 1 root root 9 14. Nov 11:45 /etc/systemd/system/tmp.mount -> /dev/null

Danach muß man das also Rebooten. Am Ende ist /tmp/ dann wieder ein normaler Ordner auf der Festplatte, der keiner Größenbeschränkung unterliegt und in dem der Datenbankserver dann auch wieder fast beliebig große Temp-Tables erzeugen kann, ohne das gleich ein unschuldiger Prozess dran glauben muß.

 

Wie man PHPMyAdmin beschleunigen kann

Habt Ihr auch einen „modernen“ PHPMyAdmin installiert, der bei einigen Anzeigen gefühlte Äonen braucht, um den banalen Inhalt der Datenbank anzuzeigen ?

Ja ? Habt Ihr auch schon gedacht, daß es an der DNS Auflösung liegen könnte ?
Ja ? und es hat so gar nichts gebracht ein schnelleres Cache  einzustellen ?
Ja ? Na dann schaltet doch mal auf ENGLISCH als PMA Sprache um, Ihr werdet Euch wundern !

Es ist einen :facepalm: wert, denn für jede popplige Seite, sei es per AJAX oder per URL Link, lädt PHP die komplette, wirklich MB große Übersetzung, wenn man Deutsch eingestellt hat. Da hilft auch kein Filecache mehr um das zu beschleunigen!

Wie man sowas findet

Sehen tut man das erst, wenn man mit STRACE den Apachen und die ausgeführten PHP Programme traced.

strace -e open,read -f -p `pidof httpd | sed -e "s/ /,/g"`

Es folgt so etwas, in einer schieren Unzahl von Zugriffen, weil PHP echt groß ist und ne Menge einlesen muß.

[pid 10052] open(„/usr/lib/php/modules/intl.so“, O_RDONLY|O_CLOEXEC) = 3
[pid 10052] read(3, „…………………………….jede Menge Text ………………“, 512) = 512

Das oben ist keiner der verdächtigen Zugriffe, zeigt aber, wie man die READs den Dateien zuordnet. Das Orangerote ist der FILEDESCRIPTOR, kurz FD.  Jede Datei die man öffnet als Programm, bekommt einen eindeutigen FD, damit man das als Programm zuordnen kann.

Typisch für PHP und die Übersetzungen sind diese Zeilen :

[pid  9436] lseek(4, 93205, SEEK_SET)   = 93205
[pid  9436] lseek(4, 93205, SEEK_SET)   = 93205
[pid  9436] read(4, „Database %s has been dropped.\0Database Log\0Database client version:\0Database comment\0Database for user account\0Database level tabs\0Database name\0Database name template\0Database operations\0Database seems to be empty!\0Database server\0Database structure\0Database system or older MySQL server to maximize output compatibility with:\0Database tree separator\0Database used for relations, bookmarks, and PDF features. See [a@http://wiki.phpmyadmin.net/pma/pmadb]pmadb[/a] for complete information. Leave blank for no support. Suggested: [kbd]phpmyadmin[/kbd].\0Database-level tabs\0Database-specific privileges\0Database:\0Databases\0Databases display options.\0Databases statistics\0Databases:\0Date\0Deactivate now\0Deactivate tracking for %s\0Debug SQL\0Dec\0December\0Default\0Default database tab\0Default format; be aware that this list depends on location (database, table) and only SQL is always available.\0Default language\0Default server\0Default server tab\0Default sort order for tables with a primary key.\0Default sorting order\0Default ta“…, 8192) = 8192

Warum PHP 8KB Blöcke einliest, statt min. 64KB wird ewig ein Geheimnis bleiben, scheint aber ein Relikt aus den Anfängen zu sein 🙂

Es gilt, wenn der einzulesende Datenblock, der Blockgröße auf im Filesystem entspricht, dann kann das OS den Datenstrom am effizientesten einlesen. Der Datenzugriff sollte nicht größer als die Blockgröße sein, wobei ganze Mehrfache der Blockgröße ok wären. Wenn jetzt wie bei PHP 8KB eingelesen werden, die Datenblockgröße aber 64 KB ist, braucht das OS schon 8x soviele Filezugriffe nebst Performanceoverhead durch die Lesefunktionen, um den gleichen Inhalt eines Blockes einzulesen. Dazu muß die Platte den Kopf 8x zu dem Datenblock fahren, falls auf einem System noch mehr los ist, als nur dieses einzige Programm. Was man getrost annehmen kann.

Sowas ist einfach ineffizient. Ganz besonders ineffizient, wenn die Datei, die man da einlesen will, mehrere MB groß ist.

Ich hoffe, es hilft Euch ein bisschen bei Euren eigenen Problemen.

 

 

SQLITE Disk IO Error

Manchmal ist es zum Mäuse melken und der Fehler „General error: 10 disk I/O error“ vom SQLLite PDO Teiber von PHP gehört dazu:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 10 disk I/O error'

Wenn Sie das bekommen, schauen Sie doch mal nach, ob Ihr User auch genug Speicherplatz zur Verfügung hat und die Daten zu schreiben. Lassen Sie sich nicht davon täuschen, daß Sie als Root User in der Shell direkt mit dem SQLLite Befehl in der Datenbank sauber schalten und walten können 😉

Genug RAM gibt es gar nicht…

“ ‚Auf der Suche nach der Wahrheit, muß der Adept allerlei Prüfungen des Geistes und Körpers über sich ergehen lassen.‘ „Prüfungen kann ich ertragen, Meister, aber an Langweile werde ich sterben.“ “ ( aus den Büchern Groths )

So geht es mir auch grade 🙂 Ich bin auf der Suche nach der Wahrheit hinter einem DNS Fehler, aber diese Wahrheit bedeutet Stress pur. Bosten läuft zum dritten Marathon durch, und kein Ergebnis, dafür aber Heldenleistungen bei den Prozesswerten.

Dabei fing alles mit einer harmlosen Frage an :

Bluefish3

Sehr lang ist noch milde ausgerückt, denn SQL Dumps werden von MySQLdumper dummerweise so gemacht, daß sie riesige Anweisungsketten sind, die mit einem einzigen INSERT mal ebend hunderttausende bis Millionen von Tabellenzeilen schreiben. Der MysqlServer schafft das deutlich besser als BlueFish 🙂

Wie man hier schön sehen kann, sollte ein Texteditor so nicht in der Prozessliste aussehen:

3.7 GByte RAM verbrauch für ein 2 GB SQL File

3.7 GByte RAM verbrauch für ein 2 GB SQL File

Und seit ca. 20 Minuten sieht man das Bild :

Bluefish2
Keine Ahnung was am Laden von 2 GB Text so viel CPU ziehen kann 😉  Sehr positiv, der Rest vom System reagiert noch richtig gut 🙂 Wozu hat man auch 8 Kerne ? Die langweilen sich ja sonst eh den ganzen Tag.

Nach 30 Minuten glaube ich langsam daran, daß sich …. nein… jetzt, fertig? Dramatik bei BlueFish.. RAM Verbrauch droppt von 3.7 GB auf 1.9 GB , aber dafür mußte etwas ausgelagert werden.. Gnome ist jetzt leider der Meinung, daß BlueFish nicht mehr auf Eingaben reagiert ( stimmt ), und möchte es terminieren, aber Bluefish ackert noch 😉  … Eine Geduldsprobe sonders gleichen..  Das Bluefish verloren hat, weil mir der Geduldsfaden geplatzt ist 🙂

Alternativen sind gefragt !

Die Anwendungen im Desktop sind alles Luschen, da müssen die Kollegen von der Bash ran 🙂

3 Minuten zum Entpacken des SQL Dumps von BZIP2 in Rohtext.
1 Sekunde zum Prüfen mit head, daß kein use database; im Dump steht.
10 Sekunden um zu sehen, daß der SQL beim Einsortieren die gesuchte Tabelle bereits eingefügt hat
10 weitere Sekunden um einen Select laufen zu lassen, der die gesuchten Informationen ausgibt.

In your Face DESKTOP ! 😀

Und das alles nur, weil der (sich beschwerende ) Kunde, seinen SOA Eintrag selbst gelöscht hat.

Was lernen wir daraus ?

  1. Ein Boston mp3 alleine macht noch keine gute Musikschleife
  2. Überbewerte Deinen Texteditor nicht, er ist nur für typische Desktopanwendungsfälle gemacht
  3. Bash rulz!