MySQL unter ZFS

  • Moin,


    ich habe ein Problem: Das Aktualisieren der Anzeigen dauert ewig. In einer von mir betreuten Community dauert es beispielsweise über 8 Stunden, die Reaktionen (ca. 4.5 Millionen) zu aktualisieren, wogegen das eigentlich innerhalb von zwei Stunden erledigt sein sollte. Von den über 8 Millionen Beiträgen im Forum will ich gar nicht erst anfangen. Im Einsatz war bisher übrigens MariaDB 10.3, jedoch habe ich letzte Nacht auf MySQL 8 migriert. Das hat zwar nichts geändert, aber wer weiß, wofür das irgendwann noch einmal gut sein wird.


    Nun habe ich mir sagen lassen, dass das Problem ein Bottleneck sein könnte, allerdings konnte ich auf die Schnelle nichts finden. Ich habe zur Ermittlung iostat verwendet:


    Code
    $ iostat -x -c -d -t 1 360 > iostat.txt
    $ grep avg-cpu -A1 ~/iostat.txt | grep -v "avg-cpu" | grep -v "-" | awk '($6+$4)<50.0{printf("%5.1f\n", $6+$4)}' | wc -l # 50 weil 2 Threads pro Core
    0


    Auch habe ich mir sagen lassen, dass sich das Problem vermutlich nicht durch Anpassen der MySQL-Konfiguration (isv. Caching, etc.) beheben ließe, weshalb ich mich dazu entschlossen habe, es mal mit ZFS zu probieren.


    Der Plan: Einen Hetzner Cloud-Server mitsamt entsprechend großem Volume aufsetzen und das Volume dann exklusiv für MySQL mit ZFS konfigurieren.

    Das Problem: Bisher kannte ich ZFS nur vom Namen und ein bisschen lesen. Daher sind mir zwar die Vorteile von ZFS weitesgehend geläufig, aber dann hört es auch schon fast wieder auf.


    Also habe ich einen Server (Ubuntu 20.04) aufgesetzt und ZFS (zfsutils-linux) installiert. Dann schon die erste Hürde: Das Volume konnte nicht für ZFS verwendet werden, weil es mit ext4 formatiert und eingehangen war. Also habe ich das mittels einem simplen umount /dev/sdb ausgehangen, woraufhin ich dann auch den Pool erstellen konnte:


    Bash
    $ zpool create -o ashift=12 mysql-server /dev/sdb


    So weit, so gut. Oder anders: Ob das so richtig war, weiß ich nicht mit Sicherheit. Zumindest gab es keine Fehlermeldung bei der Pool-Erstellung und der Status scheint auch in Ordnung:



    Anschließend habe ich noch zwei Container hinzugefügt und das Ganze dann entsprechend konfiguriert:


    Bash
    $ zfs create mysql-server/log
    $ zfs create mysql-server/data
    $ zfs set atime=off mysql-server
    $ zfs set compression=lz4 mysql-server
    $ zfs set logbias=throughput mysql-server
    $ zfs set primarycache=metadata mysql-server
    $ zfs set recordsize=16k mysql-server
    $ zfs set xattr=sa mysql-server


    Da InnoDB selbst Prefetching kann, habe ich das ZFS Prefetching deaktiviert:


    Bash
    $ echo 1 >/sys/module/zfs/parameters/zfs_prefetch_disable


    Gerade in Cloud-Umgebungen scheint das empfohlen zu sein, wenn die Festplatten-E/A stark eingeschränkt- und die Bereitstellung weiterer IOPS teuer ist.


    ---


    Nun habe ich MySQL installiert (hätte ich auch vorher tun können, ist ja gehpost, wie gesprungen) und danach die Dateien in ZFS verschoben und die Mountpoints gesetzt:


    Bash
    $ mv /var/lib/mysql/ib_logfile* /mysql-server/log/
    $ mv /var/lib/mysql/* /mysql-server/data/
    $ zfs set mountpoint=/var/lib/mysql mysql-server/data
    $ zfs set mountpoint=/var/lib/mysql-log mysql-server/log
    $ chown mysql.mysql /var/lib/mysql /var/lib/mysql-log


    Anschließend musste ich die MySQL-Konfiguration entsprechend anpassen:



    All set. Dachte ich. Denn scheinbar fehlten dem ganzen nun irgendwelche Rechte, denn der MySQL-Server ließ sich aufgrund von Permission-Fehlern nicht starten. Daher habe ich noch den Teil


    Apache Configuration
    # Allow log file access
      /var/log/mysql.err rw,
      /var/log/mysql.log rw,
      /var/log/mysql/ r,
      /var/log/mysql/** rw,


    in der Datei /etc/apparmor.d/usr.sbin.mysqld ausgetauscht gegen:


    Apache Configuration
    # Allow log file access
      /var/log/mysql/ r,
      /var/log/mysql/** rw,
      /var/lib/mysql-log/ r,
      /var/lib/mysql-log/** rwk,


    Und siehe da: Der MySQL-Server startet ohne Fehler.


    ----


    Nun war alles bereit und ich habe die rund 10 GB große Datenbank auf dem "alten" Server exportiert und dann auf dem ZFS-Test-Server eingespielt. Hat etwas gedauert, habe ich aber auch nicht anders erwartet. Andererseits habe ich zumindest für den Import noch spezielle Einstellungen in MySQL festgelegt:


    Code
    innodb_flush_log_at_trx_commit = 0
    sync_binlog = 0


    Ob ich hierfür auch sync=disabled in ZFS hätte setzen sollen, weiß ich nicht mit Sicherheit, ich habe es nicht getestet.


    Am Ende waren die Daten aber eingespielt und soweit schien auch alles zu funktionieren:



    Ich wollte das Ganze nun endlich testen und hatte die große Hoffnung, dass ich mit diesem Setup eine signifikante Verbesserung (10-20%) des Prozesses zum Aktualisieren der Anzeigen feststellen würde. Aber Pustekuchen. Gefühlt ist genau das Gegenteil eingetreten und das Aktualisieren der Anzeigen hat noch viel länger gedauert, als im "normalen" Setup. Es ist also nicht ein bisschen besser, sondern wesentlich schlechter geworden, weshalb ich nun diesen Thread hier verfasse, weil es ja doch einige gibt, die sich im Gegensatz zu mir schon ausgiebig mit ZFS befasst haben.


    Habe ich in der Umsetzung irgendeinen Fehler gemacht? Irgendwas vergessen? Oder liegt das Problem vielleicht darin, dass der Server, auf dem ich das aufgesetzt habe, ein externer ist (der Server mit der Live-Community steht in Falkenstein, der ZFS-Test-Server in Nürnberg) und dadurch die Latenz zu hoch ist?


    Das war's vorerst. Danke für's Lesen :D

  • Kleiner Nachtrag zum Grundproblem: Nachdem ich die 4.5 Millionen Reaktionen endlich verarbeitet hatte, habe ich dann doch mal versucht, die Posts verarbeiten zu lassen (ohne ZFS). Ergebnis: Fast die doppelte Anzahl Posts (rund 8 Millionen) war in 1/3 der Zeit (also nach knapp 3h) komplett verarbeitet. Kurios.