Nivel de servicio y consistencia

Nota: Requisitos de versión

Los niveles de servicios han sido introducidos en la versión 1.2.0-alpha de PECL mysqlnd_ms. mysqlnd_ms_set_qos() está disponible con PHP 5.4.0 o superior.

Las diferentes soluciones de clúster MySQL ofrecen diferentes niveles de servicios y de consistencia de datos para sus usuarios. Un clúster de replicación MySQL asíncrono ofrece la consistencia final de forma predeterminada. Una lectura ejecutada en un esclavo asíncrono puede devolver datos actuales, antiguos, o ningún dato, dependiendo de si el esclavo ha replicado todos los conjuntos de cambios del maestro.

Las aplicaciones que utilizan un clúster de replicación MySQL necesitan ser diseñadas para que funcionen correctamente con datos de consistencia final. En algunos casos, sin embargo, los datos antiguos no son aceptables. En estos casos solamente son permitidos ciertos accesos a esclavos o incluso a un maestro para realizar la calidad de servicio requerida del clúster.

A partir de PECL mysqlnd_ms 1.2.0, el complemento puede seleccionar automáticamente nodos de replicación de MySQL que proporcionen consistencia de sesión o consistencia fuerte. La consistencia de sesión significa que un cliente puede leer sus escrituras. Otros clientes pueden ver o no la escritura del cliente. La consistencia fuerte significa que todos los clientes verán todas las escrituras del cliente.

Ejemplo #1 Consistencia de sesión: lectura de sus escrituras

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost",
                "socket": "\/tmp\/mysql.sock"
            }
        },
        "slave": {
            "slave_0": {
                "host": "127.0.0.1",
                "port": "3306"
            }
        }
    }
}

Ejemplo #2 Solicitud de consistencia de sesión

<?php
$mysqli 
= new mysqli("myapp""nombre_usuario""contraseña""base_datos");
if (!
$mysqli) {
    
/* Por supuesto, su manejo de errores es mejor... */
    
die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));
}

/* división de lecutura-escritura: se usa el maestro */
if (!$mysqli->query("INSERT INTO orders(order_id, item) VALUES (1, 'christmas tree, 1.8m')")) {
    
/* Por favor, use un manejo de errores mejor en su código */
    
die(sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* Solicitud de consistencia de sesión: lectura de sus escrituras */
if (!mysqlnd_ms_set_qos($mysqliMYSQLND_MS_QOS_CONSISTENCY_SESSION)) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* El complemento selecciona un nodo que posee los cambios, aquí: el maestro */
if (!$res $mysqli->query("SELECT item FROM orders WHERE order_id = 1")) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

var_dump($res->fetch_assoc());

/* Volver a la consistencia final: se permiten datos antiguos */
if (!mysqlnd_ms_set_qos($mysqliMYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* El complemento seleccina cualquier esclavo, se permiten datos antiguos */
if (!$res $mysqli->query("SELECT item, price FROM specials")) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}
?>

Los niveles de servicios se pueden establecer en el fichero de configuración del complemento y en tiempo de ejecución usando mysqlnd_ms_set_qos(). En el ejemplo, la función se usa para forzar la consistencia de sesión (lectura de sus escrituras) para todas las sentencias futuras hasta próximo aviso. La sentencia SELECT de la tabla orders se ejecuta en el maestro para asegurarse de que las escrituras anteriores puedan ser vistas por el cliente. La lógica de la división de lectura-escritura ha sido adaptada para satisfacer el nivel de servicio.

Después de que una aplicación haya leído sus cambios desde la tabla orders, vuelve al nivel de servicio predeterminado, que es la consistencia final. Ésta no pone restricciones al elegir un nodo para la ejecución de sentencias. Por lo tanto, la sentencia SELECT de la tabla specials se ejecuta en un esclavo.

La nueva funcionalidad sustituye el uso de sugerencias SQL y la opción de configuración master_on_write. En muchos casos, mysqlnd_ms_set_qos() es más fácil de usar, más potente y mejora la portabilidad.

Ejemplo #3 Edad máxima/demora del esclavo

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost",
                "socket": "\/tmp\/mysql.sock"
            }
        },
        "slave": {
            "slave_0": {
                "host": "127.0.0.1",
                "port": "3306"
            }
        },
        "failover" : "master"
    }
}

Ejemplo #4 Limitar la demora del esclavo

<?php
$mysqli 
= new mysqli("myapp""nombre_usuario""contraseña""base_datos");
if (!
$mysqli) {
    
/* Por supuesto, su manejo de errores es mejor... */
    
die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));
}

/* Demora de la lectura desde los esclavos de no más de cuatro segundos */
$ret mysqlnd_ms_set_qos(
    
$mysqli,
    
MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL,
    
MYSQLND_MS_QOS_OPTION_AGE,
    
4
);

if (!
$ret) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* El comlemento elige cualquier esclavo, el cual puede o no poseer los cambios */
if (!$res $mysqli->query("SELECT item, price FROM daytrade")) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* Volver a lo predeterminado: usar todos los esclavos y maestros permitidos */
if (!mysqlnd_ms_set_qos($mysqliMYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}
?>

El nivel de servicio de consistencia final se puede usar con un parámetro opcional para establecer la demora máxima de los esclavos elegidos. Si se establece, el complemento comprueba SHOW SLAVE STATUS para todos los esclavos configurados. En el caso del ejemplo, solamente los esclavos para los que Slave_IO_Running=Yes, Slave_SQL_Running=Yes y Seconds_Behind_Master <= 4 sea verdadero, son tenidos en cuenta para la ejecución de la sentencia SELECT item, price FROM daytrade.

La comprobacion de SHOW SLAVE STATUS se realiza de manera transparente desde la perspectiva de las aplicaciones. Los errores, si los hubiera, son notificados como advertencias. No se establecerá ningún error en el gestor de conexión. Incluso si todas las sentencias SQL SHOW SLAVE STATUS ejecutadas por el complemento fallan, la ejecución de las sentencias del usuario no se detienen, dado que la toleracia a fallos del maestro está habilitada. Por lo tanto, no se requieren cambios en la aplicación.

Nota: Operaciones caras y lentas

La comprobación de SHOW SLAVE STATUS para todos los esclavos añade carga adicional a la aplicación. Es una operación en segundo plano cara y lenta. Intente minimizar su uso. Desafortunadamente, un clúster de replicación MySQL no proporciona a los clientes la posibilidad de solicitar una lista de candidatos desde una instancia central. En consecuencia, no existe una manera más eficiente de comprobar la demora de los esclavos.

Por favor, observe las limitaciones y propiedades de SHOW SLAVE STATUS tal y como están explicadas en el manual de referencia de MySQL.

Para evitar que mysqlnd_ms emita una advertencia si no se pudieron encontrar esclavos que se demoren no más que el número de segundos definido con respecto al maestro, es necesario habilitar la tolerancia a fallos del maestro en el fichero de configuración del complemento. Si no se pudieron encontrar esclavos y la tolerancia a fallos está habilitada, el complemento eligirá un maestro para ejecutar la setencia.

Si no se pudieron encontrar esclavos y la tolerancia a fallos está deshabilitada, el complemento emitirá una advertencia, no ejecutará la sentencia y establecerá un error sobre la conexión.

Ejemplo #5 La tolerancia a fallos no está establecida

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost",
                "socket": "\/tmp\/mysql.sock"
            }
        },
        "slave": {
            "slave_0": {
                "host": "127.0.0.1",
                "port": "3306"
            }
        }
    }
}

Ejemplo #6 No hay esclavos dentro del límite de tiempo

<?php
$mysqli 
= new mysqli("myapp""nombre_usuario""contraseña""base_datos");
if (!
$mysqli) {
    
/* Por supuesto, su manejo de errores es mejor... */
    
die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));
}

/* Demora de la lectura desde los esclavos de no más de cuatro segundos */
$ret mysqlnd_ms_set_qos(
    
$mysqli,
    
MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL,
    
MYSQLND_MS_QOS_OPTION_AGE,
    
4
);

if (!
$ret) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}

/* El comlemento elige cualquier esclavo, el cual puede o no poseer los cambios */
if (!$res $mysqli->query("SELECT item, price FROM daytrade")) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}


/* Volver a lo predeterminado: usar todos los esclavos y maestros permitidos */
if (!mysqlnd_ms_set_qos($mysqliMYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) {
    die(
sprintf("[%d] %s\n"$mysqli->errno$mysqli->error));
}
?>

El resultado del ejemplo sería:

PHP Warning:  mysqli::query(): (mysqlnd_ms) Couldn't find the appropriate slave connection. 0 slaves to choose from. Something is wrong in %s on line %d
PHP Warning:  mysqli::query(): (mysqlnd_ms) No connection selected by the last filter in %s on line %d
[2000] (mysqlnd_ms) No connection selected by the last filter