MySQL Shell - Cluster Your Application In 40 Minutes

muster

Auf der DOAGA 2019 in Nürnberg konnte ich die Möglichkeit nutzen, eine LAMP-Applikation (LAMP= Linux + Apache + MySQL + PHP) hochverfügbar zu gestalten. Dazu wird unter Zuhilfenahme der MySQL Shell ein InnoDB Cluster aufgebaut und zusätzlich die klassische Verbindung zwischen Applikation und Datenbank um den MySQL Router erweitert. Im Live-Beispiel wurden sämtliche Komponenten mit Docker zur Verfügung gestellt. Laden Sie sich das Beispiel selbst bei Github herunter und probieren es aus!

Let's Go

Als erstes wird mittels der MySQL Shell eine Verbindung zur ursprünglichen Applikationsdatenbank hergestellt (doag2019_mysql1):

MySQLJS > \connect root:mysql@doag2019_mysql1
Creating a session to 'root@doag2019_mysql1'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 23 (X protocol)
Server version: 8.0.15 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQLdoag2019_mysql1:33060+ sslJS > 

Nun wird im JavaScript-Modus der Shell (zu erkennen am JS-Prompt) ein Cluster mittels des vordefinierten dba-Objektes initialisiert und ein Name vergeben:

MySQL doag2019_mysql1:33060+ ssl JS > var cluster = dba.createCluster("DOAG")
A new InnoDB cluster will be created on instance 'root@doag2019_mysql1:3306'.
Validating instance at doag2019_mysql1:3306...
This instance reports its own address as 147b4a8d40e5
Instance configuration is suitable.
Creating InnoDB cluster 'DOAG' on 'root@doag2019_mysql1:3306'...
Adding Seed Instance...
Cluster successfully created. Use Cluster.addInstance() to add MySQL instances.
At least 3 instances are needed for the cluster to be able to withstand up to
one server failure. 

Die Shell nutzt die offene Verbindung zum MySQL Server, um das Cluster zu initialisieren und die erste Instanz automatisch hinzuzufügen. An dieser Stelle wird zusätzlich auf die notwendige Konfiguration überprüft. Muss die Konfiguration ergänzt werden, nutzt die Shell hierbei die mysqld_auto.conf (Artikel folgt), um einen fortlaufenden Betrieb gewährleisten zu können.

Für ein vollständiges Cluster fehlen nun noch zwei weitere Instanzen. Mit nur einem Befehl lassen sie sich ganz leicht hinzufügen:

Nr. 2 

MySQL doag2019_mysql1:33060+ ssl JS > cluster.addInstance({host: "doag2019_mysql2", user: "root", password: "mysql"})
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Adding instance to the cluster ...
Validating instance at doag2019_mysql2:3306...
This instance reports its own address as 964e69c4f133
Instance configuration is suitable.
The instance 'root@doag2019_mysql2' was successfully added to the cluster. 

Nr. 3 

MySQL doag2019_mysql1:33060+ ssl JS > cluster.addInstance({host: "doag2019_mysql3", user: "root", password: "mysql"})
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Adding instance to the cluster ...
Validating instance at doag2019_mysql3:3306...
This instance reports its own address as d993851a2f5e
Instance configuration is suitable.
The instance 'root@doag2019_mysql3' was successfully added to the cluster. 

And Now?

Nachdem nun zwei weitere Instanzen hinzugeführt wurden, kann der Status des Clusters ermittelt werden:

MySQL doag2019_mysql1:33060+ ssl JS > cluster.status()
{
"clusterName": "DOAG",
"defaultReplicaSet": {
"name": "default",
"primary": "doag2019_mysql1:3306",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
"topology": {
"doag2019_mysql1:3306": {
"address": "doag2019_mysql1:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"doag2019_mysql2:3306": {
"address": "doag2019_mysql2:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"doag2019_mysql3:3306": {
"address": "doag2019_mysql3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "147b4a8d40e5:3306"
} 

Durch die Konfiguration des Clusters wurden die beiden neuen Instanzen automatisch als READ_ONLY (mysqld_auto.conf) konfiguriert und erlauben daher keine schreibenden Zugriffe. Fällt nun der Primary Node (doag2019_mysql1) aus, reagiert das Cluster automatisch und öffnet eine der übrigen Instanzen im Read-Write-Modus:

{
"clusterName": "DOAG",
"defaultReplicaSet": {
"name": "default",
"primary": "doag2019_mysql2:3306",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
"topology": {
"doag2019_mysql1:3306": {
"address": "doag2019_mysql1:3306",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"status": "(MISSING)"
},
"doag2019_mysql2:3306": {
"address": "doag2019_mysql2:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"doag2019_mysql3:3306": {
"address": "doag2019_mysql3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "964e69c4f133:3306"
} 
Ist die verlorene Instanz wieder verfügbar, kann der Primary Knoten bei Bedarf (zurück) gewechselt werden:
MySQL doag2019_mysql2:33060+ ssl JS > cluster.setPrimaryInstance("doag2019_mysql1:3306")
Setting instance 'doag2019_mysql1:3306' as the primary instance of cluster 'DOAG'...
Instance 'doag2019_mysql2:3306' was switched from PRIMARY to SECONDARY.
Instance 'doag2019_mysql1:3306' was switched from SECONDARY to PRIMARY.
Instance 'doag2019_mysql3:3306' remains SECONDARY.
WARNING: The cluster internal session is not the primary member anymore. For cluster management operations please obtain a fresh cluster handle using <Dba>.getCluster().
The instance 'doag2019_mysql1:3306' was successfully elected as primary. 

Let's bootstrap

Damit die Applikation nun stets mit der primären Instanz des Clusters arbeiten kann, muss der MySQL Router gestartet und konfiguriert werden.

Hierbei nutzen wir die Bootstrap-Funktionalität des Routers, indem wir beim erstmaligen Starten lediglich die Verbindung einer Instanz des Clusters angeben. Daraufhin erstellt der Router eine vollständige Konfigurationsdatei und holt sich alle notwendigen Metadaten des Clusters:

mysqlrouter --bootstrap "root@doag2019_mysql1:3306" --user=mysqlrouter --directory /tmp/mysqlrouter --force
Der Router nutzt per Default den Port 6446 für schreibende & lesende Zugriffe sowie den Port 6447 für ausschließlich lesende Zugriffe:
{replace175}gt; mysql --host=doag2019_router --port=6446 -uroot -pmysql
mysql> create database test;
Query OK, 1 row affected (0.02 sec)
{replace179}gt; mysql --host=doag2019_router --port=6447 -uroot -pmysql
mysql> create database test2;
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement 

In der Applikationskonfiguration muss nun nur noch die Datenbankverbindung durch die Verbindung zum MySQL Router über den gewünschten Port ersetzt werden. Alles Weitere regelt das InnoDB Cluster automatisch.

Wenn Sie Fragen zu MySQL im Allgemeinen oder zum Themenbereich Hochverfügbarkeit im Speziellen haben, dann wenden Sie sich gerne an uns.