Posts tagged with “percona”
Nuevos cambios, nuevas oportunidades
Hace bastante tiempo que no escribo aquí y creo que es necesario dar una explicación a las personas que me siguen, aunque no sean muchas ;)
Mi vida laboral ha vuelto a dar un cambio importante, el segundo en lo que voy de año. Desde Octubre he entrado a trabajar en Percona como Support Engineer lo cual, bajo mi punto de vista, es un salto profesional grandísimo y le estoy muy agradecido a Ewen Fortune por esta oportunidad. Ahora mismo tengo de todo en mi vida, pero no tiempo libre ;) Los que me conocen bien saben que en cuanto tengo un reto por delante no puedo dejar de trabajar y esforzarme dando lo mejor de mi, por lo que estos meses están siendo muy intensos. Mucha lectura, estudio y práctica para poder alcanzar el nivel de profesionalidad y conocimiento que tanto hacen destacar a Percona.
Seguiré publicando posts sobre MySQL en mi blog, pero en un principio con menos frecuencia que antes, al menos hasta que me asiente completamente en mi nuevo trabajo. De vez en cuando podéis encontrar algún post mio en MySQL Performance Blog y si saco tiempo iré publicando aquí las versiones en Español.
Para empezar, aquí tenéis mi primera contribución:
Avoiding auto-increment holes on InnoDB with INSERT IGNORE
Muchas gracias a todos y un saludo especial a mis antiguos compañeros de Arsys.
Percona Live London
La próxima semana comienza la Percona Live y esta vez podremos disfrutarla sin salir de Europa. Durará dos días, 24 y 25 de Octubre.
Durante el primer dia se impartirán tutoriales de diversos tema, como por ejemplo NDB o Sphinx.
http://www.percona.com/live/london-2011/schedule-tutorial/
Y el segundo día se reserva para las conferencias:
http://www.percona.com/live/london-2011/schedule-conference/
Las conferencias serán impartidas no solo por compañeros de Percona, si no también por trabajadores de empresas como Paypal, Facebook, Nokia, Couchbase o Monty Program.
Como se puede comprobar, posibilidades de aprender hay miles. Aún estás a tiempo de apuntarte :) Yo estaré por allí toda la semana, así que si algún lector de este blog va a asistir a la Percona Live... ¡allí nos veremos!
Recopilar información del SO en el momento preciso
Muchas veces los problemas que tenemos con nuestra base de datos no son continuos, si no puntuales, y ocurren generalmente cuando no estamos mirando la pantalla. Las herramientas que hoy voy a mostraron os permitirán cazar el momento exacto de un problema y recabar todos los datos posibles en ese preciso instante de tiempo.
Las dos herramientas pertenecen al Percona Toolkit. Estas toolkit incluyen lo que antes era Aspersa y Maatkit. Por lo tanto, comenzamos descargando:
Dentro de el las utilidades que hoy usaremos serán:
pt-collector: es la herramienta que se lanzará cuando una condición específica se de. Entre otras cosas se encargará de recargar información sobre IO, procesos, memoria, processlist, estado de las variables en MySQL y salidas de comandos como iostat, mpstat, df, lsof. También permite capturar tráfico tcpdump, el proceso con strace y lanzar un profile con Oprofile.
pt-stalk: será el encargado de llamar a pt-collector cuando una determinado estado, definido por nosotros, se de. Por ejemplo, podemos configurarlo de forma que lance pt-collector cuando el número de queries simultáneas alcancen los 100, cuando existan más de 25 tabla temporales en creación, cuando un proceso/usuario en particular se conecte, etc.
Los dos son script en bash (no es Perl! Yuhuu!). No hay fichero de configuración, se deben editar las variables dentro de los propios scripts.
Empezamos con pt-stack:
# ######################################################################## # Configuration settings. # ######################################################################## # This is the max number ofwe want to tolerate. THRESHOLD=${THRESHOLD:-50} # This is the thing to check for. VARIABLE=${VARIABLE:-Threads_connected} # How many times must the condition be met before the script will fire? CYCLES=${CYCLES:-1} # Collect GDB stacktraces? GDB=${GDB:-no} # Collect oprofile data? OPROFILE=${OPROFILE:-yes} # Collect strace data? STRACE=${STRACE:-no} # Collect tcpdump data? TCPDUMP=${TCPDUMP:-yes} # Send mail to this list of addresses when the script triggers. # EMAIL= # Any options to pass to mysql/mysqladmin, such as -u, -p, etc # MYSQLOPTIONS="" # This is the interval between checks. INTERVAL=${INTERVAL:-30} [...] # This is the location of the 'collect' script. if [ -z "${COLLECT}" ]; then COLLECT="/usr/bin/pt-collect"; fi # This is where to store the collected data. if [ -z "${DEST}" ]; then DEST="/tmp/collected/" fi
Como vemos, la configuración es sencilla. Estamos diciendo que cuando la variable "Threads_connected" tenga un valor superior a 50 lanzará el pt-collect junto con información de oprofile y tcpdump. Hay más opciones, pero esto es un post de introducción, no un manual :)
Entonces, el primer paso es lanzar el pt-stack:
root@debian-slave:~# pt-stalk 2011_10_07_14_34_36 check results: Threads_connected = 2, matched = no, cycles_true = 0 [...] 2011_10_07_14_41_28 check results: Threads_connected = 71, matched = yes, cycles_true = 1 2011_10_07_14_41_28 sleeping 300 seconds to avoid DOS attack
Si ahora miramos la carpeta donde se guardan los resultados de collect, ahí tendemos toda la información necesaria recogida.
root@debian-slave:~# ls -l /tmp/collected/ total 1248 -rw-r--r-- 1 root root 9120 oct 7 14:42 2011_10_07_14_41_29-df -rw-r--r-- 1 root root 19800 oct 7 14:42 2011_10_07_14_41_29-diskstats -rw-r--r-- 1 root root 13 oct 7 14:42 2011_10_07_14_41_29-hostname -rw-r--r-- 1 root root 2645 oct 7 14:41 2011_10_07_14_41_29-innodbstatus1 -rw-r--r-- 1 root root 2645 oct 7 14:42 2011_10_07_14_41_29-innodbstatus2 -rw-r--r-- 1 root root 40800 oct 7 14:42 2011_10_07_14_41_29-interrupts -rw-r--r-- 1 root root 66 oct 7 14:41 2011_10_07_14_41_29-iostat -rw-r--r-- 1 root root 66 oct 7 14:41 2011_10_07_14_41_29-iostat-overall -rw-r--r-- 1 root root 91 oct 7 14:41 2011_10_07_14_41_29-log_error -rw-r--r-- 1 root root 16327 oct 7 14:41 2011_10_07_14_41_29-lsof -rw-r--r-- 1 root root 35580 oct 7 14:42 2011_10_07_14_41_29-meminfo -rw-r--r-- 1 root root 66 oct 7 14:41 2011_10_07_14_41_29-mpstat -rw-r--r-- 1 root root 66 oct 7 14:41 2011_10_07_14_41_29-mpstat-overall -rw-r--r-- 1 root root 66573 oct 7 14:41 2011_10_07_14_41_29-mutex-status1 -rw-r--r-- 1 root root 66573 oct 7 14:42 2011_10_07_14_41_29-mutex-status2 -rw-r--r-- 1 root root 442530 oct 7 14:42 2011_10_07_14_41_29-mysqladmin -rw-r--r-- 1 root root 32040 oct 7 14:42 2011_10_07_14_41_29-netstat -rw-r--r-- 1 root root 41520 oct 7 14:42 2011_10_07_14_41_29-netstat_s -rw-r--r-- 1 root root 53 oct 7 14:41 2011_10_07_14_41_29-opentables1 -rw-r--r-- 1 root root 52 oct 7 14:42 2011_10_07_14_41_29-opentables2 -rw-r--r-- 1 root root 423 oct 7 14:42 2011_10_07_14_41_29-output -rw-r--r-- 1 root root 26742 oct 7 14:41 2011_10_07_14_41_29-pmap -rw-r--r-- 1 root root 15714 oct 7 14:41 2011_10_07_14_41_29-processlist1 -rw-r--r-- 1 root root 194 oct 7 14:42 2011_10_07_14_41_29-processlist2 -rw-r--r-- 1 root root 35224 oct 7 14:42 2011_10_07_14_41_29-procstat -rw-r--r-- 1 root root 44906 oct 7 14:42 2011_10_07_14_41_29-procvmstat -rw-r--r-- 1 root root 6451 oct 7 14:41 2011_10_07_14_41_29-ps -rw-r--r-- 1 root root 229140 oct 7 14:42 2011_10_07_14_41_29-slabinfo -rw-r--r-- 1 root root 25 oct 7 14:41 2011_10_07_14_41_29-stacktrace -rw-r--r-- 1 root root 22924 oct 7 14:41 2011_10_07_14_41_29-sysctl -rw-r--r-- 1 root root 7006 oct 7 14:41 2011_10_07_14_41_29-top -rw-r--r-- 1 root root 90 oct 7 14:41 2011_10_07_14_41_29-trigger -rw-r--r-- 1 root root 6596 oct 7 14:41 2011_10_07_14_41_29-variables -rw-r--r-- 1 root root 2670 oct 7 14:42 2011_10_07_14_41_29-vmstat -rw-r--r-- 1 root root 313 oct 7 14:42 2011_10_07_14_41_29-vmstat-overall
Por ejemplo:
root@debian-slave:~# cat /tmp/collected/2011_10_07_14_41_29-processlist1 |more *************************** 1. row *************************** Id: 304 User: root Host: localhost db: sbtest Command: Query Time: 0 State: NULL Info: UNLOCK TABLES *************************** 2. row *************************** Id: 305 User: root Host: localhost db: sbtest Command: Execute Time: 0 State: Writing to net Info: SELECT c from sbtest where id between 504280 and 504379 order by c *************************** 3. row *************************** Id: 306 User: root Host: localhost db: sbtest Command: Query Time: 0 State: NULL Info: UNLOCK TABLES [...]
Ya no es necesario que te quedes a las 3 de la mañana esperando a que todo deje de funcionar para empezar a recopilar datos :)
Backups y restauraciones de tablas en InnoDB
Mucha gente cree erroneamente que gracias a la opción innodb-file-per-table te permite, como MyISAM, portar una tabla en binario de un servidor a otro de forma transparente o recuperar el backup de una tabla. El problema viene cuando realmente necesitan hacer uso de ese backup y no funciona como ellos esperaban.
Al contrario que con MyISAM, donde los ficheros de tablas MYD e MYI son independientes del resto y portables, todas las tablas de InnoDB dependen de un tablespace común donde se almacenan las definiciones de las tablas y además depende de los IDs de transacciones entre otras cosas. Por lo que, si restauras un .idb, no recuperarás los datos.
Todo esto se aplica a la versión original de MySQL, la desarrollada por Oracle. Pero Xtrabackup y Percona Server nos permite esquivar esta limitación y trabajar con los ficheros binarios como si se tratasen de tablas MyISAM, moviéndolas y restaurándolas de un servidor a otro.
Lo primero que sorprende es que el servidor de origen no tiene porque ser Percona Server, puede ser el MySQL del repositorio de tu distribución. Los únicos requisitos son:
- El servidor origen y destino deben tener --innodb-file-per-table
- Hacer el backup con xtrabackup
- Recuperar el tablespace en un Percona Server
El proceso de realización del backup no cambia, pero si la posterior reparación (--prepare). El comando de reparación será el mismo, solo que añadimos la opción --export.
xtrabackup --prepare --export --innodb-file-per-table --target-dir=/mnt/backups/mysql-data
Esta opción generará un fichero extra .exp
/mnt/backups/mysql-data/db/table.exp /mnt/backups/mysql-data/db/table.ibd
Ahora, ¿cómo importamos esa tabla en otro MySQL?
Tal y como hemos comentado antes, el destino debe ser un Percona Server. Únicamente tendremos que ejecutar los siguientes comandos:
ALTER TABLE db.table DISCARD TABLESPACE;
Copiamos las tablas exportadas al subdirectorio db/ del servidor destino.
ALTER TABLE db.table IMPORT TABLESPACE;
Y listo.
Tenéis información más actualizada en el propio manual de xtrabackup.
http://www.percona.com/docs/wiki/percona-xtrabackup:xtrabackup:export_and_import
Pre-cachear los datos de InnoDB en el buffer pool
Cuando tienes un entorno activo-pasivo o montas un esclavo para las lecturas, el mayor problema que te puedes encontrar al poner los nuevos servidores en producción es que las cachés se encuentren frias (cold cache). Como dichos servidores no han recibido consultas, todas sus cachés, como query cache o innodb_buffer_pool se encuentran vacias y todas las consultas tendrán que ir a disco duro durante los primeros minutos u horas. En esos primeros instantes, el rendimiento de tu backend será pésimo.
Hasta ahora, para evitar en la medida de lo posible ese problema, se lanzaban SELECT contra las tablas que obligasen a leerse todas las filas. Eran consultas muy pesadas que tardaban mucho tiempo en ejecutarse y ralentizaban aún más el rendimiento, pero... no había otra solución. Un ejemplo de está solución se puede leer en el blog de Santi Saez Woop!. Pero esto ya ha cambiado.
En la versión de desarrollo de MySQL 5.6 ya es posible guardar el estado actual del buffer pool en un fichero y recuperarlo en memoria después del reinicio del servicio. No más esperas ni trucos de scripting, ahora hablamos de un procedimiento nativo dentro del propio servicio.
Lanzar un dump del buffer pool:
mysql> SET innodb_buffer_pool_dump_now=ON;
Configurar un dump cuando se realice el apagado del servicio:
mysql> SET innodb_buffer_pool_dump_at_shutdown=ON;
Recuperar un dump en memoria:
mysql> SET innodb_buffer_pool_load_now=ON;
Y en my.cnf podemos poner esta línea, para que se cargue automáticamente durante el arranque del servicio:
innodb_buffer_pool_load_at_startup=ON
Podemos ver el progreso del dump:
mysql> SHOW STATUS LIKE 'innodb_buffer_pool_dump_status';
Y el progreso de la restauración:
mysql> SHOW STATUS LIKE 'innodb_buffer_pool_load_status';
Así como cancelarlo :)
mysql> SET innodb_buffer_pool_load_abort=ON;
Es importante recalcar que si estás utilizando Percona Server, su engine xtradb dispone también de esta funcionalidad, por lo que no tienes que esperar. Simplemente, los comandos cambian:
Lanzar un dump del buffer pool:
mysql> select * from information_schema.XTRADB_ADMIN_COMMAND /*!XTRA_LRU_DUMP*/;
Recuperar un dump en memoria:
mysql> select * from information_schema.XTRADB_ADMIN_COMMAND /*!XTRA_LRU_RESTORE*/;
Programar un dump cada X segundos:
innodb_auto_lru_dump = X
Cargar automáticamente el dump en el arranque:
innodb_buffer_pool_restore_at_startup=1
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 :)








