systemadmin.es > DBA > Balanceador software (PHP) para MySQL: mysqlnd_ms

Balanceador software (PHP) para MySQL: mysqlnd_ms

Mediante la extensión mysqlnd_ms podemos automáticamente balancear las queries según sean de lectura o escritura entre el MySQL master y sus diferentes MySQL slaves. Vamos a ver cómo instalarla

En caso que no lo tengamos ya habilitado, deberemos recompilar PHP con mysqlnd habilitado para los diferentes métodos de acceso al MySQL. En el ejemplo lo usamos tanto para el clásico mysql (funciones llamadas mysql_), mysqli y para el PDO:

./configure' \
(...)
--enable-mysqlnd \
--with-mysql=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-mysqli=mysqlnd \
(...)

A continuación deberemos instalar el modulo mysqlnd_ms para poder usar dicho balanceador mediante pecl:

# pecl install mysqlnd_ms

El el fichero de configuración del php, deberemos añadir lo siguiente:

extension=mysqlnd_ms.so
mysqlnd_ms.enable=1
mysqlnd_ms.config_file=/var/www/mysqlndms.conf

El fichero de configuración (mysqlnd_ms.config_file) es opcional, pero resulta mucho más fácil usar dicho fichero que configurarlo dinámicamente mediante llamadas PHP.

Podemos verificar que esta cargado mediante php -ri:

# php --ri mysqlnd_ms

mysqlnd_ms

mysqlnd_ms support => enabled
Mysqlnd master/slave plugin version => 1.4.0-alpha (10400)
Plugin active => yes
Transaction mode trx_stickiness supported => no
mysqlnd_ms_get_last_used_connection() supported => no
mysqlnd_ms_set_qos() supported => no
Table partitioning filter supported => no
Query caching through mysqlnd_qc supported => no

Directive => Local Value => Master Value
mysqlnd_ms.enable => On => On
mysqlnd_ms.force_config_usage => 0 => 0
mysqlnd_ms.config_file => /var/www/systemadmin.es/mysqlconfig.ini => /var/www/systemadmin.es/mysqlconfig.ini
mysqlnd_ms.collect_statistics => 0 => 0
mysqlnd_ms.multi_master => 0 => 0
mysqlnd_ms.disable_rw_split => 0 => 0

Los parámetros del fichero de configuración lo podemos consultar en la documentación de mysqlnd_ms

A grandes rasgos, suponiendo un fichero de configuración como el siguiente:

{
    "ndtest": {
        "master": {
            "master0": {
                "host": "192.168.56.1",
                "port": "3306"
            }
        },
        "slave": {
            "slave0": {
                "host": "192.168.56.2",
                "port": "3306"
            }
        }
    }
}

Mandará todas las queries de escritura (updates, inserts…) sobre el master y todas las de lectura sobre el slave (ninguna lectura sobre el master)

En el código PHP para conectar simplemente deberemos indicar el host como ndtest como esta definido en dicho fichero. Por lo tanto para los diferentes métodos de acceso a la base de datos haríamos:

$pdo = new PDO('mysql:host=ndtest;dbname=jordi', 'jordi', 'jordi');
$mysql = mysql_connect("ndtest","jordi","jordi");
$mysqli = new mysqli("ndtest","jordi","jordi","jordi");

Unos ejemplos completos serían:

  • PDO-mysql:
    <?php
    
    $pdo = new PDO('mysql:host=ndtest;dbname=jordi', 'jordi', 'jordi');
    $results=$pdo->query("select @@read_only");
    print_r($results->fetchAll());
    
    ?>
    
  • mysql_*:
    <?php
    
    $mysql = mysql_connect("ndtest","jordi","jordi");
    mysql_select_db("jordi", $mysql);
    $result = mysql_query("select @@read_only", $mysql);
    print_r(mysql_fetch_array ($result));
    
    ?>
    
  • mysqli:
    <?php
    
    $mysqli = new mysqli("ndtest","jordi","jordi","jordi");
    $result=$mysqli->query("select @@read_only");
    print_r($result->fetch_array());
    
    ?>
    

Dicha configuración básica dista mucho de poder ser usada en producción ya que deberemos tener en cuenta otros casos:

En el caso que se haga una escritura, nos interesa que las queries (lecturas) a continuación se hagan sobre el master para evitar que por el retraso a aplicarse en el slave no veamos los cambios que acabamos de hacer, para ello deberemos añadir la opción:

"master_on_write": 1

Por defecto ya esta configurado que las conexiones se establezcan cuando se necesiten, pero es buena práctica forzar ficha configuración que nos interesa aunque ya sea el por defecto:

"lazy_connections": 1,

Si movemos todas las lecturas al slave, el master va a quedar desaprovechado, por lo que para repartir lecturas entre el master y el slave deberemos añadir el master como un slave más. Por lo tanto, una configuración más realista sería como la siguiente:

{
    "ndtest": {
        "master": {
            "master0": {
                "host": "192.168.56.1",
                "port": "3306"
            }
        },
        "slave": {
            "slave0": {
                "host": "192.168.56.2",
                "port": "3306"
            },
            "slave1": {
                "host": "192.168.56.1",
                "port": "3306"
        }
        }
    },
    "lazy_connections": 1,
    "master_on_write": 1
}

Con dicha configuración no hay estrategia de failover, en caso que un slave falle, la aplicación deberá gestionar el fallo (reintentado o bien informando al usuario). Podemos configurar una estrategia de failover de acuerdo con la documentación:

  • disabled: Por defecto, sin estrategia de failover
  • master: En caso de fallo, se consulta en el master
  • loop_before_master: Se prueban el resto de slave y sino se hace sobre el master

Un ejemplo sería:

{
    "ndtest": {
        "master": {
            "master0": {
                "host": "192.168.56.1",
                "port": "3306"
            }
        },
        "slave": {
            "slave0": {
                "host": "192.168.56.2",
                "port": "3306"
        },
        "slave1": {
                "host": "192.168.56.3",
                "port": "3306"
        }
        },
    "failover": {"strategy": "loop_before_master" }
    },
    "lazy_connections": 1,
    "master_on_write": 1
}

Si falla, saltará un warning, pero la query se ejecutará en el siguiente, por ejemplo:

Warning: mysqli::query() [mysqli.query]: [2002] Connection refused (trying to connect via tcp://192.168.56.2:3306) in /var/www/html/index.php on line 22

2 comments to “Balanceador software (PHP) para MySQL: mysqlnd_ms”

  1. Échale un vistazo a MySQL Fabric, tiene parte en el conector, pero también parte de middleware, por lo que soluciona el gran problema de HA a nivel de conector (consistencia si tienes más de 1 nodo de aplicación, haciendo la lógica del failover común a todos los nodos). Además, incluye la posibilidad de sharding semi-automático.

    Está todavía un poco verde, pero creo que es el futuro- más que mysqlnd_ms- sobre todo si se confirma que MySQL 5.7 traerá replicación multi-master, write-anyware síncrona.

  2. Tengo pendiente de hacerlo, pero de todas formas atacan al problema desde diferentes perspectivas, por lo que pueden llegar a ser complementarios

    saludos,

Deja un comentario:

XHTML - Tags permitidos:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>