# rm-rf.es | Administración de sistemas

Bitácora personal de un SysAdmin Gnu/Linux, Windows, BSD...

MySQL: crear bases de datos con caracteres especiales en el nombre

MySQLLo primero que hay que decir, es que no suele ser recomendable crear bases de datos con caracteres especiales en su nombre, ni en el de las tablas, campos, etc. El motivo es que aparte de que no tendría porque ser un requerimiento (no veo el motivo la verdad), con posteridad puede generar numerosos problemas de compatibilidad con las aplicaciones e incluso con el propio servidor MySQL.

Si a pesar de esto, seguimos necesitando añadir al nombre caracteres como @%?$#… tenemos que especificar el nombre de la bbdd dentro de la shell MySQL entre comillas de ejecución “:

Error:

mysql> create database test&01;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual
 that corresponds to your MySQL server version for the right syntax to use
 near '&01' at line 1

Solución:

mysql> create database `test&01`;
Query OK, 1 row affected (0.00 sec)

Limitar recursos por usuario en MySQL

MySQLYo, hasta el momento, la única forma que conocía de limitar recursos por usuario en MySQL era estableciendo la variable max_user_connections o en su defecto creando instancias independientes del servicio para distintas bases de datos. Revisando la documentación de MySQL me he dado cuenta de que es posible aplicar más restricciones de forma independiente para cada usuario, entre las que se encuentran:

  • Número de consultas que un usuario pueda hacer cada hora.
  • Número de updates que un usuario puede hacer cada hora.
  • Número de veces que un usuario puede acceder al servidor a la hora.
  • Número de conexiones simultaneas permitidas para cada usuario (como max_user_connections pero a nivel individual en lugar de global).

Estos límites tendrán un contador para cada acceso del usuario,excepto cuando su consulta sea servida a través de la cache. Este tipo de contextos se establecen en la tabla mysql.user y los podemos aplicar mediante GRANT. En el siguiente ejemplo estableceríamos todos estos límites para el usuario prueba cuando sus conexiones se realizan desde localhost y contra la base de datos test:

mysql> CREATE USER 'prueba'@'localhost' IDENTIFIED BY 'PASSWORD';
mysql> GRANT ALL ON test.* TO 'prueba'@'localhost'
->     WITH MAX_QUERIES_PER_HOUR 100
->          MAX_UPDATES_PER_HOUR 30
->          MAX_CONNECTIONS_PER_HOUR 200
->          MAX_USER_CONNECTIONS 10;

Para realizar modificaciones en estos límites a posteriori, utilizaremos a nivel global GRANT USAGE:

mysql> GRANT USAGE ON *.* TO 'prueba'@'localhost'
    ->    WITH MAX_QUERIES_PER_HOUR 50;

Para eliminar cualquier límite establecido ponemos el valor a 0:

mysql> GRANT USAGE ON *.* TO 'prueba'@'localhost'
    ->    WITH MAX_QUERIES_PER_HOUR 0;

Para poner a 0 los contadores de limites a nivel general podemos ejecutar el comando:

mysql> FLUSH USER_RESOURCES

También recargando las tablas de privilegios:

mysql> FLUSH PRIVILEGES

Múltiples instancias MySQL en un servidor o Cluster

MySQLYa sea dentro de un Cluster o directamente en un servidor estándar, podemos necesitar tener más de una instancia diferenciada de un servicio, en este caso MySQL. Vamos a ver el proceso de instalación y configuración de dos instancias/servidores MySQL dentro de un único servidor.

Lo primero que hacemos es la instalación propiamente dicha de MySQL, ya sea por rpm, yum, apt o compilando. Nosotros bajamos el rpm de MySQL 5.5 y lo instalamos:

# wget http://mysql.mirrors.ovh.net/ftp.mysql.com/Downloads/MySQL-5.5/MySQL-5.5.20-1.linux2.6.x86_64.tar
# tar -xvf MySQL-5.5.20-1.linux2.6.x86_64.tar
# rpm -ivh MySQL-server-5.5.20-1.linux2.6.x86_64.rpm MySQL-client-5.5.20-1.linux2.6.x86_64.rpm

Una vez instalado vamos ver como estructurar el sistema:

  1. Todas las instancias compartirán los binarios y librerías, ubicados en /usr/bin/mysql /usr/lib64/mysql
  2. Cada instancia tendrá su filesystem o directorio en el que almacenar sus ficheros propios de configuración, bases de datos y logs. Si es un cluster se encontrará en una NAS y servido por iSCSI o FC.
    /mysql1
    /mysql1/etc
    /mysql1/data
    /mysql1/data/logs
    /mysql2
    /mysql2/etc
    /mysql2/data
    /mysql2/data/logs
    ...
    ...
  3. Cada instancia tendrá su propio script de arranque:
    /etc/init.d/mysql1
    /etc/init.d/mysql2
    ...
    ...

El punto 1 está hecho ya que tenemos MySQL instalado. Para el punto 2, creamos la estructura de directorios indicada y copiamos en /mysqlX/etc/ el fichero de configuración por defecto de MySQL my.cnf ubicado en /etc:

# cp -p /etc/my.cnf /mysql1/etc/

Para diferenciar cada instancia debemos hacer lo siguiente:

  1. Cada instancia escuchará por una IP distinta. Si comparten IP tendrán que escuchar por puertos distintos.
  2. Cada instancia usará un socket distinto.
  3. Cada instancia tendrá un PID file distinto.
  4. Cada instancia tendrá un datadir distinto.

Así pues, editamos dichos valores en cada my.cnf de cada instancia según requerimientos:

[mysqld]

# Ruta al datadir de cada instancia:
datadir=/mysql1/data

# Puerto de escucha, si las instancias comparten IP
# debe ser distinto en cada una
port=3306

# IP de escucha, si se quiere mantener el mismo puerto TCP
# para todas debe ser única en cada una:
bind-address=10.0.0.110

# Ruta al socket (único por instancia)
socket=/var/tmp/mysql1.sock

Una vez configurados los my.cnf de todas las instancias podemos inicializar e instalar las bases de datos por defecto:

# mysql_install_db --datadir=/mysql1/
# mysql_install_db --datadir=/mysql2/

Ya podríamos arrancar las dos instancias manualmente, únicamente debemos especificar el fichero de configuración:

# /usr/bin/mysqld_safe --defaults-file=/mysql1/etc/my.cnf &
# /usr/bin/mysqld_safe --defaults-file=/mysql2/etc/my.cnf &

Si queremos usar los scripts de arranque en init.d deberemos cambiar los siguientes parámetros:

# If you change base dir, you must also change datadir. These may get
# overwritten by settings in the MySQL configuration files.

# Únicamente si no se especifican en el my.cnf
basedir=
datadir=

Ruta al PID file de cada instancia:

mysqld_pid_file_path=/mysql1/data/mysql1.pid

En el case de arranque añadir el fichero de configuración:

case "$mode" in
  'start')
    # Start daemon

    # Safeguard (relative paths, core dumps..)
    cd $basedir

    echo $echo_n "Starting MySQL"
    if test -x $bindir/mysqld_safe
    then
      # Give extra arguments to mysqld with the my.cnf file. This script
      # may be overwritten at next upgrade.
      mysqld_safe --defaults-file=/mysql1/etc/my.cnf --pid-file="$mysqld_pid_file_path" >/dev/null 2>&1 &

Crear un cluster MySQL con un único comando

MySQLSi por lo que sea no tenéis ni tiempo ni ganas para montar un cluster MySQL podéis echar un vistazo a esta opción que nos ofrece MySQL Cluster Manager. Básicamente nos bajamos la versión adecuada para nuestro sistema operativo y arquitectura de MySQL Cluster Manager, la descomprimimos y ejecutamos un comando con el que arrancará el cluster.

Para bajarla, hay acceder a edelivery.oracle.com (hay que registrarse en Oracle) y elegir el pack “MySQL Database”, luego sale ya todo el listado de descargas.

Una vez descargado, es tan simple como:

# tar -xzvf mcm-1.1.2_32-cluster-7.1.15a_64-solaris10-x86.tar.gz
# mv  mcm-1.1.2_32-cluster-7.1.15a_64-solaris10-x86 mcm
# cd mcm
# ./bin/mcmd --bootstrap
MySQL Cluster Manager 1.1.2 started
Connect to MySQL Cluster Manager by running "/home/alex/V27753-01/mcm/bin/mcm" -a localhost:1862
Configuring default cluster 'mycluster'...
...
...

A partir de ese momento ya tendremos un cluster MySQL formado por dos nodos de datos (ndbd), dos servidores MySQL (mysqld) y un nodo de gestión (ndb_mgmd). Si quisieramos personalizar el cluster deberíamos acceder a la consola mcmd y gestionarlo desde ahí:

#./bin/mcmd
mcm>create site -h khepri14 mysite;
mcm>add package -b /home/mcm/cluster mypackage;
mcm>create cluster -P mypackage -R ndb_mgmd@khepri14,ndbd@khepri14,ndbd@khepri14,mysqld@khepri14,mysqld@khepri14 m
ycluster;
mcm>set port:mysqld:4=3306,port:mysqld:5=3307 mycluster;
mcm>start cluster mycluster;

Replicación MySQL: SQL_SLAVE_SKIP_COUNTER

MySQLHace unos días vimos como crear una replicación MySQL maestro-esclavo paso a paso. Ahora vamos a empezar a ver la forma de arreglar los errores más comunes en este tipo de replicaciones. En esta entrada concretamente aquel provocado cuando una acción realizada en el master no se puede replicar al esclavo por el motivo que sea.

Vamos a forzar un fallo en la replicación creando una base de datos en el servidor esclavo y posteriormente crearla en el maestro. La forma natural de que el esclavo tuviera esa base de datos sería heredandola del maestro, no creándose de forma manual. Esto provocará el fallo de la replicación:

mysql-esclavo> CREATE DATABASE prueba;
mysql-master> CREATE DATABASE prueba;

Una vez realizado esto, el master replicará la creación de esa base de datos en el esclavo, pero como la hemos creado a mano provocará el fallo y que se pare la replicación, lo podemos ver al hacer un SHOW SLAVE STATUS en el esclavo:

mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 192.168.1.129
                Master_User: replicacion
                Master_Port: 3306
              Connect_Retry: 60
            Master_Log_File: mysqld-bin.000004
        Read_Master_Log_Pos: 179
             Relay_Log_File: mysqld-relay-bin.000008
              Relay_Log_Pos: 236
      Relay_Master_Log_File: mysqld-bin.000004
           Slave_IO_Running: Yes
          Slave_SQL_Running: No
            Replicate_Do_DB:
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 1007
                 Last_Error: Error 'Can't create database 'prueba'; database exists' on query. Default database: 'prueba'. Query: 'create database prueba'
               Skip_Counter: 0
        Exec_Master_Log_Pos: 98
            Relay_Log_Space: 317
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: No
         Master_SSL_CA_File:
         Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
             Master_SSL_Key:
      Seconds_Behind_Master: NULL
1 row in set (0.00 sec)

Sabemos que el error se encuentra en la consulta SQL de creación de la base de datos “prueba”. Gracias a la opción SET GLOBAL SQL_SLAVE_SKIP_COUNTER podemos decirle al servidor esclavo que se salte X posiciones del log binario para así evitar las consultas problemáticas:

mysql-esclavo>SET GLOBAL SQL_SLAVE_SKIP_COUNTER =1;

Con el comando anterior hemos saltado la consulta de creación de la base de datos “prueba”, ahora podemos reiniciar la replicación en el esclavo y verificar si ya no tenemos errores y vuelve a la normalidad:

mysql-esclavo> START SLAVE;
Query OK, 0 rows affected (0.01 sec)

mysql-esclavo> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 192.168.1.129
                Master_User: replicacion
                Master_Port: 3306
              Connect_Retry: 60
            Master_Log_File: mysqld-bin.000004
        Read_Master_Log_Pos: 179
             Relay_Log_File: mysqld-relay-bin.000008
              Relay_Log_Pos: 317
      Relay_Master_Log_File: mysqld-bin.000004
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB:
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 0
                 Last_Error:
               Skip_Counter: 0
        Exec_Master_Log_Pos: 179
            Relay_Log_Space: 317
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: No
         Master_SSL_CA_File:
         Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
             Master_SSL_Key:
      Seconds_Behind_Master: 0
1 row in set (0.00 sec)

Efectivamente, todo ha vuelto a la normalidad. Si siguiera pasando habría que revisar la opción de seguir saltando consultas del log. Lo importante cuando hacemos esto es mantener los mismos datos en el maestro y el esclavo, ya que en este caso sabemos que simplemente era por la prueba de crear la base de datos, pero en cuestión de consultas INSERT, DROP, etc la cosa puede no ser tan sencilla.

Replicacion MySQL maestro-esclavo

MySQLEn este artículo vamos a continuar con la alta disponibilidad de MySQL creando un sencilla arquitectura de replicación maestro-esclavo (master-slave). Nuestro sistema va a contar con dos servidores MySQL, uno que hará la función de maestro, que recibirá todos los UPDATES de tablas y los enviará mediante un log binario a otro servidor MySQL esclavo (podríamos poner tantos como quisieramos).

Partimos de la base que tenemos instalado MySQL en ambos servidores, sino toca instalarlo como si de una instalación estándar se tratara (por yum, compilando, por paquetes precompilados, etc). También utilizaremos la base de datos world para hacer pruebas, disponible en:

http://downloads.mysql.com/docs/world.sql.zip

# yum install mysql-server

Vamos a utilizar la siguiente nomenclatura para los hosts:

mysql-master
mysql-slave

Lo primero que debemos saber a la hora de configurar los dos nodos es que:

  1. El servidor Master debe tener activado el log binario (log-bin).
  2. Ambos deben tener un identificador único (server-id).
  3. El servidor maestro deberá tener un usuario con privilegio REPLICATION SLAVE.

 

Puesta en marcha del Master (mysql-master)

Para activar el log binario, accedemos al fichero de configuración /etc/my.cnf y añadimos log-bin dentro de la sección [mysqld].

[mysqld]
log-bin

En la misma sección [mysqld] debemos asignar un número único a modo de identificador tanto al Master como al Slave, en este caso asignamos 1 al Master:

Master my.cnf:

[mysqld]
log-bin
server-id=1

Reiniciamos el servidor maestro y verificamos que los logs binarios se han creado en el directorio de datos:

mysql-master# /etc/init.d/mysqld restart && ls -l /var/lib/mysql/*bin*
-rw-rw---- 1 mysql mysql 98 Jul 30 23:28 /var/lib/mysql/mysqld-bin.000001
-rw-rw---- 1 mysql mysql 20 Jul 30 23:28 /var/lib/mysql/mysqld-bin.index

Accedemos por shell a la consola MySQL y creamos el usuario con el privilegio REPLICATION SLAVE, 192.168.1.129 es la IP del servidor esclavo:

mysql-master> GRANT REPLICATION SLAVE ON *.* TO 'replicacion'@'192.168.1.129' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.01 sec)

mysql-master> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Aprovechamos para volcar la base de datos world sobre la que haremos algunas pruebas:

mysql-master> create database world;
mysql-master> use world
mysql-master> SOURCE world.sql

 

Puesta en marcha del Slave (mysql-slave)

En la sección [mysqld] del fichero de configuración /etc/my.cnf debemos asignar  el id 2 al Slave:

[mysqld]
server-id=2

Reiniciamos el servidor MySQL esclavo:

mysql-master# /etc/init.d/mysqld restart

 

Copia inicial de la base de datos (directorio data)

Antes de activar la replicación ambos servidores deben tener la misma información (bases de datos) y en el mismo punto de restauración. Lo primero que haremos antes de hacer la copia es hacer un flush de las tablas y bloquear las escrituras, de este modo podemos hacer el ‘snapshot’ sin tener que apagar el servidor MySQL. Todo esto lo hacemos en el Master:

mysql-master> FLUSH TABLES WITH READ LOCK;

Ahora podemos hacer la copia de muchas formas, con mysqldump, rsync de los datos, comprimiendo el directorio de datos donde se alojan las bases de datos con .tar.gz, etc

mysql-master# cd /var/lib && tar czvf /tmp/data-mysql.tar.gz mysql
mysql-master# scp /tmp/data-mysql.tar.gz root@mysql-slave:/tmp
data-mysql.tar.gz                                                                100%  417KB 416.9KB/s   00:00

Una vez realizado podemos volver a desbloquear las tablas, no sin antes abrir una nueva shell de mysql y apuntar por un lado el fichero de log binario que se está usando y la posición en la que se ha quedado pues será la que se usará posteriormente para comenzar la replicación en el esclavo:

mysql-master> SHOW MASTER STATUS;
+-------------------+----------+--------------+------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| mysqld-bin.000002 |   724514 |              |                  |
+-------------------+----------+--------------+------------------+
1 row in set (0.01 sec)

Ahora sí que podemos desbloquearlas:

mysql-master> UNLOCK TABLES;

Llega el momento de movernos al esclavo, lo primero que debemos hacer es asegurarnos de parar la replicación en caso de que estuviera activa:

mysql-slave> slave stop;
Query OK, 0 rows affected, 1 warning (0.00 sec)

En este momento los datos del master que hemos comprimido tendríamos que restaurarlos en la carpeta de datos del esclavo, simplemente los descomprimimos sobre la ruta /var/lib/mysql o la que tenga vuestra instalación.

Ahora llega el momento clave, tenemos que indicarle al esclavo quien es el servidor maestro y establacer la comunicación. Especificaremos el hostname del master, el usuario creado anteriormente para la replicación, su clave, el fichero de log binario a utilizar y su posición. Recordad que esto último lo hemos apuntado al hacer un SHOW MASTER STATUS;

mysql-slave> CHANGE MASTER TO
    ->     MASTER_HOST='192.168.1.130',
    ->     MASTER_USER='replicacion',
    ->     MASTER_PASSWORD='password',
    ->     MASTER_LOG_FILE='mysqld-bin.000002',
    ->     MASTER_LOG_POS=724514;
Query OK, 0 rows affected (0.04 sec)

Llego el momento de arrancar la replicación, para ello reactivamos el esclavo:

mysql-slave> START SLAVE;
Query OK, 0 rows affected (0.01 sec)

 

Revisar el estado de la replicación

Una vez puesta en marcha la replicación, deberían existir unos procesos concretos tanto en el master como el slave. En el master debería aparecer una conexión del servidor esclavo con el usuario usado en la replicación esperando a recibir actualizaciones a través del bin-log:

mysql-master> show processlist;
+----+-------------+-----------------+------+-------------+------+----------------------------------------------------------------+------------------+
| Id | User        | Host            | db   | Command     | Time | State                                                          | Info             |
+----+-------------+-----------------+------+-------------+------+----------------------------------------------------------------+------------------+
|  2 | replicacion | mysql-slave:59928 | NULL | Binlog Dump |    7 | Has sent all binlog to slave; waiting for binlog to be updated | NULL             |
|  3 | root        | localhost       | NULL | Query       |    0 | NULL                                                           | show processlist |
+----+-------------+-----------------+------+-------------+------+----------------------------------------------------------------+------------------+
2 rows in set (0.00 sec)

Y el slave debería tener dos conexiones relacionadas con la replicación, un thread SQL y otro de I/O relacionado con las lecturas de relay log:

mysql-slave> show processlist;
+----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------+------------------+
| Id | User        | Host      | db   | Command | Time | State                                                                 | Info             |
+----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------+------------------+
|  1 | system user |           | NULL | Connect |   11 | Waiting for master to send event                                      | NULL             |
|  2 | system user |           | NULL | Connect |   11 | Has read all relay log; waiting for the slave I/O thread to update it | NULL             |
|  4 | root        | localhost | NULL | Query   |    0 | NULL                                                                  | show processlist |
+----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------+------------------+

 

Monitorización de la replicación

Ahora que ya tenemos la replicación funcionando tenemos que conocer la forma de monitorizar su funcionamiento, el comando más importante lo podemos ejecutar en el esclavo (SHOW SLAVE STATUS), nos indicará el estado detallado de la replicación:

mysql-slave> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 192.168.1.130
                Master_User: replicacion
                Master_Port: 3306
              Connect_Retry: 60
            Master_Log_File: mysqld-bin.000002
        Read_Master_Log_Pos: 724601
             Relay_Log_File: mysqld-relay-bin.000002
              Relay_Log_Pos: 323
      Relay_Master_Log_File: mysqld-bin.000002
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB:
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 0
                 Last_Error:
               Skip_Counter: 0
        Exec_Master_Log_Pos: 724601
            Relay_Log_Space: 323
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: No
         Master_SSL_CA_File:
         Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
             Master_SSL_Key:
      Seconds_Behind_Master: 0
1 row in set (0.00 sec)

Los puntos más importantes:

  • Slave_IO_State: Monitoriza el estado del servidor esclavo respecto al master, en este caso está esperando a recibir eventos (actualizaciones del log binario por parte del master.
  • Master_log_File: Especifica el log binario en el servidor maestro que se está utilizando en la replicación.
  • Read_Master_Log_Pos: Indica la posición hasta la cual se ha leído del log binario del master, debería coincidir con la que nos da el SHOW MASTER STATUS en el servidor maestro y con el valor del parámetro Exec_Master_Log_Pos
  • Relay_Master_log_File: Especifica el log binario en el servidor esclavo que se está utilizando en la replicación.
  • Slave_IO_Running y Slave_SQL_Running: Monitorizan si los dos procesos mencionados antes están corriendo, los threads IO_THREAD y SQL_THREAD. Es crítico que ambos estén siempre corriendo.
  • Seconds_Behind_Master: Este parámetro puede ser un indicativo claro de problemas de carga, conectividad de red, etc. Indica el número de segundos de retardo respecto al servidor maestro. Lo ideal, 0.

 

Probando la replicación

A partir de aquí podemos verificar de una forma muy sencilla el funcionamiento de la replicación. Si añadimos, modificamos o borramos registros en una tabla de una base de datos del master automáticamente se replicará en la slave. Si creamos una base de datos en el master automáticamente se creará en el slave, etc:

mysql-master:

mysql-master> create database test01;
Query OK, 1 row affected (0.00 sec)

mysql-master> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| test               |
| test01             |
| world              |
+--------------------+
5 rows in set (0.00 sec)

Inmediatamente en el mysql-slave:

mysql-slave> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| test |
| test01 |
| world |
+--------------------+
5 rows in set (0.01 sec)

 

Bueno, de momento esto podría ser suficiente para una primera aproximación a la replicación maestro-esclavo de MySQL. En futuros artículos si os resulta interesante podemos indagar y detalladar algunas de las cosas tratadas en el artículo y/o revisar otros tipos de replicación, como la circular, la de doble sentido, etc.

mysqldump: Error 2013: Lost connection to MySQL server during query when dumping table

Este error se suele producir cuando hacemos un mysqldump de una base de datos MySQL con tablas de gran tamaño:

mysqldump: Error 2013: Lost connection to MySQL server during query when dumping table XXXX

Para solucionarlo, podemos aumentar temporalmente el tamaño máximo del buffer de comunicación de mysql a través de la variable max_allowed_packet. El valor se puede establecer en K, M ó G para indicar er kilobytes, megabytes, o gigabytes. Probad a aumentar el límite y verificar si el error queda solucionado, sino, es posible que tengáis que aumentarlo más:

$ mysqldump --opt --max_allowed_packet=128M base_de_datos > bd.sql

5 formas de encontrar y optimizar consultas MySQL problemáticas

MySQLA la hora de detectar problemas en servidores MySQL (cuellos de botella, uso excesivo de CPU, bajo rendimiento, lentitud, etc) es conveniente conocer las distintas opciones que nos ofrece MySQL para dar con el problema. Vamos a ver algunas de ellas:

Activar el Slow Query Log

Activar el log de consultas lentas en MySQL supondrá que en el momento que una consulta SQL supere el tiempo de ejecución establecido con la variable long_query_time quedará registrada en el log establecido. Para activarlo añadimos las siguientes directivas en el fichero de configuración my.cnf:

log-slow-queries=/var/log/mysql-slow-queries.log
long_query_time = 5

Este tema ya lo traté en un artículo anterior, para profundizar en ello acceded a la entrada a través del siguiente enlace: Slow Queries en MySQL.

Usar SHOW PROCESSLIST

Tanto desde la propia shell desde MySQL como usando mysqladmin podemos visualizar a tiempo real las consultas SQL que se están ejecutando en el servidor:

mysql> show processlist;
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host      | db   | Command | Time | State | Info             |
+----+------+-----------+------+---------+------+-------+------------------+
| 35 | root | localhost | NULL | Query   |    0 | NULL  | show processlist |
+----+------+-----------+------+---------+------+-------+------------------+
1 row in set (0.00 sec)

Como podéis observar, nos muestra el estado de la consulta (State), la base de datos, el comando que está ejecutando, el usuario, el host y el tiempo de ejecución, siendo este último de los puntos más importantes. Recordad que podéis formatear la salida con \G:

mysql> show processlist \G
*************************** 1. row ***************************
     Id: 35
   User: root
   Host: localhost
     db: NULL
Command: Query
   Time: 0
  State: NULL
   Info: show processlist
1 row in set (0.00 sec)

Y con mysqladmin desde la shell bash:

$ mysqladmin processlist -u root --pXXXX
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 42 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+

Si queréis ver el contenido completo de la consulta SQL que se está ejecutando probablemente necesitéis utilizar SHOW FULL PROCESSLIST; en lugar de SHOW PROCESSLIST:

mysql> SHOW FULL PROCESSLIST;
+----+------+-----------+------+---------+------+-------+-----------------------+
| Id | User | Host      | db   | Command | Time | State | Info                  |
+----+------+-----------+------+---------+------+-------+-----------------------+
| 35 | root | localhost | NULL | Query   |    0 | NULL  | SHOW FULL PROCESSLIST |
+----+------+-----------+------+---------+------+-------+-----------------------+
1 row in set (0.00 sec)

Optimizar las consultas SQL con EXPLAIN

Gracias a EXPLAIN podemos obtener toda la información sobre el modo en el que una consulta SQL se ejecutaría en el servidor. Es extremadamente útil para conocer la configuración de índices en las tablas, los índices que podrían ser configurados para mejorar su rendimiento, el número de filas que se revisan, el tipo de query, etc.

Aquí tenéis un ejemplo de EXPLAIN contra la tabla City de la base de datos de muestra de MySQL (world). Para profundizar en el funcionamiento de EXPLAIN os recomiendo revisar la documentación oficial de MySQL.

mysql> EXPLAIN select * from City;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | City  | ALL  | NULL          | NULL | NULL    | NULL | 4079 |       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
mysql> EXPLAIN select * from City \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: City
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4079
        Extra:
1 row in set (0.00 sec)

Activar el log general

Una medida quizás un poco agresiva y que no debería ser ejecutada en servidores de producción. Este log (General Query Log) registrará todas las consultas SQL que se ejecuten en el servidor. Para activarlo debemos especificar la ruta del log mediante –general_log_file= (MySQL 5.1.29+). También podemos activarlo y desactivarlo a tiempo real desde la shell MySQL del siguiente modo:

mysql> SET GLOBAL general_log = 'OFF';
mysql> SET GLOBAL general_log = 'ON';

MySQL Query Analyzer

Si pese a las opciones anteriores seguís sin poder detectar las consultas SQL problemáticas y optimizarlas, quizás MySQL Query Analyzer sea una opción factible. Es un software de pago perteneciente a la versión Enterprise que de forma gráfica monitoriza las consultas SQL del servidor y detecta el origen de los problemas. A mi entender, las otras opciones deberían ser suficiente (junto con buenos conocimientos de DBA todo sea dicho) para detectar y solucionar cualquier problema que se presente.

 

Como habréis podido observar, la combinación de estas opciones en distintos ordenes nos permitirá arreglar muchos problemas con consultas SQL, primero detectamos los problemas con los logs de slow_queries o SHOW PROCESSLIST y después optimizamos con EXPLAIN las consultas detectadas en los pasos anteriores.