Posts tagged with “replicación”
Replicación con Multi-Threaded Slaves, sacando uso a nuestros cores
Ahora mismo, todos los servidores que compramos son multi-core o multi-cpu. MySQL ha ido solucionando sus problemas de escalabilidad, sobre todo a nivel del engine InnoDB, y la diferencia es clara entre MySQL 5.0 y MySQL 5.5, donde el rendimiento en entornos multicore es cada vez mayor. Pero aún queda un punto por mejorar, la replicación de MySQL.
En una replicación MySQL Master/Slave el problema se puede ver claramente. Mientras que en el maestro puedes tener cientos de threads modificando datos en paralelo, estos se escriben de forma ordenada en el binlog mientras que el slave, que solo tiene un thread para aplicar los cambios (SQL Thread), tiene que escribir los cambios uno a uno. De esta forma, el rendimiento que ganamos con la paralelización de las consultas, se pierden al llegar al Slave. Razón por la cual en entornos de alta carga siempre vemos que el esclavo va muy por detrás del Master aplicando los cambios (Seconds Behind Master).
Este es un problema que se está intentando solucionar en las versiones de desarrollo de MySQL. En la versión 5.6 se ha introducido un nuevo concepto llamado Workers, que son diferentes SQL Threads para la aplicación en paralelo de los cambios.

Podemos descargar una versión de desarrollo con esta funcionalidad desde http://labs.mysql.com/.
Con una serie nueva de parámetros que veremos a continuación podemos decirle a nuestro Slave cuantos Workers debe lanzar. Cada Worker podrá escribir los cambios del relaylog en paralelo, siempre y cuando pertenezcan a diferentes bases de datos. Por lo que de momento, para poder sacar beneficio a esta nueva funcionalidad, deberás tener diferentes bases de datos, cosa que es bastante habitual en entornos complejos donde el Sharding es la norma a seguir.
En primer lugar, debemos decirle a nuestro MySQL que ahora algunos de los estados relacionados con la replicación la queremos guardar en base de datos, concretamente en tablas dentro de la BD mysql:
relay-log-info-repository="TABLE" master-info-repository="TABLE"
relay-log-info-repository: esta variable indica donde se guardará la posición en la que nos encontramos dentro del relay log master-info-repository: esta variable indica donde se guardará la información referente a su master
Todas las opciones se encuentran ya documentadas en http://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html. Es recomendable tener esta URL a mano, ya que al ser algo que se encuentra en desarrollo, las especificaciones y las opciones cambian constantemente.
Una vez hecho, nos conectamos al Slave y le decimos que queremos cuatro Workers.
mysql> STOP SLAVE; Query OK, 0 rows affected (0.00 sec) mysql> SET GLOBAL slave_parallel_workers=4; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@slave_parallel_workers; +------------------------------+ | @@slave_parallel_workers | +------------------------------+ | 4 | +------------------------------+ 1 row in set (0.00 sec) mysql> START SLAVE; Query OK, 0 rows affected (0.05 sec)
A continuación comprobamos si realmente se han lanzado los 4 procesos:
mysql> show processlist; +----+-------------+------+---------+------+---------------------------------------------+ | Id | User | db | Command | Time | State | +----+-------------+------+---------+------+---------------------------------------------+ | 1 | root | NULL | Query | 0 | init | | 8 | system user | NULL | Connect | 2 | Waiting for master to send event | | 9 | system user | NULL | Connect | 2 | Slave has read all relay log; waiting [...] | | 10 | system user | NULL | Connect | 2 | Waiting for an event from sql thread | | 11 | system user | NULL | Connect | 2 | Waiting for an event from sql thread | | 12 | system user | NULL | Connect | 2 | Waiting for an event from sql thread | | 13 | system user | NULL | Connect | 2 | Waiting for an event from sql thread | +----+-------------+------+---------+------+---------------------------------------------+ 7 rows in set (0.00 sec)
Ahora mismo ya tenemos cuatro threads SQL para la aplicación de datos en paralelo. Como hemos comentado anteriormente, es posible aplicar cambios en paralelo siempre que estos sean de bases de datos diferentes. Por lo no tiene sentido que el número de threads que lancemos sea superior al del número de bases de datos que queremos replicar.
Esta es solo una más de las múltiples mejoras que se están implantando en la rama de desarrollo, intentaré manteneros informados de todas las novedades interesantes que sigan surgiendo :)
Mantener la consistencia de los datos en la replicación
Las replicaciones necesitan de un chequeo constante en la integridad de los datos. Fallos de disco, corrupción de de logs, mezcla de tablas transaccionales y no transaccionales y otros problemas pueden tumbar la consistencia de nuestros datos. Por lo tanto, podemos tener una replicación funcionando, pero los datos, si no hay una comprobación activa, pueden ser diferentes en las dos máquinas. MySQL no tiene comprobaciones activas de consistencia, por lo que es trabajo nuestro. Para ello, instalamos las herramientas maatkit de Percona:
apt-get install maatkit
Las herramientras que usaremos serán mk-table-checksum y mk-table-sync. El funcionamiento de la herramienta se basa en la replicación en base a sentencias de mysql. mk-table-checksum realiza una comprobación mediante un algoritmo de hashing en las tablas, escribiendo los resultados en la base de datos. Estas sentencias se replicarán en el esclavo y se volverán a ejecutar, realizando por lo tanto el mismo hashing en las tablas del esclavo. De esta forma, unicamente debemos comparar los resultados en el maestro y en el esclavo para comprobar si los datos son exactamente iguales o no.
Tenemos dos MySQL en Master-Slave.
NODO 1:
Puerto TCP 19369
master [localhost] {msandbox} (vida) > select * from producto; +---+--------+ | i | nombre | +---+--------+ | 1 | VPS | | 2 | Cloud | +---+--------+ 2 rows in set (0.00 sec)
NODO 2:
Puerto TCP 19370
slave1 [localhost] {msandbox} (vida) > select * from producto; +---+---------+ | i | nombre | +---+---------+ | 1 | VPS | | 2 | Cloud | | 3 | Storage | +---+---------+ 3 rows in set (0.00 sec)
¿Como comprobamos si los datos son o no consistentes?
Primero, le decimos a mk-table-checksum que haga un checksum de todas las tablas de la base de datos vida y guarde el resultado en test.checksum.
~$ mk-table-checksum u=root,p=msandbox --socket=/tmp/mysql_sandbox19369.sock --databases=vida --replicate test.checksum --create-replicate-table Cannot connect to P=19369,S=/tmp/mysql_sandbox19369.sock,h=SBslave1,p=...,u=root Cannot connect to P=19369,S=/tmp/mysql_sandbox19369.sock,h=SBslave1,p=...,u=root DATABASE TABLE CHUNK HOST ENGINE COUNT CHECKSUM TIME WAIT STAT LAG vida producto 0 soporteit69 InnoDB 2 935b4964 0 NULL NULL NULL
Comprobamos los resultados en Master:
master [localhost] {msandbox} (test) > select * from checksum; +------+----------+-------+------------+----------+----------+------------+------------+ | db | tbl | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt | +------+----------+-------+------------+----------+----------+------------+------------+ | vida | producto | 0 | 1=1 | 935b4964 | 2 | 935b4964 | 2 | +------+----------+-------+------------+----------+----------+------------+------------+ 1 row in set (0.00 sec)
Comprobamos los resultados en Slave:
slave1 [localhost] {msandbox} (test) > select * from checksum; +------+----------+-------+------------+----------+----------+------------+------------+ | db | tbl | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt | +------+----------+-------+------------+----------+----------+------------+------------+ | vida | producto | 0 | 1=1 | 6120c018 | 3 | 935b4964 | 2 | +------+----------+-------+------------+----------+----------+------------+------------+ 1 row in set (0.00 sec)
Como podemos ver, el crc del slave (this_crc) es diferente al del master (master_crc) por lo tanto, tenemos incosistencias en los datos de la tabla producto de la base de datos vida.
¿Cómo lo arreglamos?
Podriamos hacerlo volviendo a montar la replicación de cero usando un dump de mysqldump. Esto vale si tu BBDD ocupa solo unos cientos de megas, pero si hablamos de gigas, puedes tardar horas y provocar caidas del sistema. Así que haremos uso de mk-table-sync.
~$ mk-table-sync --execute --replicate test.checksum --sync-to-master u=root,p=msandbox --socket=/tmp/mysql_sandbox19370.sock
Comprobamos si ahora los datos son consistentes:
master [localhost] {msandbox} (vida) > select * from producto; +---+--------+ | i | nombre | +---+--------+ | 1 | VPS | | 2 | Cloud | +---+--------+ 2 rows in set (0.00 sec)
slave1 [localhost] {msandbox} (vida) > select * from producto; +---+--------+ | i | nombre | +---+--------+ | 1 | VPS | | 2 | Cloud | +---+--------+ 2 rows in set (0.00 sec)
slave1 [localhost] {msandbox} (test) > select * from checksum; +------+----------+-------+------------+----------+----------+------------+------------+ | db | tbl | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt | +------+----------+-------+------------+----------+----------+------------+------------+ | vida | producto | 0 | 1=1 | 935b4964 | 2 | 935b4964 | 2 | +------+----------+-------+------------+----------+----------+------------+------------+ 1 row in set (0.00 sec)
Listo! Replicación consistente y en marcha :)
(No) Escalado de escrituras en MySQL
Muchas veces se montan replicaciones Master-Master con el fin de "balancear y escalar escrituras". En MySQL el concepto de escalabilidad existe, pero únicamente en lecturas. En cambio, con las escrituras esto no es posible a no ser que usemos engines especiales como SpiderSQL o modificaciones de replicación como Galera. Las razones por las que no se recomienda usar Master-Master para balanceo de carga son las siguientes:
El balanceo de carga es falso, por el simple hecho de tener una replicación master-master. Si lanzas una Update al HostA que tarda 5 minutos en terminar, cuando la query se replique al HostB este tardará también 5 minutos. Aquí no hay balanceo de carga, da igual a que host lances el INSERT o el UPDATE, todos tendrán que ejecutarlo y dedicar recursos. Por lo tanto no balanceas carga, solamente decides quien será el primero en aplicar los cambios. Si activas Row-Based replication si puede existir una pequeña ganancia, pero no evitas el siguiente problema.
En MySQL no existen los bloqueos de tabla distribuidos, por lo que el balanceo de queries entre diferentes masters dará como resultado datos inconsistentes.
Pongamos como ejemplo una tabla con estos datos:
NODO1> select \* from producto; +----+--------+ | ID | Nombre | +----+--------+ | 2 | VPS | | 4 | cloud | +----+--------+ 2 rows in set (0.00 sec)
Replicado master-master en dos nodos mysql, llamados NODO1 y NODO2. Se balancean las escrituras mediante un balanceador.
En el NODO 1 se bloquea la tabla para hacer un cambio de producto (dejamos de ofrecer VPS y ofrecemos STORAGE), de forma que solo la sesión que ha creado el bloqueo pueda escribir:
NODO1> lock tables producto WRITE; Query OK, 0 rows affected (0.00 sec) NODO1> update producto SET Nombre="storage" where Nombre="VPS"; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 NODO1> select \* from producto; +----+---------+ | ID | Nombre | +----+---------+ | 2 | storage | | 4 | cloud | +----+---------+ 2 rows in set (0.00 sec)
Durante dicha transacción, en el NODO2 entra una petición de eliminar un producto (no ofrecemos VPS). La tabla en realidad está bloqueada en el NODO1, pero no existe bloqueo distribuido, por lo que alguien elimina el producto VPS de la tabla en el NODO2.
NODO2> delete from producto where Nombre="vps"; Query OK, 1 row affected (0.00 sec) NODO2> select \* from producto; +----+--------+ | ID | Nombre | +----+--------+ | 4 | cloud | +----+--------+ 1 row in set (0.00 sec)
En este momento acabamos de tirar a la basura la consistencia de los datos, metíendonos en un problemón:
NODO1> select \* from producto; +----+---------+ | ID | Nombre | +----+---------+ | 2 | storage | | 4 | cloud | +----+---------+ 2 rows in set (0.00 sec) NODO2> select \* from producto; +----+--------+ | ID | Nombre | +----+--------+ | 4 | cloud | +----+--------+ 1 row in set (0.00 sec)
Los datos son incosistentes, la replicación sigue funcionando y nadie se ha enterado. Este ejemplo se aplica a master-master de dos nodos y a replicación circular.
Heartbeat en la replicación de MySQL 5.5
La nueva versión MySQL 5.5 incluye mejoras en la replicación que hace casi obligatorio la actualización a esta nueva versión mayor. Una de ellas ya se comentó en este blog, la replicación semi-síncrona. Ahora vamos a hablar de otra mejora, la inclusión de un heartbeat para la replicación.
Heartbeat es un término (traducido como latidos) usado por muchas aplicaciones de alta disponibilidad. El funcionamiento es sencillo, un sistema envía latidos (que no son más que unos pequeños paquetes de datos) y en el momento que el receptor no los reciba sabrá que el primer equipo se habrá caído.
Esto nos permite saber de una forma casi instantánea cuando una replicación se ha parado. En MySQL 5.5 es el maestro quien envía los latidos al esclavo y si este deja de recibirlos dará por supuesto que la comunicación se ha caído. La configuración es muy sencilla:
STOP SLAVE; CHANGE MASTER TO master_heartbeat_period=segundos; START SLAVE;
El valor en segundos puede ir desde 0.001 a 4294967. Si no se indica nada, por defecto el valor será el resultado de slave_net_timeout/2. Así el slave indica al master cada cuanto tiempo debe enviar el latido.
Y podemos comprobar el estado con este comando:
node2 [localhost] {msandbox} ((none)) > show status like '%heartbeat%'; +---------------------------+-------+ | Variable_name | Value | +---------------------------+-------+ | Slave_heartbeat_period | 1.000 | | Slave_received_heartbeats | 1670 | +---------------------------+-------+ 2 rows in set (0.00 sec)
Introducción a MySQL Cluster
MySQL Cluster es una base de datos que como su nombre indica funciona en un Cluster de servidores. Mucha gente confunde terminos y define un conjunto de servidores con replicación como un MySQL Cluster, pero hay que tener en cuenta que son dos conceptos totalmente distintos. MySQL Cluster nos ofrece:
- Alta disponibilidad
- Escalabilidad
- Failover automático
- Redundancia
- Alto throughput
La versión actual es la 7.1 y puede descargarse de http://www.mysql.com/products/database/cluster/
Componentes
Un Cluster MySQL está compuesto por los siguientes componentes:
Manager (ndb_mgmd): es un servicio encargado de poner en marcha el cluster, conectar nuevos servidores y ejecutar distintos comandos de administración mediante el CLI ndb_mgm. Una vez que hemos levantado el cluster no es necesario ni un requisito indispensable que esté levantado.
Data Nodes (ndbd): son nodos encargados del almacenamiento de los datos. Se recomiendan al menos dos para disponer de redundancia y alta disponibilidad. Estas serán las máquinas más potentes del cluster, almacenarán los índices en memoria y los datos en memoria o disco. Todos los Data Nodes deben tener el mismo hardware para evitar crear cuellos de botella.
API nodes (mysqld): aunque el más usado sea mysqld, un API node puede ser cualquier aplicación que haciendo uso de la API acceda al cluster. El típico, también conocido como SQL Node, es el demonio mysqld típico (compilado con soporte nbdcluster). De esta forma podremos escribir o leer datos de nuestra BBDD como hemos hecho hasta ahora, mediante comandos SQL.
Se recomienda que cada componente esté instalado en una máquina física distinta.
Funcionamiento interno
Internamente, el funcionamiento del cluster se basa en dos conceptos básicos. Replicación interna síncrona y auto particionado de datos. La primera nos ofrece la redundancia y el segundo nos da la escalabilidad. Importante diferenciarlo de la replicación típica de MySQL (asíncrona). En este caso, hasta que los datos no han sido replicados en los nodos seleccionados no se devuelve el control al usuario, obteniendo de esta forma la consistencia que no tenemos en la replicación asíncrona.
El particionado (PARTITION BY KEY) es también totalmente automático. El cluster se encarga de dividir las tablas en distintas particiones y dividir los datos entre los distintos Data Nodes. Aunque es posible que definamos nuestro propio particionado, no se recomienda. Añades complejidad y posiblemente el rendimiento no sea el esperado.
Replicas
A la hora de configurar nuestro cluster, una de los valores más importantes a tener en cuenta es decidir el número de replicas que tendremos de nuestros datos. No podemos decidir cualquier número, si no que tendremos que seguir unas sencillas reglas. Pongamos por ejemplo que tenemos 4 Data Nodes. En este caso podremos tener 1, 2 y 4 replicas. Esto es, el número de nodos debe poder ser divisible por el numero de replicas. Aún así, no se debería tener una única replica, ya que eso no nos da ningún tipo de alta disponibilidad ya que al caerse un solo nodo perderíamos el acceso a los datos.
Node Groups
MySQL Cluster agrupa automáticamente los Data Nodes en grupos. Esto no está bajo nuestro control ni podemos decidir que nodo está en que grupo, será trabajo del cluster hacer estas agrupaciones. Siguiendo el ejemplo anterior, si tenemos 4 Data Nodes y 2 réplicas, MySQL Cluster nos generará dos Node Groups (4/2=2). Además hay que tener en cuenta que el número de particiones que se harán de nuestros datos siempre será igual al número de Data Nodes.
Por lo tanto, imaginemos que tenemos el N1 y N2 en el grupo 1 (G1) y N3 y N4 en el grupo 2 (G2). A la hora de particionar y repartir los datos es necesario pensar en la alta disponibilidad, por lo que el particionado se hará de la siguiente forma:
G1 N1 = P1 y P2' N2 = P2 y P1' G2 N3 = P3 y P4' N4 = P4 y P3'
Siendo PX el número de la partición y PX' una copia de la partición de Backup.
Sabiendo esto, podemos imaginar cuando dispondremos de alta disponibilidad. Mientras al menos uno de los nodos de un grupo esté levantado, el cluster estará online. Por ejemplo se podrían caer N1 y N4 o N3 y N2 y todo seguiría funcionando. Pero una caida de N1 y N2 dejaría el cluster completamente caido.
A mayor número de replicas menos posibilidades de fallo, más escalabe y más throughput :)
Próximamente extenderé el tema de MySQL Cluster con más entradas según vaya profundizando en el estudio de la certificación. La intención será termianr teniendo un mini manual para andar por casa que nos permita dar los primeros pasos.
Replicación maestro-esclavo en MongoDB
Esta es una entrada cortita, gracias en parte a la extrema facilidad de administrador de nuestra base de datos NoSQL favorita. En esta ocasión vamos a ver como crear una replicación dentre dos sistemas MongoDB, en arquitectura Maestro-Esclavo. Como pasa bastante habitualmente, la arquitectura Maestro-Maestro, a pesar de ser posible, no la recomiendan. Esta, al igual que en MySQL, se basa en el truco de hacer que un esclavo sea al mismo tiempo maestro.
Yo voy a mostrar la opción recomendada, Maestro-Esclavo.
Para ello, lo primero es instalar dos MongoDB en Debian Lenny.
El primero se llamará Maestro con IP 192.168.1.105 y el segundo Esclavo con IP 192.168.1.101.
Arrancamos el maestro indicando que actuará con ese Rol:
debian1:/usr/local/mongodb# bin/mongod --master Sat Jun 12 16:41:23 Mongo DB : starting : pid = 2158 port = 27017 dbpath = /data/db/ master = 1 slave = 0 64-bit Sat Jun 12 16:41:23 db version v1.4.3, pdfile version 4.5 Sat Jun 12 16:41:23 git version: 47ffbdfd53f46edeb6ff54bbb734783db7abc8ca Sat Jun 12 16:41:23 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41 Sat Jun 12 16:41:23 waiting for connections on port 27017 Sat Jun 12 16:41:23 ****** Sat Jun 12 16:41:23 creating replication oplog of size: 944MB (use --oplogSize to change) Sat Jun 12 16:41:23 ****** Sat Jun 12 16:41:23 allocating new datafile /data/db/local.ns, filling with zeroes... Sat Jun 12 16:41:23 done allocating datafile /data/db/local.ns, size: 16MB, took 0.026 secs Sat Jun 12 16:41:23 allocating new datafile /data/db/local.0, filling with zeroes... Sat Jun 12 16:41:23 done allocating datafile /data/db/local.0, size: 64MB, took 0.185 secs Sat Jun 12 16:41:23 allocating new datafile /data/db/local.1, filling with zeroes...
Una vez hecho, arrancamos el Esclavo indicándole por parámetro donde está su Maestro:
debian2:/usr/local/mongodb/bin# ./mongod --slave --source 192.168.1.105:27017 Sat Jun 12 16:42:03 Mongo DB : starting : pid = 2172 port = 27017 dbpath = /data/db/ master = 0 slave = 1 64-bit Sat Jun 12 16:42:03 db version v1.4.3, pdfile version 4.5 Sat Jun 12 16:42:03 git version: 47ffbdfd53f46edeb6ff54bbb734783db7abc8ca Sat Jun 12 16:42:03 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41 Sat Jun 12 16:42:03 waiting for connections on port 27017 Sat Jun 12 16:42:03 web admin interface listening on port 28017 Sat Jun 12 16:42:04 allocating new datafile /data/db/local.ns, filling with zeroes... Sat Jun 12 16:42:04 done allocating datafile /data/db/local.ns, size: 16MB, took 0.028 secs Sat Jun 12 16:42:04 allocating new datafile /data/db/local.0, filling with zeroes... Sat Jun 12 16:42:05 done allocating datafile /data/db/local.0, size: 64MB, took 0.185 secs Sat Jun 12 16:42:05 building new index on { _id: 1 } for local.sources Sat Jun 12 16:42:05 Buildindex local.sources idxNo:0 { name: "_id_", ns: "local.sources", key: { _id: 1 } } Sat Jun 12 16:42:05 done for 0 records 0secs Sat Jun 12 16:42:05 repl: from host:192.168.1.105:27017 Sat Jun 12 16:42:05 repl: applied 1 operations Sat Jun 12 16:42:05 repl: end sync_pullOpLog syncedTo: Sat Jun 12 16:42:00 2010 4c139cb8:1
Ya tenemos las dos bases de datos en marcha:

Creamos una nueva base de datos llamada test y añadimos una colección:
\> use test switched to db test \> a = { nombre : "Miguel Angel", apellido : "Nieto" } { "nombre" : "Miguel Angel", "apellido" : "Nieto" } \> db.gente.save(a);
Comprobamos que se ha añadido en Maestro:

Comprobamos que se ha añadido en Esclavo:

Y lo buscamos en el Esclavo:

Más facil y rápido imposible :)
Replicación multi-master y síncrona con Galera

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
Timeout en la replicación del esclavo
Otro de los problemas que nos podemos encontrar en una replicación es la red. Si esta está congestionada o con desconexiones intermitentes podemos terminar teniendo graves como lag entre maestro y esclavos o la parada completa del esclavo. Últimamente me he encontrado con este problema en algunas instalaciones de replicación y los síntomas no ayudaban a conocer la causa. Conectándome al esclavo y ejecutando el típico show slave status no encontraba la razón por la cual la replicación se habia parado. Los dos procesos, IO y SQL estaban funcionando y Seconds Behind Master indicaba 0.
Cuando el esclavo pide los últimos logs al maestro, se queda esperando un tiempo para recibir la respuesta hasta que al final da timeout. Eso es un comportamiento normal, lo que ya no es normal es el valor por defecto de dicha espera, 3600 segundos, ¡una hora! El esclavo se quedará en el estado:
Slave_IO_State: Waiting for master to send event
Los valores que tendremos que modificar en nuestra configuración son:
slave-net-timeout = 30 master-connect-retry = 60
De esta forma, el esclavo se quedará esperando a los eventos del maestro 30 segundos y después dará timeout y se intentará reconectar. Si por alguna razón no puede conectarse al maestro, lo seguirá reintentando cada 60 segundos (valor por defecto).
Como bien dice Daniel Schneller, no te creas todo lo que te dice el show slave status :)
Nuevas trasparencias: administración avanzada de MySQL
Aquí os pongo unas nuevas transparencias de un curso que he dado recientemente. Abarca gran cantidad de temas relacionados con la administración de nuestra base de datos favorita :)
- Instalación
- Engines
- Optimización de consultas
- Optimización de tablas
- Optimización del servicio
- Usuarios y permisos
- Replicación
- Alta disponibilidad
- Backup
- etc. :)
Espero que os guste y os sea de utilidad. Cualquier sugerencia o crítica es bienvenida. Si hay algún fallo comentádmelo para solucionarlo lo antes posible.
¡Gracias a todos!
Los peligros de binlog-do-db en la replicación
A la hora de configurar una replicación, el punto más importante es aquel en el que decidimos que replicar. Y para ello debemos seleccionar que guardar en el log binario. Tenemos muchas opciones, pero hay algunas que debemos evitar:
- binlog-do-db
- binlog-ignore-db
- replicate-do-db
- replicate-ignore-db
Para ver la razón, nada mejor que un ejemplo practico de un sistema master-master.
El servidor A tiene dos bases de datos, VIDA y MUERTE. VIDA será la que se replicará al segundo maestro.
El servidor B solo tiene la base de datos VIDA.
Servidor A:
server-id=101 log-bin=mysql-bin log-slave-updates replicate-same-server-id=0 auto_increment_increment=2 auto_increment_offset=1 binlog-do-db=vida
Servidor B:
server-id=102 log-bin=mysql-bin log-slave-updates replicate-same-server-id=0 auto_increment_increment=2 auto_increment_offset=2 binlog-do-db=vida
Ejecutamos en el servidor A:
node1 [localhost] {msandbox} ((none)) > insert into vida.t values(10); Query OK, 1 row affected (0.00 sec) node1 [localhost] {msandbox} ((none)) > use vida; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A node1 [localhost] {msandbox} (vida) > create table muerte.t (i int); Query OK, 0 rows affected (0.00 sec)
Ya nos hemos cargado la replicación por dos sitios. El valor 10 no se ha insertado en la tabla t de VIDA y por otro lado, el servidor B intentará crear la tabla t en la base de datos MUERTE (que en realidad no tiene).
Last_SQL_Error: Error 'Unknown database 'muerte'' on query. Default database: 'vida'. Query: 'create table muerte.t (i int)'
¿Y cual es la razón de este comportamiento si le hemos indicado que queremos solo la replicación de VIDA? Respuesta corta, binlog-do-db no hace lo que nosotros creemos :) Bien... ¿y la respuesta larga?
Los 4 parámetros para el log binario que hemos visto antes, se aplican si son la base de datos por defecto, esto es, si hemos hecho un USE VIDA. Una vez que convertimos mediante ese comando VIDA en la base de datos por defecto, todos los comandos que ejecutemos se logearán en el log binario.
Por lo tanto, si vemos los comandos SQL ejecutados anteriormente se puede entender la razón por la cual hemos roto la replicación:
- Hemos realizado un insert en la tabla t de VIDA sin que esta sea nuestra base de datos por defecto. MAL! No se logeará, el esclavo no recibirá las actualizaciones.
- Hemos creado una tabla t en MUERTE siendo VIDA nuestra base de datos por defecto. MAL! La sentencia se logeará y enviará al esclavo. Cuando este intente replicarla fallará por no tener dicha base de datos.
Ahora sabemos cual es el problema, hay que saber la solución. Y en esta ocasión tenemos dos:
- Hacer uso de replicate-wild-do-table=VIDA.% Esto logeará todo lo que modifique la base de datos VIDA, sea nuestra base de datos por defecto o no.
- Activar la replicación en base a filas (MySQL 5.1)
Este es un fallo muy común, por lo que os recomiendo revisar las configuraciones de vuestras replicaciones ante posibles fallos de configuración.







