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 😉