phpmyadmin, charts, MariaDB – a strange bug with x-axis value order and ORDER clauses in sub-SELECTS

A customer of mine wants to use phpMyAdmin to create some charts whilst working with a complex database. I recommended a trial with phpMyAdmin and its chart functionality. Unfortunately, this did not always work out as expected:

You may e.g. create an ordered resultset first – with a suitable SELECT and ORDER clause – e.g. to get two columns – an index “i” and associatd values “val”. Rows of the resultset ordered e.g. ASC with the “i”-values. But after using the columns “i” a simple line graph the order on the x-axis may appear different than requested by your ORDER clause.

I have posted this as an issue to the (nice) developers of phpmyadmin – who are working on it :-).

There is a workaround. But what I found more interesting is the cause of the problem, which appears on MariaDB and NOT on MySQL. It has apparently to do with a different handling and interpretation of SQL standards for ORDER clauses in sub-SELECTs! See:
https://mariadb.com/kb/en/library/why-is-order-by-in-a-from-subquery-ignored/

This is of general interest for developers using MySQL or MariaDB-databases. I, myself, was not aware of it. Besides an already different handling of database replication, I regard this point as one more indication for a growing “distance” between both RDBM-systems…

If you stumble across the phpmyadmin-chart problem, see a discussion and a workaround on the following site:
https://github.com/phpmyadmin/phpmyadmin/issues/14239

I also want to explicitly thank M. Jayaratne who supplied me with the valuable information referenced above.

Eclipse, Git, SSH2, Einträge in der ~/.ssh/config, Bug bei Ciphers mit Domain-Angabe

Wenn man mit Eclipse (Oxygen2, Neon3) Git-Repositories auf Remote-Servern versorgen will, macht man das natürlich über SSH. Ich hatte in einem früheren Blogbeitrag schon mal auf einen Artikel

https://stribika.github.io/2015/01/04/secure-secure-shell.html

hingewiesen, in dem verschiedene Härtungsmaßnahmen aufgezeigt werden. Natürlich verwendet man dabei auch eine Public Key Authentication, wobei der öffentliche RSA-Schlüssel in hinreichender Länge (> 2048 Bit) auf dem Server implementiert wird. Er ist dort wie üblich beim entsprechenden externen User in die Datei “~/.ssh/authorized_keys” einzukopieren.

Unter Eclipse ist dann in jedem Fall noch Folgendes erforderlich: Unter dem Menüpunkt

Window >> Preferences >> General >> Network Connections >> SSH2

trägt man im Fensterchen zum “General”-Tab das Verzeichnis ein, in dem man seine privaten Keys aufbewahrt – und listet zudem die von Eclipse zu verwendenden Keys für die verschiedenen Git- bzw. SSH-Server auf. Das sieht dann etwa so aus:

Ohne diese Einträge funktioniert eine key-basierte SSH-Authentifizierung unter Eclipse nicht. Das gilt nicht nur für Git sondern auch für andere Arten von SSH-Verbindungen etwa unter den “Remote Systems”-Modulen, für SFTP- und für SSH-Shell-Zugriffe. Die korrespondierende Git-Push-Konfiguration für SSH kann dann etwa wie folgt aussehen:

“55511” sei dabei der gewählte SSH-Port, unter dem der Server erreichbar sein soll. Der Leser muss an dieser Stelle natürlich die für ihn geletenden Einträge vorzunehmen.

Auf einem typischen OpenSSH-Server unter Linux lassen sich die Vorgaben des oben genannten Artikels zu Einträgen in der SSH-Konfigurationsdatei “/etc/ssh/sshd_config” problemlos umsetzen. Etwas anders sieht es aber auf dem OpenSSH-Client-System aus. Dort würde man gerne zumindest folgende empfohlenen Härtungseinträge in der Datei “~/.ssh/config” unterbringen:

Korrekte Einträge in der OpenSSH “~/.ssh/config”-Datei, die aber unter Eclipse nicht funktionieren:

  
Host myserv.myhosteddomain.net
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes256-cbc

Nutzt man die Zeile für “Ciphers” in der angegebenen Form, so funktioniert dies zwar für eine native SSH-Verbindung von der Kommandozeile eines Terminalfensters aus; Eclipse reagiert jedoch traurigerweise mit einer Exception “….. java.lang.NullPointerException“.

Ursache dafür ist eine falsche Behandlung von Domain-Angaben in den Cipher-Verfahren durch die zuständigen Eclipse-Module. Interessanterweise funktionieren aber die Domain-Angaben hinter den KEX-Verfahren!

Folgende Variante funktioniert mit den SSH-Modulen von Eclipse:

Funktionierende SSH-config-Datei für Eclipse:

  
Host myserv.myhosteddomain.net
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
# The following does not 
work in Eclipse 
# Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes192-cbc
# This works, however: 
Ciphers aes256-cbc,aes256-ctr 

Es ist irgendwie traurig, dass Eclipse sich in puncto SSH und Cipher-Angaben fehlerhaft verhält; denn man steht nun vor einer (geringfügigen) Einschränkung der Sicherheit. 256Bit basierte AES-Verfahren sollten im Moment allerdings noch ausreichen.

Ich hoffe, das hilft dem einen oder anderen, der auf die oben genannte Exception stößt. Zu hoffen bleibt auch, dass die Eclipse-Entwickler solche Bugs baldmöglichst beseitigen.

MariaDB, Master/Slave – Master/Slave-Replikation – Synchronisation der Binary Logs mit dem Storage ? – II

Im letzten Beitrag hatte ich eine row-basierte Master/Slave-Replikation zwischen zwei MariaDB-Servern konfiguriert und mit kontinuierlichen INSERTs getestet. Ein Ergebnis war, dass es bei einer Dauerlast auf dem Master und einer hochfrequenten Synchronisation der Log-Files auf dem Slave-System mit Festplatten durchaus zu einem systematischen Anwachsen der “Catch Up”-Zeit zwischen Master- und Slave-System kommen kann. Als Ursache erschien plausibel, dass der Slave mehr mit Updates der Logfiles und deren Synchronisation [SYNC] in Richtung Storage-System beschäftigt ist als mit der Durchführung der SQL-Änderungen in der Datenbank selbst.

Ein Heilmittel bestand darin, die Sychronisation der Log-Files im Bedarfsfall nicht, wie üblicherweise empfohlen, nach jeder Änderung sondern erst nach mehreren Änderungen vornehmen zu lassen. Das RDBMS kann dann wieder Commits in hinreichender Anzahl ausführen und der Änderungsfrequenz auf dem Master-RDBMS nahtlos folgen.

Im vorliegenden Beitrag überprüfen wir diesen Befund nun mittels eines etwas komplexeren Szenarios – nämlich einer parallel stattfindenden “Master/Slave – Master/Slave”-Replikation zwischen zwei Servern für zwei unterschiedliche Datenbanken (Schemata).

Konfiguration des ersten Servers

Die Skizze aus dem letzten Beitrag zeigt die Konfiguration schematisch:

Wir haben zwei RDBMS-Serversysteme “histux” und “hertux“. “hertux” fungiert als Master bzgl. der Datenbank “Kirk”, “histux” dagegen als Master bzgl. der Datenbank “Spock”. Repliziert wird zum jeweils anderen Server.

Die für die Replikation relevanten Einträge in der Konfigurationsdatei “/etc/my.cnf” auf dem Server “hertux” sehen dann etwa wie folgt aus:

Server “hertux”:

[mysqld]
# Enforce GTID consistency
#gtid-mode=ON
#enforce_gtid_consistency=1

# Binary Log 
log_bin=/var/lib/mysql/mysql-bin
binlog_format=row 
binlog_do_db='Kirk'

# Slave with respect to "Spock"
relay_log=/var/lib/mysql/relay-bin
skip_slave_start
log_slave_updates=1

slave-parallel-threads=10
replicate_do_db='Spock' 

# SYNC statements
sync_binlog=1
sync_master_info=2
sync_relay_log=2
sync_relay_log_info=2

# Server ID
server-id	= 2

# These are commonly set, remove the # and set as required.
# port = 3306
...
...

 
Die meisten Einträge kennen wir bereits aus dem letzten Artikel. Die Statements, die sich auf die Slave-Rolle beziehen, wurden dort für das System “histux” erläutert. Sie sind nun in gleicher Weise auf dem System “hertux” für die Datenbank “Spock” zur Anwendung zu bringen.

Neu ist dagegen das Statement

binlog_do_db=’Kirk’

Dieses Statement schränkt die Aufzeichnung der zu replizierenden Master-Änderungen(SQL-Statements oder Row-Änderungen) im Binary Log File auf die Datenbank “Kirk” ein! Das ist im vorliegenden Fall harmlos. Anders sähe die Situation dagegen im Fall einer statement-basierten Replikation aus: Die Einschränkung bezieht sich nämlich immer nur auf die aktive Bank! Das hat Auswirkungen bei bank-übergreifenden SQL-Statements! Im Fall statement-basierter Replikaton sollte man deshalb die Warnungen unter “https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html” beachten:

Zitat bzgl. der Variable “binlog-do-db=db_name”:
…. Statement-based logging: Only those statements are written to the binary log where the default database (that is, the one selected by USE) is db_name. To specify more than one database, use this option multiple times, once for each database; however, doing so does not cause cross-database statements such as UPDATE some_db.some_table SET foo=’bar’ to be logged while a different database (or no database) is selected. …” (Hervorhebung durch mich).

Für die im aktuellen Szenario verwendete “row-basierte” Replikation ist die Situation jedoch einfacher.

Zitat:
“… Row-based logging: Logging is restricted to database db_name. Only changes to tables belonging to db_name are logged; the default database has no effect on this. Suppose that the server is started with –binlog-do-db=sales and row-based logging is in effect, and then the following statements are executed:

USE prices;
UPDATE sales.february SET amount=amount+100;

The changes to the february table in the sales database are logged in accordance with the UPDATE statement; this occurs whether or not the USE statement was issued. However, when using the row-based logging format and –binlog-do-db=sales, changes made by the following UPDATE are not logged:

USE prices;
UPDATE prices.march SET amount=amount-25;

Even if the USE prices statement were changed to USE sales, the UPDATE statement’s effects would still not be written to the binary log. ….”

Das entspricht aus meiner Sicht doch recht gut der Erwartungshaltung eines Anwenders.

Bzgl. seiner zweiten Rolle als Slave-Systems schränkt das Statement

replicate_do_db=’Spock’

die Replikation auf die Bank “Spock” ein; “replicate_do_db” ist das slave-bezogene Gegenstück zum Statement “binlog_do_db” für die Master-Rolle.

Im Falle einer Replikation mehrerer Datenbanken ist zudem folgender Punkt zu beachten:

To specify multiple databases you must use multiple instances of this option. Because database names can contain commas, the list will be treated as the name of a single database if you supply a comma-separated list.

Konfiguration des zweiten Servers

Für den Server “histux” nehmen wir eine gespiegelte Konfiguration vor:

Server “histux”

[mysqld]
# Enforce GTID consistency
#gtid_mode=ON
#enforce_gtid_consistency=1

# Binary Log File 
log_bin=/var/lib/mysql/mysql-bin
binlog_format=row 
binlog_do_db='Spock'

# Slave with respect to database "Kirk"
relay_log=/var/lib/mysql/relay-bin
skip_slave_start
log_slave_updates=1
slave-parallel-threads=10
replicate_do_db='Kirk' 

# SYNC statements  
sync_binlog=1
sync_master_info=2
sync_relay_log=2
sync_relay_log_info=2

#Server ID 
server-id	= 3

# These are commonly set, remove the # and set as required.
port = 3386
...
...

 
Wir haben hier zusätzlich den MySQL-Port geändert! Natürlich müssen festgelegte Ports für die Replikation in evtl. zwischengeschalteten Firewalls geöffnet werden.

Test 1: 10000 INSERTs auf beiden Servern / SYNC-Werte ≤ 2 / Replikationsstart zeitlich versetzt

Auf beiden Servern sehen wir – wie im letzten Artikel beschrieben – einen manuellen Start der Replikation vor (skip_slave_start !). Den priviligierten Datenbank-User für die Durchführung von Replikationen hatten wir bereits im letzten Artikel auf beiden RDBMS angelegt.

Wie im letzten Artikel erzeugen wir auf beiden Servern für eine begrenzte Zeit von ca. 80 Sek. eine
Dauerlast durch permanente Durchführung von 10000 Einzel-INSERTs in Tabellen der Bank “Kirk” auf “hertux” und nun such in Tabellen der Bank “Spock” auf “histux”. Nach dem Start des Testprograms starten wir auf beiden Servern etwas zeitversetzt die jeweilige Slave-Replikation. Vor dem Replikationsstart auf “hertux” müssen wir allerdings noch (einmalig) die Daten für den Master-Server “histux” (DB Spock) am MySQL-Prompt setzen; dies geschieht in Anlehnung an das Vorgehen im letzten Artikel durch folgende Statements:

MariaDB [Spock]> CHANGE MASTER TO MASTER_HOST='histux.mynet.de',  MASTER_USER='repl', MASTER_PASSWORD='MyReplPasswd', MASTER_PORT=3386;
MariaDB [Spock]> CHANGE MASTER TO master_use_gtid=slave_pos;
MariaDB [Spock]> START SLAVE;

Für SYNC-Werte

sync_binlog=1
sync_master_info=2
sync_relay_log=2
sync_relay_log_info=2

auf beiden Servern erhielt ich für meine HW-Konfiguration ein systematisch anwachsendes Nachlaufen eines der beiden RDBMS:

30 < Catch Up-Zeit < 60 Sekunden

Welcher Server hinterherhinkte und welchen genauen Wert die Catch Up-Zeit annahm, hing u.a. davon ab

  • auf welchem Server die INSERTs zuerst gestartet wurden,
  • und wie groß der zeitliche Versatz des Starts der Slave-Replikationen gewählt wurde.

Festzuhalten bleibt aber auch diesmal, dass ein systematisches Anwachsen der Catch Up-Zeit durchaus möglich ist, wenn die SYNC-Parameter zu klein gewählt werden. Die absoluten Werte für die Catch Up-Zeit wirken klein; angesichts der gesamten INSERT-Zeit von ca. 80 Sekunden pro Server sind sie aber signifikant groß.

Test 2: 10000 INSERTs auf beiden Servern / SYNC-Werte ≥ 2 / Replikationsstart zeitlich versetzt

Wählt man hingegen SYNC-Werte von

# SYNC-Werte
sync_binlog=1
sync_master_info=3
sync_relay_log=3
sync_relay_log_info=3

und führt wieder zeitlich etwas versetzte INSERTS auf beiden Servern aus, so ergibt sich keine nenneswerte Catch Up-Zeit:

Server “histux”:

MariaDB [(none)]> SHOW SLAVE STATUS\G
....              
                  Master_Host: hertux.mynet.de
                  Master_User: repl
                  Master_Port: 3306
....
              Replicate_Do_DB: Kirk
....
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1660492
              Relay_Log_Space: 943
....
        Seconds_Behind_Master: 0
....
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
                   Using_Gtid: Slave_Pos
                  Gtid_IO_Pos: 0-2-537280
1 row in set (0.00 sec)

 
Server “hertux”:

MariaDB [(none)]> SHOW SLAVE STATUS\G

                  Master_Host: histux.mynet.de
                  Master_User: repl
                  Master_Port: 3386
...
              Replicate_Do_DB: Spock
...                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 342
              Relay_Log_Space: 1671507
...
        Seconds_Behind_Master: 0
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 3
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: Slave_Pos
                  Gtid_IO_Pos: 0-3-535624
1 row in set (0.00 sec)

 
Beide
Server arbeiten also synchron; auf beiden RDBMS gilt:

Seconds_Behind_Master: 0

Bewertung und Fazit

Steht der bzw. stehen die Master-Server in einer Replikation vom Typ

  • “Master-Slave”>
  • oder “Master/Slave – Master/Slave”

unter dauerhafter Last, sollte man ein Auge auf die SYNC-Parametrierung für die Log-Files werfen. Ggf. sollten die slave-bezogenen SYNC-Variablen auf Werte > 2 gesetzt werden, um ein systematisches Anwachsen von Catch Up-Zeiten zwischen den RDBMS-Systemen zu vermeiden.

sync_binlog=1
sync_master_info=3
sync_relay_log=3
sync_relay_log_info=3

Der zu wählende Wert ist natürlich abhängig von der HW-Konfiguration.

Wir groß ist der potentielle Schaden im Fall eines Crashes, in dem Änderungen verloren gehen, die nicht rechtzeitig auf das Plattensystem geschrieben wurden? Aus meiner Sicht nicht groß. Relevant nach einem Systemcrash ist ja der zu wählende richtige Aufsetzpunkt für die jeweilige lave-Replikation. (Bzgl. der Änderungen in einer Master-Datenbank müssen sowieso Mechanismen der jeweiligen Applikation greifen.) Werden die SYNC-Parameter klein gewählt, sind nur einige wenige letzte Transaktionen vor dem Crash zu prüfen, um den richtigen Aufsetzpunkt (bzw. die zugehörige GTID) zu bestimmen. In einer Abwägung

zwischen einem nicht ganz nahtlosen Wiederaufsetzen der Replikation nach einem Crash
undeiner systematisch anwachsenden Catch UP-Zeit während der Replikation

erscheint mir der begrenzte Aufwand für die Identifikation der richtigen GTID das kleinere Übel zu sein.

Links

https://mariadb.com/kb/en/library/gtid/
https://www.lexiconn.com/blog/2014/04/how-to-set-up-selective-master-slave-replication-in-mysql/
https://docs.jelastic.com/mariadb-master-slave-replication