Note: Version requise
L'injection d'identifiant de transaction globale côté client a été ajoutée avec mysqlnd_ms version 1.2.0-alpha. Ce n'est pas requis pour les clusters synchrones, comme MySQL Cluster. Utilisez le avec des clusters asynchrones, comme la réplication MySQL.
Depuis la version admissible MySQL 5.6.5-m8, le serveur MySQL fournit des identifiants de transaction globale. Cette fonctionnalité est supportée par
PECL/mysqlnd_ms
1.3.0-alpha ou supérieure. Toutefois, le jeu de fonctionnalités inclus dans la version finale de MySQL 5.6 est insuffisant pour supporter toutes les idées discutées ici. Veuillez vous référer à la section sur les concepts pour plus de détails.
PECL/mysqlnd_ms
peut soit utiliser sa propre émulation des identifiants
de transaction globale, soit la fonctionnalité interne du serveur
MySQL 5.6.5-m8 ou supérieure. D'un point de vue développeur, ces différentes
approches offrent les mêmes fonctionnalités au niveau des niveaux de service
fournis par PECL/mysqlnd_ms. Leurs différences sont abordées dans la
section sur les concepts.
La section sur le démarrage rapide montre l'utilisation de l'émulation de l'identifiant
de transaction globale côté client interne à PECL/mysqlnd_ms
avant de montrer
son homologue côté serveur. Cet ordre assure que l'idée originelle soit abordée
en premier lieu.
Idée et émulation côté client
Dans sa forme de base, un identifiant de transaction globale (GTID) est un compteur dans une table sur le maitre. Le compteur est incrémenté chaque fois qu'une transaction est commitée sur le maitre. Les esclaves répliquent la table. Le compteur a deux rôles. Dans le cas d'un échec du maitre, il aide l'administrateur à identifier l'esclave le plus récent pour le promouvoir nouveau maitre. L'esclave le plus récent est celui qui possède la plus grande valeur de l'identifiant. L'application peut utiliser le GTID pour chercher les esclaves ayant répliqué une certaine écriture identifiée par le GTID.
PECL/mysqlnd_ms
peut injecter du SQL pour toute transaction comitée afin d'incrémenter le GTID.
Le GTID est accessible par l'application pour identifier une opération d'écriture. Ceci permet
au plugin d'assurer le niveau de service consistence de session en requêtant les esclaves qui
ont répliqué la donnée. La charge de lecture est donc supprimée du maitre.
l'émulation GTID coté client a quelques limites, lisez attentivement la section sur les concepts pour bien comprendre les principes et les idées avant de l'utiliser en production.
D'abord, créez une table compteur sur votre maitre et insérez-y un enregistrement. Le plugin ne créer pas la table. Les administrateurs doivent s'assurer qu'elle existe. En fonction du mode de rapport d'erreur, le plugin ignorera éventuellement silencieusement l'absence de table, ou s'arrêtera brusquement.
Exemple #1 Creation de la table de compteur sur le maitre
CREATE TABLE `trx` ( `trx_id` int(11) DEFAULT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1 INSERT INTO `trx`(`trx_id`) VALUES (1);
Dans le fichier de configuration, utilisez on_commit
pour insérer
le SQL qui mettra à jour le GTID, section global_transaction_id_injection
.
Assurez vous que le nom de table pour la commande UPDATE
est
pleinement qualifié. Dans l'exemple, test.trx
est utilisé pour
faire référence à la table trx
dans la base test
.
Le nom pleinement qualifié est important car la connexion sur laquelle la requête sera
éxecutée peut changer et représenter une base différente. Assuez-vous aussi des droits de
l'utilisateur qui ouvre la connexion pour l'éxecution de commandes UPDATE
.
Activez le rapport d'erreurs concernant l'injection de GTID.
Exemple #2 Plugin config: SQL pour l'injection GTID coté client
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "127.0.0.1", "port": "3306" } }, "global_transaction_id_injection":{ "on_commit":"UPDATE test.trx SET trx_id = trx_id + 1", "report_error":true } } }
Exemple #3 Injection GTID transparente
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli) {
/* Utilisez ici votre propre gestion des erreurs... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
}
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if (!$mysqli->query("DROP TABLE IF EXISTS test")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if (!$mysqli->query("CREATE TABLE test(id INT)")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* auto commit mode, lecture sur esclave, pas d'incrémentation */
if (!($res = $mysqli->query("SELECT id FROM test"))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
var_dump($res->fetch_assoc());
?>
L'exemple ci-dessus va afficher :
array(1) { ["id"]=> string(1) "1" }
L'exemple lance 3 requêtes en mode autocommit sur le maitre, causant 3 transactions
sur le maitre. Pour châque requête, le plugin injectera l'UPDATE
configuré de manière transparente avant d'exécuter la requête utilisateur. Lorsque le
script se termine, le GTID a été incrémenté de 3 sur le maitre.
La 4ème requête exécutée dans l'exemple, un SELECT
, ne déclenche pas
l'incrémentation. Seules les écritures sur le maitre déclenchent l'incrémentation.
Note: SQL pour GTID: Solution efficace demandée!
Le SQL utilisé pour le GTID n'est pas efficace. Il est conçu pour être lisible, pas performant. Considérez une solution plus robuste et reportez-la, nous l'incluerons dans le manuel.
Exemple #4 Plugin config: SQL pour récupérer le GTID
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "127.0.0.1", "port": "3306" } }, "global_transaction_id_injection":{ "on_commit":"UPDATE test.trx SET trx_id = trx_id + 1", "fetch_last_gtid" : "SELECT MAX(trx_id) FROM test.trx", "report_error":true } } }
Exemple #5 Obtenir le GTID après l'injection
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli) {
/* Utilisez ici votre propre gestion des erreurs... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
}
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if (!$mysqli->query("DROP TABLE IF EXISTS test")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
printf("GTID after transaction %s\n", mysqlnd_ms_get_last_gtid($mysqli));
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if (!$mysqli->query("CREATE TABLE test(id INT)")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
printf("GTID après transaction %s\n", mysqlnd_ms_get_last_gtid($mysqli));
?>
L'exemple ci-dessus va afficher :
GTID après transaction 7 GTID après transaction 8
Les applications peuvent demander à mysqlnd_ms le GTID correspondant à la dernière
écriture. La fonction mysqlnd_ms_get_last_gtid() retourne le
GTID obtenu lors de l'éxecution d'une requête SQL depuis l'entrée
fetch_last_gtid
de la section
global_transaction_id_injection
du fichier de configuration.
La fonction doit être appelée après que le GTIF ait été incrémenté.
Les applications ne doivent pas exécuter le SQL elles-mêmes car elles risquent alors de causer une incrémentation accidentelle du GTID. Aussi, si la fonction est utilisée, il est plus simple de migrer une application.
Le guide de démarrage rapide montre une requête SQL qui retourne un GTID supérieur ou
égal à celui crée pour la requête précédente. Il s'agit du GTID de la requête précédente
si aucun client ne l'ont incrémenté entre temps jusqu'au SELECT
le
récupérant, sinon, il est plus élevé.
Exemple #6 Plugin config: Vérifier un certain GTID
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "127.0.0.1", "port": "3306" } }, "global_transaction_id_injection":{ "on_commit":"UPDATE test.trx SET trx_id = trx_id + 1", "fetch_last_gtid" : "SELECT MAX(trx_id) FROM test.trx", "check_for_gtid" : "SELECT trx_id FROM test.trx WHERE trx_id >= #GTID", "report_error":true } } }
Exemple #7 Consistence de session et GTID combinés
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli) {
/* Utilisez ici votre propre gestion des erreurs... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
}
/* auto commit mode, transaction sur le maitre, GTID doit être incrémenté */
if ( !$mysqli->query("DROP TABLE IF EXISTS test")
|| !$mysqli->query("CREATE TABLE test(id INT)")
|| !$mysqli->query("INSERT INTO test(id) VALUES (1)")
) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* GTID comme identifiant pour la dernière écriture */
$gtid = mysqlnd_ms_get_last_gtid($mysqli);
/* Consistence de session ("lit tes écritures"): essaye de lire depuis les esclaves, pas seulement le maitre */
if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION, MYSQLND_MS_QOS_OPTION_GTID, $gtid)) {
die(sprintf("[006] [%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* Exécute sur le maitre ou un des esclaves ayant la donnée */
if (!($res = $mysqli->query("SELECT id FROM test"))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
var_dump($res->fetch_assoc());
?>
Un GTID retourné par mysqlnd_ms_get_last_gtid()
peut être utilisé comme option dans le niveau de service consistence de
session, indiqué au moyen de mysqlnd_ms_set_qos().
Dans l'exemple, le plugin exécute la requête SELECT
soit sur le maitre, soit sur l'esclave qui possède la donnée répliquée.
PECL mysqlnd_ms va vérifier de manière transparente chaque esclave configuré
pour savoir s'il possède la donnée répliquée de l'INSERT
au moyen de la table des GTID. La vérification utilise la requête SQL indiquée
par l'option check_for_gtid
de la section
global_transaction_id_injection
du fichier de configuration.
Notez qu'il s'agit s'une opération lente et couteuse, les applications doivent
l'utiliser à bon escient si la charge en lecture sur le maitre devient trop
importante.
Utilisation de la fonctionnalité d'identifiant de transaction globale côté serveur
Note: Support serveur insuffisant dans MySQL 5.6
Le greffon a été développé sur la base d'une version de pre-production de MySQL 5.6. Or il s'avère que les versions MySQL 5.6 publiées finalement ne fournissent pas des clients avec assez d'informations pour imposer la consistence des sessions basés sur les GTIDs. Veuillez vous référer à la section sur les concepts pour plus de détails.
Depuis MySQL 5.6.5-m8, le système de réplication MySQL utilise des identifiants
globaux de transaction côté serveur. Ces identifiants de transaction sont
automatiquement générés et maintenus par le serveur. Les utilisateurs n'ont pas
à s'inquiéter de les maintenir. Il n'y a besoin d'avoir des tables de disponible
à l'avance, ou de configuration sur le on_commit
. L'émulation
côté client n'est plus nécessaire non plus.
Les clients peuvent continuer d'utiliser l'identifiant de transaction globale
dans un but de consistence des sessions lors des opérations de lecture sur
les esclaves de réplication MySQL dans certains cas mais pas dans tous!
L'algorithme fonctionne tel que décrit ci-après.
Des requêtes SQL différentes doivent être configurées pour
fetch_last_gtid
et check_for_gtid
.
Ces requêtes sont fournies ci-dessous. Notez que MySQL 5.6.5-m8 est une version
de développement. Les détails sur l'implémentation serveur peuvent changer dans le
futur et nécessite une adoption des requêtes SQL utilisées.
En utilisant la configuration suivante, n'importe quelle fonctionnalité décrite ci-dessous peut être utilisée en plus de la fonctionnalité des identifiants de transaction globale côté serveur. mysqlnd_ms_get_last_gtid() et mysqlnd_ms_set_qos() continuent de fonctionner tel que décrit. La seule différente est que le serveur n'utilise plus qu'une simple séquence de numéro mais une chaîne contenant un identifiant de serveur et une séquence de numéro. Aussi, les utilisateurs ne peuvent plus déduire un ordre depuis les GTIDs retournés par la fonction mysqlnd_ms_get_last_gtid().
Exemple #8 Configuration du plugin : utilisation de la fonctionnalité GTID interne à MySQL 5.6.5-m8
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "127.0.0.1", "port": "3306" } }, "global_transaction_id_injection":{ "fetch_last_gtid" : "SELECT @@GLOBAL.GTID_DONE AS trx_id FROM DUAL", "check_for_gtid" : "SELECT GTID_SUBSET('#GTID', @@GLOBAL.GTID_DONE) AS trx_id FROM DUAL", "report_error":true } } }