ProxySQL es un proxy para MySQL que entiende el protocolo y las consultas: mantiene un pool de conexiones hacia los servidores, enruta cada consulta según reglas y detecta qué servidor es el primario y cuáles son réplicas. En este tutorial instalarás ProxySQL en Ubuntu 24.04 delante de un primario MySQL con dos réplicas, enviarás las escrituras al primario y las lecturas a las réplicas, y verificarás el enrutamiento con las tablas de estadísticas de ProxySQL, todo sin cambiar el código de la aplicación.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS para ProxySQL, por ejemplo un VPS de CubePath. Puede ser el mismo servidor de la aplicación.
- Replicación MySQL funcionando con un primario y al menos una réplica, con
read_only = ONen las réplicas. En esta guía se usan estas direcciones privadas:
| Rol | IP privada |
|---|---|
| Primario | 10.0.0.21 |
| Réplica 1 | 10.0.0.22 |
| Réplica 2 | 10.0.0.23 |
- El puerto 3306 de los servidores MySQL accesible desde el servidor de ProxySQL.
- Un usuario no root con privilegios
sudo.
Paso 1: Crear los usuarios en MySQL
ProxySQL necesita dos usuarios en MySQL: uno de monitorización, con el que comprueba cada pocos segundos si un servidor responde y si tiene read_only activo, y el usuario de la aplicación. Créalos en el primario; la replicación los copiará a las réplicas:
sudo mysql
CREATE USER 'monitor'@'10.0.0.%' IDENTIFIED WITH mysql_native_password BY 'your_monitor_password';
GRANT USAGE, REPLICATION CLIENT ON *.* TO 'monitor'@'10.0.0.%';
CREATE DATABASE IF NOT EXISTS appdb;
CREATE USER 'appuser'@'10.0.0.%' IDENTIFIED WITH mysql_native_password BY 'your_app_password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'10.0.0.%';
EXIT;
Sustituye las contraseñas por valores propios. Se usa mysql_native_password porque es el método de autenticación con el que ProxySQL tiene mejor compatibilidad hacia los servidores. Está disponible en MySQL 8.0, la versión de Ubuntu 24.04; en MySQL 8.4 y posteriores viene desactivado y tendrías que habilitarlo o usar caching_sha2_password con TLS.
Paso 2: Instalar ProxySQL desde el repositorio oficial
Descarga la clave del repositorio de ProxySQL para la rama 2.7.x:
sudo apt update
sudo apt install curl lsb-release mysql-client
sudo curl -fsSL -o /etc/apt/keyrings/proxysql.asc https://repo.proxysql.com/ProxySQL/proxysql-2.7.x/repo_pub_key
Añade el repositorio:
echo "deb [signed-by=/etc/apt/keyrings/proxysql.asc] https://repo.proxysql.com/ProxySQL/proxysql-2.7.x/$(lsb_release -sc)/ ./" | sudo tee /etc/apt/sources.list.d/proxysql.list
Instala ProxySQL y arranca el servicio:
sudo apt update
sudo apt install proxysql
sudo systemctl enable --now proxysql
Comprueba que está escuchando. El puerto 6032 es la interfaz de administración y el 6033 el que usarán las aplicaciones:
sudo ss -ltnp | grep proxysql
LISTEN 0 1024 0.0.0.0:6032 0.0.0.0:* users:(("proxysql",pid=2211,fd=31))
LISTEN 0 1024 0.0.0.0:6033 0.0.0.0:* users:(("proxysql",pid=2211,fd=26))
Paso 3: Entender cómo guarda ProxySQL la configuración
ProxySQL no se configura editando un archivo. /etc/proxysql.cnf solo se lee la primera vez que arranca; a partir de entonces la configuración vive en una base de datos SQLite en /var/lib/proxysql y se modifica con SQL desde la interfaz de administración. Hay tres capas:
- MEMORY: donde editas con
INSERTyUPDATE. Los cambios aún no tienen efecto. - RUNTIME: la configuración activa. Pasas a ella con
LOAD ... TO RUNTIME. - DISK: la persistencia. Guardas con
SAVE ... TO DISKpara que sobreviva a un reinicio.
Por eso, en los pasos siguientes cada bloque termina con un LOAD y un SAVE.
Paso 4: Cambiar la contraseña de administración
Conéctate a la interfaz de administración con las credenciales por defecto, admin/admin, que solo se aceptan desde localhost:
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQLAdmin> '
Cámbialas de inmediato:
UPDATE global_variables SET variable_value = 'admin:your_admin_password' WHERE variable_name = 'admin-admin_credentials';
LOAD ADMIN VARIABLES TO RUNTIME;
SAVE ADMIN VARIABLES TO DISK;
A partir de ahora conéctate con -padmin sustituido por tu nueva contraseña. Mantén el puerto 6032 cerrado al exterior en el firewall.
Paso 5: Registrar los servidores y los hostgroups
ProxySQL agrupa los servidores en hostgroups. Usarás el hostgroup 10 para escrituras y el 20 para lecturas. Configura primero el usuario de monitorización:
UPDATE global_variables SET variable_value = 'monitor' WHERE variable_name = 'mysql-monitor_username';
UPDATE global_variables SET variable_value = 'your_monitor_password' WHERE variable_name = 'mysql-monitor_password';
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
Indica a ProxySQL qué pareja de hostgroups forma una replicación. Con esta tabla, ProxySQL mira read_only en cada servidor y lo coloca automáticamente en el hostgroup 10 si es OFF o en el 20 si es ON:
INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment) VALUES (10, 20, 'cluster app');
Añade los tres servidores. Basta con ponerlos todos en el hostgroup de lectura: el monitor moverá el primario al de escritura:
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '10.0.0.21', 3306);
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '10.0.0.22', 3306);
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '10.0.0.23', 3306);
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
Espera unos segundos y comprueba cómo han quedado:
SELECT hostgroup_id, hostname, status FROM runtime_mysql_servers ORDER BY hostgroup_id, hostname;
+--------------+-----------+--------+
| hostgroup_id | hostname | status |
+--------------+-----------+--------+
| 10 | 10.0.0.21 | ONLINE |
| 20 | 10.0.0.21 | ONLINE |
| 20 | 10.0.0.22 | ONLINE |
| 20 | 10.0.0.23 | ONLINE |
+--------------+-----------+--------+
El primario aparece en ambos hostgroups porque, por defecto, el escritor también atiende lecturas (variable mysql-monitor_writer_is_also_reader). Si quieres que las lecturas vayan solo a las réplicas, pon esa variable a false y vuelve a cargar las variables y los servidores.
Verifica también que el monitor puede leer read_only sin errores:
SELECT hostname, read_only, error FROM monitor.mysql_server_read_only_log ORDER BY time_start_us DESC LIMIT 3;
+-----------+-----------+-------+
| hostname | read_only | error |
+-----------+-----------+-------+
| 10.0.0.23 | 1 | NULL |
| 10.0.0.22 | 1 | NULL |
| 10.0.0.21 | 0 | NULL |
+-----------+-----------+-------+
Si la columna error muestra Access denied, revisa el usuario y la contraseña de monitorización.
Paso 6: Añadir el usuario de la aplicación
Las aplicaciones se autentican contra ProxySQL, que luego se conecta a MySQL con las mismas credenciales. Registra el usuario con el hostgroup de escritura como destino por defecto, de modo que toda consulta que no coincida con una regla vaya al primario:
INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('appuser', 'your_app_password', 10);
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;
La contraseña debe ser la misma que definiste en MySQL en el paso 1.
Paso 7: Separar lecturas y escrituras con reglas de consulta
Las reglas se evalúan en orden de rule_id. La primera envía los SELECT ... FOR UPDATE al primario, porque bloquean filas y deben ir dentro de la transacción de escritura. La segunda envía el resto de SELECT a las réplicas:
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (1, 1, '^SELECT.*FOR UPDATE', 10, 1);
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (2, 1, '^SELECT', 20, 1);
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
INSERT, UPDATE, DELETE y cualquier otra sentencia no coinciden con ninguna regla y van al default_hostgroup del usuario, el 10. Dentro de una transacción explícita, ProxySQL mantiene todas las consultas en el mismo servidor (transaction_persistent está activado por defecto en mysql_users), así que un SELECT después de un UPDATE en la misma transacción no se escapa a una réplica.
Sal de la interfaz de administración con EXIT.
Paso 8: Probar el enrutamiento
Conéctate como lo haría la aplicación, al puerto 6033. Ejecuta varias veces una lectura que devuelva el nombre del servidor:
for i in 1 2 3 4; do mysql -u appuser -p'your_app_password' -h 127.0.0.1 -P 6033 -N -e "SELECT @@hostname;"; done
mysql-replica-2
mysql-replica-1
mysql-primary
mysql-replica-2
Las lecturas se reparten entre los servidores del hostgroup 20. Ahora haz una escritura:
mysql -u appuser -p'your_app_password' -h 127.0.0.1 -P 6033 appdb -e "CREATE TABLE IF NOT EXISTS t (id INT PRIMARY KEY); INSERT INTO t VALUES (1);"
Vuelve a la interfaz de administración y consulta las estadísticas por patrón de consulta:
SELECT hostgroup, digest_text, count_star FROM stats_mysql_query_digest ORDER BY count_star DESC LIMIT 5;
+-----------+---------------------------------------------------+------------+
| hostgroup | digest_text | count_star |
+-----------+---------------------------------------------------+------------+
| 20 | SELECT @@hostname | 4 |
| 10 | CREATE TABLE IF NOT EXISTS t (id INT PRIMARY KEY) | 1 |
| 10 | INSERT INTO t VALUES (?) | 1 |
+-----------+---------------------------------------------------+------------+
El SELECT fue al hostgroup 20 y las escrituras al 10. Esta tabla es también la herramienta para encontrar las consultas más frecuentes y lentas de tu aplicación.
Paso 9: Ajustar el reparto de carga y el retraso de réplica
El peso (weight) de cada servidor controla la proporción de tráfico que recibe dentro de su hostgroup, y max_replication_lag saca temporalmente de rotación a una réplica que va demasiado retrasada. Por ejemplo, para enviar el doble de lecturas a la réplica 1 y apartar cualquier réplica con más de 10 segundos de retraso:
UPDATE mysql_servers SET weight = 2000 WHERE hostname = '10.0.0.22' AND hostgroup_id = 20;
UPDATE mysql_servers SET max_replication_lag = 10 WHERE hostgroup_id = 20;
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
El peso por defecto es 1000. Una réplica apartada por retraso aparece con estado SHUNNED en runtime_mysql_servers y vuelve a ONLINE cuando se pone al día. Puedes ver cómo se reparten las conexiones y consultas por servidor en stats_mysql_connection_pool:
SELECT hostgroup, srv_host, status, ConnUsed, ConnFree, Queries FROM stats_mysql_connection_pool;
Solución de problemas
- Todos los servidores quedan en el hostgroup 20: el primario tiene
read_only = ONo el monitor no puede conectarse. Revisamonitor.mysql_server_read_only_logymonitor.mysql_server_connect_log. Access deniedal conectar por el puerto 6033: el usuario no existe enmysql_users, faltaLOAD MYSQL USERS TO RUNTIMEo la contraseña no coincide con la de MySQL.- Los cambios desaparecen al reiniciar: faltó el
SAVE ... TO DISKcorrespondiente. - Editaste
/etc/proxysql.cnfy no pasa nada: es lo esperado, ese archivo solo se usa si no existe la base de datos en/var/lib/proxysql. Haz los cambios desde la interfaz de administración. - Lecturas que no ven datos recién escritos: es el retraso de replicación. Envía esas lecturas concretas al primario con una regla más específica o dentro de una transacción.
Conclusión
Tienes ProxySQL en Ubuntu 24.04 separando lecturas y escrituras entre un primario y dos réplicas MySQL, detectando los roles por read_only y apartando réplicas retrasadas, con estadísticas para verificar a dónde va cada consulta. Como siguientes pasos, puedes montar un segundo ProxySQL y sincronizarlos con ProxySQL Cluster, activar la caché de consultas con cache_ttl en reglas concretas y exportar las métricas al sistema de monitorización que ya uses.
