La version actuelle du plugin ne gère pas les transactions de façon sûre par défaut, car il n'est pas sûre d'exécuter des transactions dans tous les cas. Les transactions SQL sont des opérations atomiques exécutées sur un seul et même serveur. Le plugin ne sait pas toujours quand l'opération atomique commence et quand elle prend fin. Ainsi, il peut décider de basculer de connexion au milieu d'une transaction.
Aucun type de balance de charge MySQL peut détecter les limites d'une transaction sans avoir d'astuce au sein de l'application.
Vous devez utiliser des astuces SQL pour éviter un tel comportement, ou activer la surveillance des appels de transaction à l'API. Dans ce cas, vous devez utiliser des appels à l'API pour contrôler les transactions, voyez ci-après.
Exemple #1 Configuration du plugin avec un esclave et un maitre
[myapp] { "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "192.168.2.27", "port": "3306" } } } }
Exemple #2 Utilisation des astuces SQL pour les transactions
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli) {
/* Evidemment, votre propre gestion des erreurs serait meilleure... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
}
/* Pas un SELECT, utilisera le maitre */
if (!$mysqli->query("START TRANSACTION")) {
/* Evidemment, votre propre gestion des erreurs serait meilleure */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* Empêche le changement de connexion! */
if (!$mysqli->query(sprintf("/*%s*/INSERT INTO test(id) VALUES (1)", MYSQLND_MS_LAST_USED_SWITCH)))) {
/* Utilisez un ROLLBACK dans votre code, pas juste un die() */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if ($res = $mysqli->query(sprintf("/*%s*/SELECT COUNT(*) AS _num FROM test", MYSQLND_MS_LAST_USED_SWITCH)))) {
$row = $res->fetch_assoc();
$res->close();
if ($row['_num'] > 1000) {
if (!$mysqli->query(sprintf("/*%s*/INSERT INTO events(task) VALUES ('cleanup')", MYSQLND_MS_LAST_USED_SWITCH))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
}
} else {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->query(sprintf("/*%s*/UPDATE log SET last_update = NOW()", MYSQLND_MS_LAST_USED_SWITCH)))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->query(sprintf("/*%s*/COMMIT", MYSQLND_MS_LAST_USED_SWITCH)))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
$mysqli->close();
?>
Depuis PHP 5.4.0 la bibliothèque mysqlnd permet au plugin de surveiller le statut de l'autocommit, si celui-ci est utilisé via des appels à l'API plutot que via une requête du type SET AUTOCOMMIT=0. Ceci rend le plugin sensible et réactif aux transactions. Dans ce cas, vous n'avez pas besoin d'utiliser d'astuces SQL.
Si vous utilisez PHP 5.4.0, les appels de l'API pour gérer l'autocommit et le paramètre trx_stickiness=master permettent au plugin de désactiver l'équilibrage de charge et la bascule entre les connexions si autocommit est désactivé, et de diriger les requêtes vers le maitre. Ceci évite la bascule de connexion au milieu d'une transaction. Une fois que autocommit est réactivé, le plugin reprend l'équilibrage de charge.
La détecttion des limites d'une transaction en se basant que l'API a été améliorée avec PHP 5.5.0 et PECL/mysqlnd_ms 1.5.0 pour couvrir non seulement les appels à mysqli_autocommit() mais aussi à mysqli_begin(), mysqli_commit() et mysqli_rollback().
Exemple #3 Le paramètre trx_stickiness
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "127.0.0.1", "port": "3306" } }, "trx_stickiness": "master" } }
Exemple #4 Gestion propre des transactions
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli) {
/* Evidemment, votre propre gestion des erreurs serait meilleure... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
}
/* Désactive autocommit, le plugin utilisera le maitre pour toutes les requêtes */
$mysqli->autocommit(FALSE);
if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) {
/* Utilisez un ROLLBACK dans votre code, pas juste un die() */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if ($res = $mysqli->query("SELECT COUNT(*) AS _num FROM test")) {
$row = $res->fetch_assoc();
$res->close();
if ($row['_num'] > 1000) {
if (!$mysqli->query("INSERT INTO events(task) VALUES ('cleanup')")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
}
} else {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->query("UPDATE log SET last_update = NOW()")) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->commit()) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* Le plugin sait que la trasaction est terminée, il reprend l'équilibrage de charge */
$mysqli->autocommit(TRUE);
$mysqli->close();
?>
Note: Versions requises
La directive de configuration du plugin trx_stickiness=master requiert PHP 5.4.0 ou plus récent.
Veuillez également prendre en compte les restrictions abordées dans la section sur les concepts de gestion des transactions.