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.