Rocky prefiere la replicación en Access
La replicación de bases de datos síncronas siempre ha dado algún que otro quebradero de cabeza ya que no es tan facil de implementar como en un principio se puede pensar. Una de las técnicas más usadas para lograrlo es el conocido commit en dos fases. En este modo un nodo llamado el coordinador envía un mensaje con una consulta a un commit. Los participantes, en función de si pueden o no aplicar la transacción, mandan una respuesta de fallo o acierto al coordinador. Si alguno manda fallo, la transacción se cancela (rollback) pero si todos dan su visto bueno esta se aplica globalmente (commit).
Esto a simple vista ya nos muestra un problema de base, la gran cantidad de bloqueos que se tienen que aplicar a las tablas y/o filas mientras se hacen las comprobaciones, degradando en gran medida el rendimiento global de nuestro cluster de replicación. Por otro lado si un nodo participante se quedase bloqueado, el coordinador tendría que esperar un timeout para después abortar la transacción. O peor aún, si durante una comprobación el coordinador se cae, los nodos participantes se quedarían bloqueados esperando la respuesta del coordinador sobre que hacer con la transacción.
Por estas y otras razones, sistemas de bases de datos como MySQL o PostgreSQL implementan replicaciones asíncronas.
Para dar una solución a este problema ha nacido la empresa Codership, desarrolladora de un sistema de replicación basado en certificación llamado Galera.
Viendo el gráfico podemos resumir el funcionamiento de Galera:
1- Galera, mediante su API, extrae la información referente a los datos a modificar.
2- Si la transacción pasa el proceso de certificado, se commitea. Si no, se hace rollback.
3- Si la replicación es correcta, se comunica al resto de nodos a través del grupo de comunicación, que es una línea (TCP) a través de la cual se comunican los diferentes nodos.
4- Cada nodo recibirá la transacción y lo pasará también por la prueba de certificación. Si esta es correcta se aplica y si no rollback.
El proceso está muy resumido, internamente la API WSREP (Write Set Replication) es mucho más compleja, pero solo es necesario conocerla a fondo en caso de querer programar contra ella. Para nuestro ejemplo es más que suficiente con tener una visión global.
Una de las primeras cosas que se deben tener en cuenta es que el control de concurrencias es Optimista. Dos nodos pueden estar modificando las mismas filas en transacciones diferentes, pero solo una de ellas será la "ganadora". En ese caso el cluster Galera cancelará la fallida y enviará un mensaje de error, Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK). Además, en caso de que producirse este problema, se puede configurar Galera para reintentar automáticamente el commit de la transacción con wsrep_retry_autocommit=On
Para que Galera funcione, el engine o la base de datos debe ser compatible con la API WSREP, por lo tanto es necesario una modificación del código fuente. La primera en recibir dichas modificaciones y que según Codership está preparada para la producción es InnoDB, así que será la que probaremos en este post :) Dicen que están trabajando en importar los cambios a MariaDB y PostgreSQL.
Para la instalación haremos uso de 3 máquinas debian. El ellas descargaremos el siguiente fichero:
http://launchpad.net/codership-mysql/0.7/0.7.3/+download/mysql-5.1.43-galera-0.7.3-i686.tgz
Este incluye la versión 5.1.43 de MySQL con los parches de compatibilidad con WSREP necesarios. Una vez descomprimido arrancamos el primer nodo:
nodo1:~/mysql-5.1.43-galera-0.7.3-i686# ./mysql-galera -g gcomm:// start
Starting mysqld instance with data dir /root/mysql-5.1.39-galera-0.7-i686/mysql/var and
listening at port 3306 and socket /root/mysql-5.1.39-galera-0.7-i686/mysql/var/mysqld.sock. Done (PID:2226)
Waiting for wsrep_ready. Done
Le hemos indicado que queremos empezar un cluster vacio (ya que no indicamos ninguna IP). Esto es así porque no tiene ningún otro nodo al que conectarse. En cambio, el resto de los nodos se tendrán que arrancar conectandose a este:
nodo2:~/mysql-5.1.43-galera-0.7.3-i686# ./mysql-galera -g gcomm://192.168.1.106 start
Starting mysqld instance with data dir /root/mysql-5.1.39-galera-0.7-i686/mysql/var and
listening at port 3306 and socket /root/mysql-5.1.39-galera-0.7-i686/mysql/var/mysqld.sock. Done (PID:2376)
Waiting for wsrep_ready........ Done
nodo3:~/mysql-5.1.43-galera-0.7.3-i686# ./mysql-galera -g gcomm://192.168.1.106 start
Starting mysqld instance with data dir /root/mysql-5.1.39-galera-0.7-i686/mysql/var and
listening at port 3306 and socket /root/mysql-5.1.39-galera-0.7-i686/mysql/var/mysqld.sock. Done (PID:2376)
Waiting for wsrep_ready........ Done
¡Listo! Ya tenemos nuestro cluster en marcha. Ahora solo queda entrar en MySQL y crear una base de datos :)
nodo1:~# mysql -u root -prootpass -S /root/mysql-5.1.43-galera-0.7.3-i686/mysql/var/mysqld.sock
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.1.43 wsrep_0.7
nodo1> create database pruebas;
Query OK, 1 row affected (0.04 sec)
nodo1> use pruebas;
Database changed
nodo1> create table t (i int) engine=innodb;
Query OK, 0 rows affected (0.08 sec)
Si nos vamos al nodo2, veremos que tenemos la tabla creada:
nodo2> use pruebas;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
nodo2> show tables;
+-------------------+
| Tables_in_pruebas |
+-------------------+
| t |
+-------------------+
1 row in set (0.01 sec)
Para comprobar el correcto funcionamiento de la replicación maestro maestro, metemos un dato en cada nodo. El nodo uno inserta "1", el dos "2"...
nodo1> insert into t VALUES(1);
nodo2> insert into t VALUES(2);
nodo3> insert into t VALUES(3);
nodo3> select * from t;
+------+
| i |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.01 sec)
¡Yeah! Funciona :)
Pero, ¿que pasa si uno de los nodos se cae?
Este sistema de replicación no usa los logs binarios, por lo tanto, a la hora de levantarse un nodo caido el modo de resincronización es ligeramente diferente. Cuando el nodo se reconecta, de los activos se selecciona uno que será el encargado de sincronizar los datos. Dicha sincronización se hace mediante mysqldump, de forma que el nodo activo hará un dump de los datos y los restaurará en el nuevo nodo entrante. Este proceso, como puedes imaginar, se hará más lento y consumirá más recursos contra más grande sean las tablas. En este momento (versión 0.7) galera no soporta otro modo de sincronización, pero se espera que en futuras versiones disponga de compatibilidad con Xtrabackup, LVM y InnoDB HotBackup. Este mismo sistema se usa cada vez que se agrega un nuevo nodo al cluster.
La teoría está bien, pero a mi me ha fallado. Después de parar el nodo3 y escribir datos en el nodo1 y nodo2, los datos no se han resincronizado. También puede ser fallo mio, no he investigado mucho. Acepto críticas y correcciones :P
Respecto a los auto incrementales, no es necesario preocuparse. Galera se encarga de gestionarlos gracias a la opción (que viene activada por defecto) wsrep_auto_increment_control.
En resumen, una solución completa para disponer de replicación multi-master y síncrona y aún en las primeras fases de desarrollo, con muchísimas posibilidades a futuro. Alta disponibilidad y escalabilidad con un sistema muy fácil de implementar y administrar. De momento solo es compatible con InnoDB.
Gracias a Seppo Jaakola por darme permiso para usar imágenes de sus diapositivas en este post.
Las imágenes tienen copyright de CoderShip Puedes conseguir las diapositivas originales en http://forge.mysql.com/w/images/2/20/MySQLGalera.pdf