Quand un site web dynamique commence à avoir un trafic important, généralement on va essayer de multiplier les serveurs web qui hébergent les fichiers.
Multiplier les serveurs web n’est pas le plus difficile, il suffit de faire une réplication des données à chaque mise à jour de votre site et de multiplier les sous-domaines.
Les choses se compliquent lorsque vous souhaitez avoir plusieurs serveurs de base de données. La base de données que nous étudierons ici est MySQL. Nous ne parlerons pas de cluster MySQL dans cet article.
Le but de l’article est de montrer comment séparer un serveur MySQL en trois serveurs distincts : 1 maitre et deux esclaves, avec un seul point d’entrée (le proxy MySQL). Le proxy devra envoyer les requêtes de lecture vers les serveurs esclaves et toutes les autres requêtes vers le serveur d’écriture.
La première partie de ce billet fera dans la théorie, nous verrons ensuite la mise en place.
Théorie réplication et mysql-proxy
Imaginons que vous ayez un site web avec 10% de mises à jour de la BDD (“INSERT” ou “UPDATE”) et 90% de sélections (“SELECT”). L’idéal serait de pouvoir séparer les écritures des lectures, et encore mieux de séparer les lectures en plusieurs serveurs.
La réplication MySQL permet d’avoir un serveur dit maitre (“MASTER”) et n serveurs esclaves (“SLAVES”). La réplication se fait du maitre vers les esclaves donc si vous faites un “INSERT” sur un esclave, les autres serveurs ne seront pas mis à jour.
En gros la réplication enregistre dans des logs (appelés logs binaires) toutes les actions (les requêtes) et les esclaves viennent lire ces logs pour répéter ces mêmes actions.
La réplication seule n’est pas suffisante, car il va falloir séparer les requêtes selon leur type (“INSERT” ou “UPDATE”).
Si vous avez déjà prévu dans votre application des fonctions du genre : mysql_read_master(), mysql_read_slave(), mysql_insert_master() alors aucun problème mais sinon il va falloir ajouter une couche logicielle entre votre application et vos serveurs MySQL.
Cette couche c’est mysql-proxy, un programme très récent écrit en C qui fonctionne sur la plupart des plateformes où fonctionne MySQL.
Application
J’imagine que vous savez ce que sont ssh, lynx, wget, mysqld, mysqldump et les commandes de base de Linux, sinon ce sera plus compliqué mais bon ça ira.
Installation de la réplication MySQL
Vous devez avoir au moins deux serveurs MySQL >=5.0 (1 maitre et 1 esclave) sur deux machines différentes.
Dans la suite de l’article j’utilise trois serveurs (1 maitre et 2 esclaves) mais il n’y a aucune différence.
Décidez quel serveur sera le maitre et qui seront les esclaves.
Configuration du maitre :
- Vérifiez que ces deux lignes n’apparaissent pas dans le fichier de configuration my.cnf :
skip-networking bind-address = 127.0.0.1
Sinon vous ne pourrez pas vous connecter à distance au serveur.
- Juste en dessous de la section [mysqld], rajoutez ces lignes :
log-bin=/home/mysql-logs/mysql-bin.log binlog-do-db=bddarepliquer server-id=1
- Redémarrez votre serveur MySQL.
- Connectez-vous au serveur MySQL pour y lancer quelques requêtes.
Nous allons ajouter l’utilisateur qui sera utilisé par les esclaves pour lire les logs de réplication :
GRANT REPLICATION SLAVE ON *.* TO 'replicant'@'%' IDENTIFIED BY 'motdepasse'; FLUSH PRIVILEGES;
Vous pouvez changez replicant pour un autre utilisateur et surtout changez motdepasse par un vrai mot de passe !
- Ensuite exécutez ces requêtes :
USE bddarepliquer; FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS;
Nous avons bloqué les tables et demandé d’afficher la position dans le log binaire à cet instant précis. Vous allez obtenir ce genre de résultat :
+------------------+----------+---------------+------------------+ | File | Position | Binlog_do_db | Binlog_ignore_db | +------------------+----------+---------------+------------------+ | mysql-bin.000001 | 183 | bddarepliquer | | +------------------+----------+---------------+------------------+ 1 row in set (0.00 sec)
Notez le nom du log binaire et sa position.Quittez le client MySQL, faites un dump de votre base SQL.
- Connectez vous au serveur MySQL (toujours le maitre), nous allons débloquer les tables :
UNLOCK TABLES;
Vous pouvez quitter votre client MySQL, la configuration du maitre est terminée.
Nous avons donc : une sauvegarde d’un instant donné de votre base à répliquer, un serveur MySQL configuré en tant que maitre de réplication, des logs qui enregistrent toutes les actions faites sur ce serveur. Il ne nous reste qu’à configurer les esclaves.
Configuration des esclaves :
- Créez la base de donnée et importez les données du dump.
- Éditez votre fichier my.cnf, ajoutez ces lignes en dessous de la section [mysqld] :
server-id=2 master-user=replicant master-password=motdepasse master-connect-retry=60 replicate-do-db=bddarepliquer
- Redémarrez MySQL
- Connectez vous à vos serveurs esclaves pour exécuter ces requêtes :
SLAVE STOP; CHANGE MASTER TO MASTER_HOST='192.168.0.100', MASTER_USER='', MASTER_PASSWORD='motdepasse', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=183;
Remplacez toutes les valeurs par celles définies/récupérées plus haut.
- Enfin executez, toujours dans votre client MySQL, ceci :
START SLAVE;
La réplication est en place.
Installation du proxy MySQL
Le proxy recevra toutes les connexions destinées aux BDD de votre application sur le port 4040 (par défaut). Il redirigera ensuite automatiquement les requêtes vers les différents serveurs selon leur type.
- Il faut donc avoir un utilisateur ayant accès aux 3 serveurs MySQL, sur chaque serveur il faudra donc ajouter cet utilisateur :
GRANT ALL PRIVILEGES ON *.* TO 'proxyuser'@'%' IDENTIFIED BY 'motdepasse'; FLUSH PRIVILEGES;
Il faut décider où va se trouver le proxy MySQL (sur quel serveur), le
choix importe peu finalement. Imaginons dans notre cas que le proxy
soit situé sur le serveur maitre.
- Sur le serveur qui héberge le proxy MySQL, il faudra aussi cette ligne pour que le proxy puisse se connecter en local au serveur MySQL hebergé sur la même machine :
GRANT ALL PRIVILEGES ON *.* TO 'proxyuser'@'localhost' IDENTIFIED BY 'motdepasse'; FLUSH PRIVILEGES;
- Téléchargez la dernière version de mysql-proxy (actuellement c’est la 0.6.0) : http://dev.mysql.com/downloads/mysql-proxy/, vous pouvez soit télécharger les binaires, soit le compiler vous même.
Dans mon cas j’utilise des binaires déjà compilés.
- Une fois le dossier du programme obtenu sur le serveur choisit pour le proxy, il n’y a plus qu’à le lancer avec la bonne commande, pour des besoins spécifiques j’ai créé un fichier sh :
while true; do LUA_PATH="/etc/mysql-proxy-0.6.0/lib/?.lua" /etc/mysql-proxy-0.6.0/src/mysql-proxy --proxy-lua-script=/etc/mysql-proxy-0.6.0/lib/rw-splitting.lua --proxy-backend-addresses=:3306 --proxy-read-only-backend-addresses=ipesclave1 --proxy-read-only-backend-addresses=ipesclave2 > /dev/null done;
En imaginant dans ce cas que vous avez décompressé les binaires dans etc/mysql-proxy-0.6.0.
Quelques explications, précisions
- LUA_PATH=”/etc/mysql-proxy-0.6.0/lib/?.lua”
définit ou se trouvent les scripts de traitements des requêtes. - /etc/mysql-proxy-0.6.0/src/mysql-proxy
définit l’emplacement du binaire. - –proxy-lua-script=/etc/mysql-proxy-0.6.0/lib/rw-splitting.lua
définit quel script de traitement des requêtes utiliser, dans notre cas c’est la séparation des requêtes d’écritures/lectures que nous utilisons (read/write splitting = rw-splitting) - –proxy-backend-addresses=:3306
définit qui est le serveur d’écriture (:3306 correspond à localhost) - –proxy-read-only-backend-addresses=ipesclave1 –proxy-read-only-backend-addresses=ipesclave2
définissent qui sont les serveurs de lectures (et donc les esclaves réplication)
Voilà une fois ce script passé en executable (chmod +x nomfichier sous Linux) vous pourrez le lancer avec la commande ./script.sh.
Alors pourquoi ce script ? Il permet de relancer automatiquement le serveur quand il plante.
En effet mysql-proxy est encore en version alpha mais tient la route. Le principal plantage intervient lorsque qu’un requête est mal formée, exemple :
insert into table values (1,'test, 'probleme');
Ce genre de requête fait planter le proxy avec un message du style :
file sql-tokenizer.l: line 402 (sql_token_append_last_token): assertion failed: (tokens->len > 0)
Il y’a d’autres messages d’erreur comme :
network-mysqld-proxy.c.2927: I have no server backend, closing connection
Qui intervient lorsque le serveur maitre ne répond plus, mais je n’ai pas encore trouvé pourquoi ce bug survient
Vous pouvez poser vos questions dans les commentaires.
Merci pour ce tuto sympa c’est presque ce qu’il me fellait
Personnellement je vais tester MySQL Proxy avec un cluster de serveur MySQL en Master/Master donc pas d’esclave.
Je vais tester le rw-splitting, c’est super interressant
Merci!
Comment par Motarion — janvier 21, 2008 @ 3:34
salut,
J’utilise également un cluster.
j’ai testé le proxy pour le load balancing Master/master, il n’y pas de problème.
Par contre, quand j’utilise une configuration du genre :
LUA_PATH=”/?.lua” ./mysql-proxy –proxy-backend-addresses=:3306 –proxy-read-only-backend-addresses=slave1:3306 –proxy-read-only-backend-addresses=slave2:3306 –proxy-lua-script=/rw-splitting.lua –admin-address=:4041 &
Toutes les requetes sont envoyées systématiquement au maitre.
autrement dit, les “proxy-read-only-backend-addresses” ne fonctionnent pas.
quelqu’un aurait il rencontré ce probleme?
J’aimerais également savoir à quel moment le proxy est cencé load balancer les requetes. (connexion ?)
merci
Comment par maverick — mars 13, 2008 @ 4:13
Salut, en fait la plupart du temps au début de l’utilisation on s’attend à ce que toutes les requêtes de lecture soient envoyées aux esclaves.
En réalité celà se produit seulement si le pool de connexion est remplis. Ce sont des paramètres de rw-splitting.lua.
Testes avec plusieurs clients fictifs mysql qui se connectent, tu verras à un moment que le client est envoyé vers l’esclave.
Je n’ai pas d’idées pour le load balancing.
Comment par sangokode — mars 13, 2008 @ 4:57