Hoy vamos a intentar mejorar la replicación de un slave MySQL, cuyo master recibe demasiadas modificaciones como para que el slave sea capaz de seguirle el ritmo. El problema que tienen los slaves, es que con versiones anteriores a MySQL 5.6, la replicación usa un único thread, y por tanto, las sentencias a replicar se ejecutan de forma secuencial. En realidad, si usamos una única base de datos, con MySQL 5.6 y su replicación con múltiples threads, no ganamos nada, puesto que MySQL 5.6 aporta un thread por base de datos.
El testeo se ha realizado con servidores CentOS 6.3 a 64 bits, con dos discos SATA 7200rpm.
Identificando el problema
En cualquier caso, el primer paso es identificar el problema, lo cual de forma natural haríamos con un «top». A continuación veremos como el único proceso del server consumiendo algo de recursos es el proceso mysqld, que a pesar de eso, no llega ni mucho menos a provocar un problema de CPU ni de RAM. Sin embargo, si que vemos un 9.6% de %iowait lo cual indica que podríamos tener ahí el cuello de botella.
[root@slave]$ top
top – 12:28:38 up 23:10, 1 user, load average: 2.76, 2.72, 2.69
Tasks: 211 total, 3 running, 208 sleeping, 0 stopped, 0 zombie
Cpu(s): 2.7%us, 1.6%sy, 0.0%ni, 85.8%id, 9.6%wa, 0.0%hi, 0.2%si, 0.0%st
Mem: 32776052k total, 32519856k used, 256196k free, 156192k buffers
Swap: 16777136k total, 122344k used, 16654792k free, 9163420k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4450 mysql 20 0 29.0g 21g 4628 S 37.7 68.0 394:26.55 mysqld
584 root 20 0 0 0 0 S 6.0 0.0 70:17.46 md4_raid1
Con «iostat» podemos ver el %iowait en intervalos de por ejemplo, 5 segundos. Así si tenemos un %iowait que nunca baja a valores cercanos a cero, tenemos un cuello de botella ocasionado por las operaciones de entrada salida.
[root@slave]$ iostat -t 5
27/03/13 12:30:45
avg-cpu: %user %nice %system %iowait %steal %idle
2,70 0,00 1,87 9,63 0,00 85,80
27/03/13 12:30:50
avg-cpu: %user %nice %system %iowait %steal %idle
3,45 0,00 1,77 9,28 0,00 85,50
27/03/13 12:30:55
avg-cpu: %user %nice %system %iowait %steal %idle
3,43 0,00 1,92 9,39 0,00 85,26
Con «iotop» podemos confirmar como el mysqld es el causante de la mayoría de operaciones de i/o:
[root@slave]$ iotop
Total DISK READ: 0.00 B/s | Total DISK WRITE: 12.24 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
4497 be/4 mysql 0.00 B/s 3.97 M/s 0.00 % 59.72 % mysqld –basedir=/usr –datadir=/data/mysql/ –plugin-dir=/usr/lib64~535 –pid-file=/data/mysql/mysql.pid –socket=/data/mysql/mysql.sock
1149 be/3 root 0.00 B/s 3.96 K/s 0.00 % 57.87 % [jbd2/md4-8]
4498 be/4 mysql 0.00 B/s 1389.55 K/s 0.00 % 32.11 % mysqld –basedir=/usr –datadir=/data/mysql/ –plugin-dir=/usr/lib64~535 –pid-file=/data/mysql/mysql.pid –socket=/data/mysql/mysql.sock
1140 be/3 root 0.00 B/s 3.96 K/s 0.00 % 21.02 % [jbd2/md3-8]
4475 be/4 mysql 0.00 B/s 63.34 K/s 0.00 % 0.12 % mysqld –basedir=/usr –datadir=/data/mysql/ –plugin-dir=/usr/lib64~535 –pid-file=/data/mysql/mysql.pid –socket=/data/mysql/mysql.sock
4463 be/4 mysql 0.00 B/s 0.00 B/s 0.00 % 0.02 % mysqld –basedir=/usr –datadir=/data/mysql/ –plugin-dir=/usr/lib64~535 –pid-file=/data/mysql/mysql.pid –socket=/data/mysql/mysql.sock
Mejora de la i/o
Si ya hemos realizado las optimizaciones necesarias al my.cnf, y al disco duro, y no podemos adquirir discos de mayor rendimiento, como serían los discos SAS a 15Krpm o discos SSD, podríamos pensar en usar un RAID0, idealmente por hardware. Para este testeo, he modificado el RAID1 software del server, para usar un RAID0 software, con los mismos discos duros, dos SATA 7200rpm.
Volúmenes de replicación altos en ambos servidores
Por ejemplo, a continuación hay un ejemplo de la i/o de dos servers idénticos, uno con RAID1 software y otro con RAID0 software, ambos replicando del mismo master, justo al acabar de montar ambos servers (es decir, justo en un momento de replicación alta, pues ambos servidores han de sincronizarse con el master).
RAID1 software, ejecutado sobre la partición que tiene el dbpath del MySQL, cada 5 segundos:
[root@slave]$ iostat -xtc 5 -p md4
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 24,18 2245,79 3114,59 19555,23 9,99 0,00 0,00 0,00 0,00
md4 0,00 0,00 0,00 1317,60 0,00 10627,20 8,07 0,00 0,00 0,00 0,00
md4 0,00 0,00 0,80 3491,60 16,00 30590,40 8,76 0,00 0,00 0,00 0,00
md4 0,00 0,00 0,00 3973,80 0,00 32028,80 8,06 0,00 0,00 0,00 0,00
md4 0,00 0,00 0,00 3959,20 0,00 32056,00 8,10 0,00 0,00 0,00 0,00
md4 0,00 0,00 0,20 4326,20 6,40 34803,20 8,05 0,00 0,00 0,00 0,00
RAID0 software, ejecutado sobre la partición que tiene el dbpath del MySQL, cada 5 segundos:
[root@slave]$ iostat -xtc 5 -p md4
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 10,59 3839,54 258,55 30813,68 8,07 0,00 0,00 0,00 0,00
md4 0,00 0,00 8,20 4923,80 180,80 39612,80 8,07 0,00 0,00 0,00 0,00
md4 0,00 0,00 7,80 5363,00 134,40 42904,00 8,01 0,00 0,00 0,00 0,00
md4 0,00 0,00 12,40 5409,60 281,60 43510,40 8,08 0,00 0,00 0,00 0,00
md4 0,00 0,00 11,80 5398,60 195,20 43188,80 8,02 0,00 0,00 0,00 0,00
md4 0,00 0,00 14,80 5345,20 296,00 42984,00 8,07 0,00 0,00 0,00 0,00
md4 0,00 0,00 14,20 5918,60 272,00 47348,80 8,03 0,00 0,00 0,00 0,00
A primera vista, vemos como el número de escrituras por segundo en RAID1 está en torno a los 3900 – 4300 w/s, mientras que en RAID0 sube a los 5300 – 5900 w/s.
Servidor con RAID0 atrapa al master
Con el tiempo, el servidor con RAID0 ha llegado a atrapar al master (gracias a su mejor ratio de escrituras por segundo), mientras que el servidor con RAID1, debido al elevado volumen de sentencias a replicar, no es capaz de atrapar al master. Ésto se ve cláramente mirando el iowait de ambos servidores, una vez el servidor con RAID0 ya ha atrapado al master:
RAID1 software, ejecutado sobre la partición que tiene el dbpath del MySQL, cada 5 segundos:
[root@slave]$ iostat -xtc 5 -p md4
avg-cpu: %user %nice %system %iowait %steal %idle
3,91 0,00 1,97 9,81 0,00 84,31
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 5,40 3929,60 172,80 31668,80 8,09 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
4,39 0,00 2,12 9,57 0,00 83,92
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 0,20 4327,00 6,40 34635,20 8,01 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
3,91 0,00 2,04 10,14 0,00 83,91
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 2,60 3863,80 83,20 33371,20 8,65 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
3,55 0,00 1,64 10,13 0,00 84,68
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 0,20 3455,00 6,40 27668,80 8,01 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
3,43 0,00 1,71 10,28 0,00 84,58
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 2,60 3545,20 83,20 30844,80 8,72 0,00 0,00 0,00 0,00
RAID0 software, ejecutado sobre la partición que tiene el dbpath del MySQL, cada 5 segundos:
[root@slave]$ iostat -xtc 5 -p md4
avg-cpu: %user %nice %system %iowait %steal %idle
0,55 0,00 0,50 2,03 0,00 96,92
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 3,20 1399,80 102,40 12006,40 8,63 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
3,36 0,00 0,60 3,23 0,00 92,80
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 0,80 2072,20 25,60 17073,60 8,25 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
0,33 0,00 0,35 1,40 0,00 97,92
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 2,80 935,20 84,80 8088,00 8,71 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
0,35 0,00 0,38 1,78 0,00 97,50
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 1,60 1147,00 51,20 9683,20 8,48 0,00 0,00 0,00 0,00avg-cpu: %user %nice %system %iowait %steal %idle
0,30 0,00 0,35 1,68 0,00 97,67
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
md4 0,00 0,00 3,80 1065,40 121,60 9118,40 8,64 0,00 0,00 0,00 0,00
En esta ocación, vemos como el iowait del servidor con RAID0 es mucho mejor que el del RAID1, puesto que el RAID1 tiene el cuello de botella en la i/o, que no es suficiente como para llegar a atrapar al master. El servidor con RAID0, sin embargo, al haber atrapado al master, ya únicamente replica en en vivo los cambios que le suceden al master, y por tanto, no tiene una gran cola de sentencias a replicar, si no que replica directamente los cambios en tiempo real. Esta es la razón de que el número de w/s sea menor, y de que por tanto el iowait también haya disminuido.
Así pues, parece que en este ejemplo, como era de esperar, el RAID0 ayuda a mejorar la i/o, y ésto, en entornos de replicación MySQL con una muy elevada carga de cambios, puede ser una solución al elevado delay de los slaves.
Foto por Disco-Dan
Claro, conciso y muy explicativo. Gracias por compartirlo.