Guía rápida y ejemplos El complemento de equilibrado de carga y replicación de mysqlnd es fácil de usar. Esta guía rápida mostrará los casos de uso típicos, y proporcionará consejos sobre cómo empezar a utilizarlo. Se recomienda leer las secciones de referencia además de esta guía rápida. Aquí se intenta evitar tratar con conceptos teóricos y limitaciones. En su lugar, se enlazará con las secciones de referencia. Es seguro comenzar con esta guía rápida. Sin embargo, antes de usar el complemento en entornos de objetivos críticos, le animamos a que lea también la información de fondo de las secciones de referencia. El enfoque es usar mysqlnd_ms de PECL para trabajar con clústeres MySQL asíncronos, a saber, la replicación MySQL. Generalmente hablando, un clúster asíncrono es más difícil de usar que uno sincrónico. Por lo tanto, los usuarios de, por ejemplo, el Clúster MySQL, encontrarán más información de la necesaria.
Puesta en marcha El complemento está implementado como una extensión de PHP. Vea también las instrucciones de instalación para instalar la extenxión PECL/mysqlnd_ms. Compile o configure la extensión (API) de MySQL para PHP (mysqli, PDO_MYSQL, mysql) que planee usar con soporte para la biblioteca mysqlnd. PECL/mysqlnd_ms es un complemento para la biblioteca mysqlnd. Para utilizar el complemento con cualqier extensión de MySQL para PHP, la extensión ha de utilizar la biblioteca mysqlnd. Después carque la extensión en PHP y active el complemento en el fichero de confuguración de PHP con la directiva de configuración llamada mysqlnd_ms.enable. Habilitar el complemento (php.ini) El complemento utiliza su propio fichero de configuración. Use la directiva de configuración de PHP mysqlnd_ms.config_file para establecer la ruta completa al fichero de configuración específico del complemento. Este fichero debe ser legible por PHP (p.ej., el usuario del servidor web). Por favor, observe que la directiva de configuración mysqlnd_ms.config_file sustituye a mysqlnd_ms.ini_file desde la versión 1.4.0. Es un error común usar la directiva de configuración antigua, la cual ya no está disponible. Cree un fichero de configuración específico del complemento. Guarde el fichero en la ruta establecida por la directiva de configuración de PHP mysqlnd_ms.config_file. El fichero de configuración del complemento está basado en JSON. Está dividido en una o más secciones. Cada sección tiene un nombre, por ejemplo, myapp. Cada sección contiene su propio conjunto de ajustes de configuración. Una sección debe incluir, como mínimo, el servidor maestro de replicación MySQL y establecer una lista de esclavos. El complemento admite únicamente un servidor maestro por sección. La configuración de replicación MySQL de múltiples maestros aún no está totalemnte soportada. Use el ajuste de configuración master para establecer el nombre del anfitrión y el puerto o socket del servidor maestro de MySQL. Los servidores esclavos de MySQL se configuran usando la palabra clave slave. Fichero de configuración mínima específico del complemento (mysqlnd_ms_plugin.ini) Es necesario configurar una lista de servidores esclavos de MySQL, aunque puede contener una lista vacía. Se recomienda que siempre se configure al menos un servidor esclavo. Las listas de servidores pueden usar la sintaxis anónima o no anónima. Las listas no anónimas incluyen sobrenombres para los servidores, tales como master_0 para el maestro del ejemplo de arriba. Esta guía rápida utiliza la sintaxis no anónima más prolija. Configuración mínima recomendada específica del complemento (mysqlnd_ms_plugin.ini) Si al menos existen dos servidores en total, el complemento puede empezar a equilibrar la carga e intercambiar conexiones. El intercambio de conexiones no siempre es transparente y puede ocasionar problemas en ciertos casos. Las secciones de referencia sobre intercambio y agrupación de conexiones, manejo de transacciones, tolerancia a fallos equilibrado de carga y división de lectura-escritura proporcionan más detalles. Los problemas potenciales están descritos más tarde en esta guía. Es responsabilidad de la aplicación manejar los problemas potenciales causados por el intercambio de conexiones, configurando un servidor maestro con al menos un servidor esclavo, lo cual permite el intercambio para trabajar con los problemas relacionados que se puedan encontrar. Los servidores maestros y esclavos de MySQL que se configuren no necesitan ser parte de la configuración de replicación MySQL. Para realizar pruebas, se puede usar un único servidor MySQL y hacerle saber al complemento que se trata de un servidor maestro y esclavo, como se muestra abajo. Esto podría ser de ayuda para detectar muchos problemas potenciales con los intercambios de conexión. Sin embargo, tal configuración no será propensa a los problemas causados por la demora de replicación. Usar un servidor como maestro y esclavo (¡solamente para pruebas!) El complemento intenta notificarle de configuraciones inválidas. Desde 1.5.0, lanzará una advertencia durante el arranque de PHP si no se puede leer el fichero de configuración, está vacío, o falló el análisis de JSON. Según la configuración de PHP, estos errores podrían aparecer únicamente en algunos ficheros log. Se hace una validación extra cuando se establece una conexión y se buscan secciones válidas en el fichero de configuración. Establecer mysqlnd_ms.force_config_usage podría ayudar a depurar una configuración incorrecta. Véanse también las notas de depuración del fichero de configuración.
Ejecutar sentencias El complemento se puede usar con cualquier extensión de MySQL para PHP (mysqli, mysql, y PDO_MYSQL) que esté compilada para utilizar la biblioteca mysqlnd. PECL/mysqlnd_ms se acopla a la biblioteca mysqlnd. No cambia la API o el comportamiento de estas extensiones. Siempre que se esté abriendo una conexión a MySQL, el complemento compara el valor del parámetro 'host' de la llamada de conexión con los nombres de las secciones del fichero de configuración específico del complemento. Si, por ejemplo, el fichero de configuración específico del complemento tiene una sección myapp , se ha de hacer referencia a ésta para abrir una conexión a MySQL para el anfitrión myapp Fichero de configuración específico del complemento (mysqlnd_ms_plugin.ini) Abrir una conexión con equilibrado de carga ]]> Los ejemplos de conexión de arriba tendrán equilibrio de carga. El complemento enviará sentencias de sólo lectura al servidor esclavo de MySQL con la IP 192.168.2.27 y escuchará el puerto 3306 para la conexión cliente de MySQL. Todas las demás sentencias serán dirigidas al servidor maestro de MySQL que se ejecuta en el anfitrión localhost. Si se ejecuta en sistemas operativos de tipo Unix, el maestro sobre localhost aceptará conexiones cliente de MySQL en el socket de dominio Unix /tmp/mysql.sock, mientras que TCP/IP es el puerto predeterminado en Windows. El complemento usará el nombre de usuario nombre_usuario y la contraseña contraseña para conectarse a cualquier servidor de MySQL incluido en la sección myapp del fichero de configuración del complemento. Una vez realizada la conexión, el complemento seleccionará base_datos como el esquema actual. El nombre de usuario, la contraseña y el nombre del esquema son tomados de las llamadas a la API de conexión y usados por todos los servidores. En otras palabras: se deben usar los mismos nombre de usuario y contraseña para cada servidor MySQL incluido en una sección del fichero de configuración del comlemento. No es una limitación generalizada. A partir de PECL/mysqlnd_ms 1.1.0, es posible establecer username y password para cualquier servidor del fichero de configuración del complemento, para utilizarlos en lugar de las credenciales pasadas a la llamada a la API. El complemento no cambia la API para ejecutar sentencias. La división de lectura-escritura se desarrolla aparte. El siguiente ejemplo asume que no existe una demora de replicación significante entre el maestro y el esclavo. Ejecutar sentencias query("DROP TABLE IF EXISTS test")) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } if (!$mysqli->query("CREATE TABLE test(id INT)")) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } /* solo lectura: la sentencia se ejecutará en un esclavo */ if (!($res = $mysqli->query("SELECT id FROM test"))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } else { $fila = $res->fetch_assoc(); $res->close(); printf("El esclavó devuelve el ID = '%s'\n", $fila['id']; } $mysqli->close(); ?> ]]> &example.outputs.similar;
Estado de la conexión El complemento cambia la semántica de un gestor de conexión de MySQL para PHP. Un gestor de conexión representa una agrupación de conexiones, en lugar de una única conexión de red cliente-servidor de MySQL. La agrupación de conexiones consiste en una conexión maestra, y opcionalmente cualquier número de conexiones esclavas. Cada conexión de la agrupación de conexiones tiene su propio estado. Por ejemplo, las variables SQL de usuario, las tablas temporales y las transacciones, son parte del estado. Se puede encontrar un listado completo con los elementos que pertenecen al estado de una conexión en los conceptos de la documentación de agrupación e intercambio de conexiones. Si el complemento decide intercambiar conexiones para equilibrar la carga, a la aplicación se le podría proporcionar una conexión que tuviera un estado diferente. Las aplicaciones deben considerar esto. Configuración del complemento con un esclavo y un maestro Problema: estado de conexión y variables SQL de usuario query("SET @myrole='master'")) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } /* Conexión 2, se ejecuta en el esclavo a causa de SELECT */ if (!($res = $mysqli->query("SELECT @myrole AS _role"))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } else { $fila = $res->fetch_assoc(); $res->close(); printf("@myrole = '%s'\n", $fila['_role']); } $mysqli->close(); ?> ]]> &example.outputs; El ejemplo abre una conexión con equilibrio de carga y ejecuta dos sentencias. La primera sentencia SET @myrole='master' no comienza con la cadena SELECT. Por lo tanto, el complemento no la reconoce como una consulta de solo lectura que se ejecutaría en un esclavo. El complemento ejecuta la sentencia en una conexión al maestro. La sentencia establece una variable SQL de usuario que está vinculada a la conexión con el maestro. El estado de la conexión maestra ha sido cambiado. La siguiente sentencia es SELECT @myrole AS _role. El complemento la reconoce como una consulta de solo lectura y la envía al esclavo. La sentencia se ejecuta en una conexión al esclavo. Esta segunda conexión no tiene ninguna variable SQL de usuario vinculada a ella. Tiene un estado diferente a la primera conexión al maestro. La variable SQL de usuario no está establecida. El script del ejemplo imprime @myrole = ''. Es responsabilidad del desarrollador de la apliación ocuparse del estado de la conexión. El complemento no monitoriza todas las actividades de cambio de estado. La monitorización de todos los casos posibles sería una tarea intensiva de la CPU, si es que esta tarea pudiera ser realizada. Los problemas se pueden evitar fácilmente usando sugerencias SQL.
Sugerencias SQL Las sugerencias SQL pueden forzar a una consulta a elegir un servidor específico de la agrupación de conexiones. Proporcionan al complemento una recomendación para utilizar un servidor indicado, lo que puede resolver problemas causados por intercambios de conexión y estados de conexión. Las sugerencias SQL son comentarios que siguen el estándar. Ya que los comentarios SQL son ignorados por sistemas de procesamiento SQL, no interfieren con otros programas como el Servidor MySQL, el Proxy MySQL, o un cortafuegos. El complemento admite tres sugerencias SQL: La sugerencia MYSQLND_MS_MASTER_SWITCH hace que el complemento ejecute una sentencia en el maestro, MYSQLND_MS_SLAVE_SWITCH fuerza el uso del esclavo, y MYSQLND_MS_LAST_USED_SWITCH ejecutará una sentencia en el mismo servidor que se utilizó con la sentencia anterior. El complemento examina el comienzo de una sentencia para la existencia de una sugerencia SQL. Las sugerencias SQL solo son reconocidas si aparecen al comienzo de la sentencia. Configuración del complemento con un esclavo y un maestro Sugerencias SQL para evitar el intercambio de conexión query("SET @myrole='master'")) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } /* Conexión 1, se ejecuta en el maestro debido a la sugerencia SQL */ if (!($res = $mysqli->query(sprintf("/*%s*/SELECT @myrole AS _role", MYSQLND_MS_LAST_USED_SWITCH)))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } else { $fila = $res->fetch_assoc(); $res->close(); printf("@myrole = '%s'\n", $fila['_role']); } $mysqli->close(); ?> ]]> &example.outputs; En el ejemplo de arriba, el uso de MYSQLND_MS_LAST_USED_SWITCH evita el intercambio de sesión desde el maestro al esclavo al ejecutar la sentencia SELECT. Las sugerencias SQL también se pueden usar para ejecutar sentencias SELECT en el servidor maestro de MySQL. Esto podría ser necesario si los servidores esclavos de MySQL están normalmente detrás del maestro, pero no se necesitan datos actuales del clúster. En la versión 1.2.0, el concepto de nivel de servicio ha sido introducido para dirigir casos en los que no son necesarios datos actuales. El uso de un nivel de servicio requiere menos atención y elimina la necesidad de usar sugerencias SQL para este caso. Se puede encontrar más información más abajo en la sección de nivel de servicio y consistencia. Enfrentarse a la demora de replicación query(sprintf("/*%s*/SELECT critical_data FROM important_table", MYSQLND_MS_MASTER_SWITCH))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } ?> ]]> Un caso de uso puede incluir la creación de tablas en un esclavo. Si no se proporciona una sugenrencia SQL, el complemento enviará las sentencias CREATE e INSERT al maestro. Utilice la sugerencia SQL MYSQLND_MS_SLAVE_SWITCH si quiere ejecutar tales sentencias en un esclavo, por ejemplo, para construir tablas de informes temporales. Creación de una tabla en un esclavo query(sprintf("/*%s*/CREATE TABLE informes_esclavo(id INT)", MYSQLND_MS_SLAVE_SWITCH))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } /* Continuar usando esta conexión esclava en particular */ if (!$mysqli->query(sprintf("/*%s*/INSERT INTO informes_esclavo(id) VALUES (1), (2), (3)", MYSQLND_MS_LAST_USED_SWITCH))) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } /* ¡No usar MYSQLND_MS_SLAVE_SWITCH, ya que permitiría el intercambio con otro esclavo! */ if ($res = $mysqli->query(sprintf("/*%s*/SELECT COUNT(*) AS _num FROM informes_esclavo", MYSQLND_MS_LAST_USED_SWITCH))) { $fila = $res->fetch_assoc(); $res->close(); printf("Hay %d filas en la tabla 'informes_esclavo'", $fila['_num']); } else { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } $mysqli->close(); ?> ]]> La sugerencia SQL MYSQLND_MS_LAST_USED prohíbe el intercambio de una conexión, por lo que fuerza el uso de la conexión utilizada anteriormente.
Transacciones locales La versión actual del complemento no es segura con transacciones de forma predeterminada, ya que no considera su ejecución en ningún caso. Las transacciones SQL son unidades de trabajo para ejecutarlas en un único servidor. El complemento no siempre sabe cuándo comienza y cuándo finaliza una unidad de trabajo. Por lo tanto, el complemento podría decidir intercambiar conexiones en mitad de una transacción. Ningún tipo de equilibrador de carga de MySQL puede detectar los límites de una transacción sin ningún tipo de sugerencia por parte de la aplicación. Se pueden usar sugerencias SQL para superar esta limitación. De forma alternativa, se puede activar la monitorización de llamadas a la API de transacciones. En el último caso, de deben usar llamadas a la API solamente para controlar transacciones. Véase más abajo. Configuración del complemento con un esclavo y un maestro Usar sugerencias SQL para transacciones query("START TRANSACTION")) { /* Por favor, use un manejo de errores mejor en su código */ die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* ¡Evitar el intercambio de conexión! */ if (!$mysqli->query(sprintf("/*%s*/INSERT INTO test(id) VALUES (1)", MYSQLND_MS_LAST_USED_SWITCH))) { /* Por favor, realice el ROLLBACK apropiado en su código, y no use simplemente 'die' */ die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if ($res = $mysqli->query(sprintf("/*%s*/SELECT COUNT(*) AS _num FROM test", MYSQLND_MS_LAST_USED_SWITCH))) { $fila = $res->fetch_assoc(); $res->close(); if ($fila['_num'] > 1000) { if (!$mysqli->query(sprintf("/*%s*/INSERT INTO events(task) VALUES ('cleanup')", MYSQLND_MS_LAST_USED_SWITCH))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } } } else { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if (!$mysqli->query(sprintf("/*%s*/UPDATE log SET last_update = NOW()", MYSQLND_MS_LAST_USED_SWITCH))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if (!$mysqli->query(sprintf("/*%s*/COMMIT", MYSQLND_MS_LAST_USED_SWITCH))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } $mysqli->close(); ?> ]]> Desde PHP 5.4.0, la biblioteca mysqlnd permite al complemento monitorizar el estado del modo autocommit, si el modo está establecido por llamadas a la API en lugar de usar sentencias SQL como SET AUTOCOMMIT=0. Esto permite que el complemento considere las transacciones. En este caso, es necesario usar sugerencias SQL. Si se está utlizando PHP 5.4.0 o superior, con las llamadas a la API que habilitan el modo autocommit, y cuando se establece la opción de configuración del complemento trx_stickiness=master, éste puede deshabilitar automáticamente el equilibrado de carga y el intercambio de conexiones para transacciones SQL. Con esta configuración, el complemento detiene el equilibrado de carga si autocommit está deshabilitado, y dirige todas las sentencias al maestro. Esto evita el intercambio de conexiones en mitad de una transacción. Una vez que autocommit es rehabilitado, el complemento inicia de nuevo las sentencias con equilibrado de carga. La detección de los límites de transacciones basada en API ha sido mejorada con PHP 5.5.0 y PECL/mysqlnd_ms 1.5.0 para cubirir no solo llamadas a mysqli_autocommit, sino también a mysqli_begin, mysqli_commit y mysqli_rollback. Equilibrado de carga considerando transacciones: el ajuste trx_stickiness Consideración de transacciones autocommit(FALSE); if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) { /* Por favor, realice el ROLLBACK apropiado en su código, y no use simplemente 'die' */ die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if ($res = $mysqli->query("SELECT COUNT(*) AS _num FROM test")) { $fila = $res->fetch_assoc(); $res->close(); if ($fila['_num'] > 1000) { if (!$mysqli->query("INSERT INTO events(task) VALUES ('cleanup')")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } } } else { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if (!$mysqli->query("UPDATE log SET last_update = NOW()")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } if (!$mysqli->commit()) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* El complemento asume que la transacción ha finalizado e inicia el equilibrado de carga de nuevo */ $mysqli->autocommit(true); $mysqli->close(); ?> ]]> Requisitos de versión La opción de configuración del complemento trx_stickiness=master requiere PHP 5.4.0 o superior. Por favor, observe las restricciones descritas en la sección de conceptos sobre manejo de transacciones.
Transacciones XA/Distribuidas Requisitos de versión Las funciones relacionadas con XA han sido introducidas en la verisón 1.6.0-alpha de PECL mysqlnd_ms. Se buscan los primeros adaptadores Esta característica está actualmente en desarrollo. Podrían existir problemas y/o limitaciones. No la use en entornos de producción, aunque las primeras pruebas indican una calidad razonable. Por favor, contacte con el equipo de desarrollo si está interesado en esta característica. Estamos buscando comentarios de la vida real para completar esta característica. Las transacciones XA son un método estandarizado para ejecutar transacciones a través de varios recursos. Estos recursos pueden ser bases de datos u otros sistemas transaccionales. El servidor de MySQL admite sentencias SQL de XA, las cuales permiten a los usuarios llevar a cabo una transacción SQL distribuida que genera varios servidores de bases de datos, o cualquier otro tipo siempre y cuando admita también las sentencias SQL. En este tipo de escenario, es responsabilidad del usuario coordinar los servidores participantes. PECL/mysqlnd_ms puede actuar como un coordinador de transacciones para una transacción global (distribuida, XA) se lleve a cabo solamente en servidores de MySQL. Como coordinador transacciones, el complemento rastrea todos los servidores involucrados en una transacción global y envía de forma transparente las sentencias SQL apropiadas a los participantes. Las transacción globales son controladas con mysqlnd_ms_xa_begin, mysqlnd_ms_xa_commit y mysqlnd_ms_xa_rollback. Los detalles SQL mayormente están ocultos a la aplicación, ya que es necesario rastrear y coordinar a los participantes. Patrón general para transacciones XA errno, $mysqli->error)); } /* ejecutar las consultas como siempre: XA BEGIN será inyectada en cuanto se ejecute una consulta */ if (!$mysqli->query("INSERT INTO orders(order_id, item) VALUES (1, 'christmas tree, 1.8m')")) { /* Tanto si falla INSERT o falla la inyección de XA BEGIN */ if ('XA' == substr($mysqli->sqlstate, 0, 2)) { printf("Fallo relacionado con una transacción/XA global, [%d] %s\n", $mysqli->errno, $mysqli->error); } else { printf("Fallo de INSERT, [%d] %s\n", $mysqli->errno, $mysqli->error); } /* revertir la transacción global */ mysqlnd_ms_xa_rollback($mysqli, $xid); die("Deteniendo."); } /* continuar con la realización de consultas en otros servidores, p.ej. otros fragmentos */ /* consignar la transacción global */ if (!mysqlnd_ms_xa_commit($mysqli, $xa_id)) { printf("[%d] %s\n", $mysqli->errno, $mysqli->error); } ?> ]]> A diferencia de las transacciones locales, la cuales se llevan a cabo en un único servidor, las transacciones XA tienen un identificador (xid) asociado a ellas. El identificador de transacción de XA está compuesto por un identificador de transacción global (gtrid), un identificador de ramificación (bqual), y un identificador de formato (formatID). Solamente el identificador de transacción global puede y debe proporcionarse al invocar a cualquier función de XA del complemento. Una vez que ha sido iniciada una transacción global, el complemento comienza a rastrear servidores hsata que la transacción global finaliza. Cuando un servidor se escoge para la ejecución de una consulta, el complemento inyecta la sentencia SQL XA BEGIN antes de ejecutar la sentencia SQL real en el servidor. XA BEGIN hace que el servidor participe en la transacción global. Si falla la inyección de la sentencia SQL, el complemento informará del problema en réplica a la función de ejecución de consultas que se empleó. En el ejemplo de arriba, $mysqli->query("INSERT INTO orders(order_id, item) VALUES (1, 'christmas tree, 1.8m')") indicaría tal error. Se podría comprobar el código de estado SQL de los errores para determinar la consulta real (aquí: INSERT) que ha fallado o el error relacionado con la transacción global. Depende de usted ignorar el fallo de iniciar la transacción global en un servidor y continuar la ejecución sin tener al servidor participando en la transacción global. Las transacciones locales y globales son mutuamente exclusivas begin_transaction()) { die(sprintf("[%d/%s] %s\n", $mysqli->errno, $mysqli->sqlstate, $mysqli->error)); } /* anora no se puede iniciar la transición global - primero debe finalizar la local */ $gtrid_id = "12345"; if (!mysqlnd_ms_xa_begin($mysqli, $gtrid_id)) { die(sprintf("[%d/%s] %s\n", $mysqli->errno, $mysqli->sqlstate, $mysqli->error)); } ?> ]]> &example.outputs; Una transacción global no se puede iniciar cuando una transacción local está activa. El complelento intenta detectar esta situación tan pronto como sea posible, que es cuando se llama a mysqlnd_ms_xa_begin. Si solamente se emplean llamadas a la API para controlar transacciones, el complemento sabrá qué transacción local está abierta, devolviendo un error para mysqlnd_ms_xa_begin. Sin embargo, observe que las limitaciones para detectar límites de transacciones del complemento. En el peor de los casos, si se emplea SQL directo para transacciones locales (BEGIN, COMMIT, ...), podría suceder que un error de demorara hasta que se ejecute alguna sentencia SQL en el servidor. Para finalizar una transacción global se ha de invocar a mysqlnd_ms_xa_commit o a mysqlnd_ms_xa_rollback. Cuando finaliza una transacción global, todos los participantes deben ser informados de dicha finalización. Por lo tando, PECL/mysqlnd_ms enviará de forma transparente las sentencias SQL relacionadas apropiadas a alguno o todos ellos. Cualquier fallo durante esta fase ocasionará una reversión implícita. La API relacionada con XA se mantiene aquí intencionadamente simple. Una API más compleja que proporcionara más control dejaría al descubierto pocas ventajas, si las hubiera, sobre una implementación de usuario que enviara todas las sentencias SQL de XA de bajo nivel por sí misma. Las transacciones XA emplean el protocolo de consignación de dos fases. El protocolo de consignación de dos fases es un protocolo de bloqueo. Hay casos en los que no se puede hacer ningún progreso, incluso al emplear tiempos de espera. Los coordinadores de transaccionesbe made, not even when using timeouts. Transaction coordinators deberían sobrevivir a sus propios fallos, ser capaces de detectar bloqueos y deshacer empates. PECL/mysqlnd_ms toma el papel de un coordinador de transacciones y puede ser configurado para sobrevivir a su propia caída para evitar problemas con servidores de MySQL bloqueados. Por tanto, el complemento puede y debería ser configurado para utilizar un estado persistente y seguro ante caídas para permitir la recolección de basura de transacciones globales abortadas y no finalizadas. Una transacción global puede ser abortada en un estado abierto si el complemento falla (se cae) o falla una conexión desde el complemento a un participante de una transacción global. Almacenamiento del estado del coordinador de transacciones Actualmente, PECL/mysqlnd_ms admite solamente el uso de tablas de bases de datos de MySQL como almacenamiento de estados. Las definiciones de SQL de las tablas se proporcionan en la sección de configuración del complemento. Asegúrese de utilizar un motor de almacenamiento transaccional y seguro ante caídas para las tablas, como InnoDB. InnoDB es el motor de tablas predeterminado en versiones recientes del servidor de MySQL. Asegúrese también de que el servidor de bases de datos sea altamente disponible. Si se ha configurado un almacén de estado, el complemento puede realizar una recolección de basura. Durante dicha recolección, podría ser necesario conectarse a un participante de una transacción global fallida. Así, el almacén de estado mantiene una lista de participantes y, entre otras cosas, sus nombres de host. Si la recolección de basura se ejecuta en otro host que no sea el que haya escrito una entrada de participación con el el nombre de host localhost, entonces localhost se resolverá a máquinas diferentes. Hay dos soluciones al problema. O no se configura ningún servidor con el nombre de host localhost y se configura una dirección IP (y puerto), o se indica la recolección de basura. En el ejemplo anterior, localhost se usa para master_0, de ahí que podría no resolverse al host correcto durante la recolección de basura. Sin embargo, participant_localhost_ip también está establecido para indicar la recolección de basura que localhost representa para la IP 192.168.2.12.
Nivel de servicio y consistencia 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. Consistencia de sesión: lectura de sus escrituras Solicitud de consistencia de sesión 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($mysqli, MYSQLND_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($mysqli, MYSQLND_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. Edad máxima/demora del esclavo Limitar la demora del esclavo 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($mysqli, MYSQLND_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. 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. La tolerancia a fallos no está establecida No hay esclavos dentro del límite de tiempo 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($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } ?> ]]> &example.outputs;
ID de transacción globales Requisitos de versión Se ha introducido una inyección de ID de transacciones global en el lado del cliente en la versión 1.2.0-alpha de mysqlnd_ms. Esta característica no es necesaria para clústeres sincrónicos, tales como el Clúster MySQL. Utilícela con clústeres asíncronos tales como la replicación MySQL clásica. A partir de la versión candidata MySQL 5.6.5-m8, el servidor MySQL introduce los identificadores de transacciones globales internos. La característica del ID de transacciones global interno está soportada por PECL/mysqlnd_ms 1.3.0-alpha o posterior. Sin embargo, el conjunto final de características encontrado en las versiones de producción de MySQL 5.6 hasta la fecha, no es suficiente para soportar las ideas tratadas abajo en todos los casos. Véase también la sección de conceptos. PECL/mysqlnd_ms puede usar tanto su propia emulación del ID de transacciones global como la característica del ID de transacciones global interna de MySQL 5.6.5-m8 o posterior. Desde la perspectiva del desarrolador, los enfoques del lado del cliente y del lado del servidor ofrecen las mismas características en cuanto a niveles de servicios proporcionados por PECL/mysqlnd_ms. Las diferencias son tratadas en la sección de conceptos. Esta guía rápida primero demuestra el uso de la emulación interna del ID de transacciones global en el lado del cliente en PECL/mysqlnd_ms antes de mostrar cómo usar su homólogo en el lado del servidor. Este orden asegura que la idea subyacente se trata primero. La idea y la emulación en el lado del cliente En su forma más básica, un ID de Transacciones Global (GTID de sus siglas en inglés) es un contador de una tabla del maestro. El contador se incrementa siempre que se consigne una transacción en el maestro. Los esclavos replican la tabla. El contador sirve para dos propósitos. En caso de un fallo del maestro, ayuda al administrador de la base de datos a identificar al esclavo que fue promovido más recientemente como nuevo maestro. Este esclavo es aquel con el valor de contador más alto. Las aplicaciones pueden usar el ID de transacciones global para buscar los esclavos que ya han replicado una escritura en particular (identificada por un ID de transacciones global). PECL/mysqlnd_ms puede inyectar SQL para cada transacción consignada para incrementar un contador GTID. El ID creado es accesible por la aplicación para poder identificar una operación de escritura de una aplicación. Esto habilita al complemento para proporcionar el nivel de servicio de consistencia de sesión (lectura de sus escrituras) mediante no solamente la consulta a los maestros, sino también a los esclavos que ya han replicado el cambio. La carga de lectura se le quita al maestro. La emulación del ID de transacciones global en el lado del cliente tiene algunas limitaciones. Por favor, lea la sección de conceptos detenidamente para comprender completamente los principios y las ideas subyacentes antes de usarla en entornos de producción. No es necesario un conocimiento profundo para continuar con esta guía rápida. Primero, cree una tabla contador en su servidor maestro e inserte un registro en ella. El complemento no asiste en la creación de la tabla. Los administradores de la base de datos deben asegurarse de que existe. Dependiendo del modo de notificación de errores, el complemento ignorará de forma silenciosa la ausencia de la tabla o abandonará. Crear una tabla contador en el maestro En el fichero de configuración del complemento establezca el SQL para actualizar la tabla del ID de transacciones global usando on_commit en la sección global_transaction_id_injection. Asegúrese de que el nombre de la tabla usado para la sentencia UPDATE está completamente cualificado. En el ejemplo, test.trx se usa para referirse a la tabla trx del esquema (base de datos) test. Use la tabla que se creó en el paso anterior. Es importante establecer el nombre de la tabla completamente cualificado, ya que la conexión donde se realiza la inyección puede usar una base de datos predeterminada diferente. Asegúrese de que al usuario que abra la conexión se le permita ejecutar la sentencia UPDATE. Habilite la notificación de los errores que pudieran ocurrir cuando mysqlnd_ms realice inyecciones de ID de transacciones global. Configuración del complemento: SQL para la inyección del GTID en el lado del cliente Inyección transparente del ID de transacciones global query("DROP TABLE IF EXISTS test")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* modo autoconsigna, transacción en el maestro, el GTID debe ser incrementado */ if (!$mysqli->query("CREATE TABLE test(id INT)")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* modo autoconsigna, transacción en el maestro, el GTID debe ser incrementado */ if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* modo autoconsigna, lectura en el esclavo, sin incremento */ if (!($res = $mysqli->query("SELECT id FROM test"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } var_dump($res->fetch_assoc()); ?> ]]> &example.outputs; string(1) "1" } ]]> El ejemplo ejecuta tres sentencias en el modo autoconsigna en el maestro, causando tres transacciones en el maestro. Para cada sentencia, el complemento inyectará el UPDATE configurado de forma transparente antes de ejecutar las sentencias SQL del usuario. Cuando finaliza el script, el contador de IDs de transacciones globales en el maestro ha sido incrementado en tres. La cuarta sentencia SQL ejecutada en el ejemplo, un SELECT, no desencadena un incremento. Solamente las transacciones (escrituras) ejecutadas en un maestro incrementarán el contador GTID. SQL para el ID de transacciones global: ¡se busca una solución eficiente! El SQL usado para la emulación del ID de transacciones global es ineficiente. Está optimizado para la trasnparencia, no para el rendimiento. No lo use para entornos de producción. Por favor, ayude a encontrar una solución eficiente para su inclusión en el manual. Apreciamos su aportación. Configuración del complemento: SQL para obtener el GTID Obtener el GTID después de la ejecución query("DROP TABLE IF EXISTS test")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } printf("GTID después de la transacción %s\n", mysqlnd_ms_get_last_gtid($mysqli)); /* modo autoconsigna, transacción en el maestro, el GTID debe ser incrementado */ if (!$mysqli->query("CREATE TABLE test(id INT)")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } printf("GTID después de la transacción %s\n", mysqlnd_ms_get_last_gtid($mysqli)); ?> ]]> &example.outputs; Las aplicaciones pueden preguntar a PECL mysqlnd_ms por un ID de transacciones global que pertenezca a la última operación de escritura realizada por la aplicación. La función mysqlnd_ms_get_last_gtid devuelve el GTID obtenido cuando se ejecuta la sentencia SQL desde la entrada fetch_last_gtid de la sección global_transaction_id_injection del fichero de configuración del complemento. La función puede ser invocada después de que el GTID haya sido incrementado. Se aconseja que las aplicaciones no ejecuten la sentencia SQL por sí mismas ya que aumenta el riesgo de que accidentalmente se ocasione un incremento implícito del GTID. También, si se usa la función, es más fácil migrar una aplicación desde una sentencia SQL para obtener un ID de transacciones a otra, por ejemplo, si ningún servidor MySQL incluye el soporte interno para IDs de transacciones globales. Esta guía rápida muestra una sentencia SQL que devolverá un GTID igual o mayor que el creado por la sentencia anterior. Éste es exactamente el GTID creado por la sentencia anterior si ningún cliente ha incrementado el GTID en el tiempo transcurrido entre la ejecución de la sentencia y el SELECT para obtener el GTID. De otro modo, será mayor. Configuración del complemento: comprobar un GTID en particular = #GTID", "report_error":true } } } ]]> Nivel de servicio de consistencia de sesión y GTID combinados query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1)") ) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* GTID es un identificador para la última escritura */ $gtid = mysqlnd_ms_get_last_gtid($mysqli); /* Consistencia de sesión (lectura de sus escrituras): intentar leer de los esclavos, no sólo del maestro */ if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION, MYSQLND_MS_QOS_OPTION_GTID, $gtid)) { die(sprintf("[006] [%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Ejecutar en el maestro o en el esclavo que ha replicado el INSERT */ if (!($res = $mysqli->query("SELECT id FROM test"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } var_dump($res->fetch_assoc()); ?> ]]> Un GTID devuelto por mysqlnd_ms_get_last_gtid se puede usar como una opción para el nivel de servicio de consistencia de sesión. La consistencia de sesión proporciona la lectura de sus escrituras. La consistencia de sesión puede ser solicitada llamando a mysqlnd_ms_set_qos. En el ejemplo, el complemento ejecutará la sentencia SELECT en el maestro o en un esclavo que ya ha repliacdo el INSERT anterior. PECL mysqlnd_ms comprobará de forma transparente cada esclavo configurado, si éste ha replicado el INSERT, mediante la comprobación de la tabla GTID de esclavos. La comprobación se realiza ejecutando la sentencia SQL establecida con la opción check_for_gtid de la sección global_transaction_id_injection del fichero de configuración del complemento. Por favor, observe que este es un procedimiento lento y caro. Las aplicaciones deberían intentar usarlo poco y únicamente si la carga de lectura en el maestro es alta. Uso de la característica del ID de transacciones global en el lado del servidor Soporte del servidor insuficiente en MySQL 5.6 El complemento ha sido derarrollado usando una versión de preproducción de MySQL 5.6. Resulta de que todas las versiones de producción de MySQL 5.6 no proporcionan clientes con suficiente información para forzar la consistencia de sesión basándose en GTID. Por favor, lea la sección de conceptos para más detalles. Desde MySQL 5.6.5-m8, el sistema de Replicación MySQL introduce los IDs de transacciones globales. Los identificadores de transacciones son automáticamente generados y mantenidos por el servidor. Los usuarios no necesitan ocuparse de ellos. No hay necesidad de configurar ninguna tabla de antemano, ni de configurar on_commit. Ya no es necesaria la emulación en el lado del cliente. Los clientes puede seguir usando los identificadores de transacciones globales para conseguir la consistencia de sesión al leer desde esclavos de la Replicación MySQL en algunos casos, ¡pero no en todos!. El algoritmo funciona como está descrito arriba. Se deben configurar diferentes sentencias SQL para fetch_last_gtid y check_for_gtid. Estas sentencias se proporcionan más abajo. Por favor, observe que MySQL 5.6.5-m8 es una versión en desarrollo. Los detalles de la implementación del servidor pueden cambiar en el futuro y requerir la adopción de las sentencias SQL mostradas. Al utilizar la siguiente configuración, cualquier funcionalidad descrita arriba puede usarse junto con la característica del ID de transacciones global en el lado del servidor. mysqlnd_ms_get_last_gtid y mysqlnd_ms_set_qos siguen funcionando como se describió arriba. La única diferencia es que el servidor no utiliza un número de secuencia simple, sino un string que contiene un identificador del servidor y un número de secuencia. Por lo tanto, los usuarios no podrán obtener fáciltmente un orden desde los GTIDs devueltos por mysqlnd_ms_get_last_gtid. Configuración de complemento: usar la característica del GTID interna de MySQL 5.6.5-m8
Integración de una caché Requisitos de versión, dependencias y estado Se puede encontrar más sobre los requisitos de la versión, las dependencias del orden de carga de las extensiones y el estado actual en la sección de conceptos. Los clústeres de bases de datos pueden proporcionar diferentes niveles de consistencia. A partir de PECL/mysqlnd_ms 1.2.0 es posible acosejar al complemento para que cosidere solamente los nodos del clúster que puedan proporcionar el nivel de consistencia requerido. Por ejemplo, si se usa la Replicación MySQL con su consistencia final a lo ancho del clúster, es posible solicitar la consistencia de sesión (lectura de sus escrituras) en cualquier momento usando mysqlnd_ms_set_quos. Por favor, vea también la introducción de nivel de servicio y consistencia. Resumen: calidad del servicio para solicitar la lectura de sus escrituras /* Solicitud de consistencia de sesión: lectura de sus escrituras */ if (!mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION)) die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); Asumiendo que a PECL/mysqlnd se le ha indicado explícitamente que proporcione un nivel de consistencia no mayor que la consistencia final, es posible reemplazar un acceso de lectura de un nodo de la base de datos por una caché en el lado del cliente usando el tiempo de vida (TTL) como estrategia de invalidación. El nodo de la base de datos y la caché pueden o no servir datos actuales, ya que esto es lo que la consistencia final define. El reemplazo de un acceso de lectura de un nodo de la base de datos por un acceso a la caché local puede mejorar el redimiento general y disminuir la carga de la base de datos. Si la entrada de la caché es reutilizada por otro cliente que no sea el que creó dicha entrada, se guaradará un acceso a la base de datos, por lo que se disminuye la carga de la base de datos. Además, el rendimiento del sistema se puede mejorar si el cálculo y la entrega de una consulta a la base de datos es más baja que un acceso a la caché local. Configuración del complemento: sin entradas especiales para la caché Almacenar en caché una petición a un servidor query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1)") ) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Explícitamente permite la consistencia final y el almacenamiento en caché (TTL <= 60 segundos) */ if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL, MYSQLND_MS_QOS_OPTION_CACHE, 60)) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Para que este ejemplo funcione, debemos esperar a que el esclavo se ponga al día. Estilo de fuerza bruta. */ $intentos = 0; do { /* comprobar si el esclavo posee la tabla */ if ($res = $mysqli->query("SELECT id FROM test")) { break; } else if ($mysqli->errno) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* esperar a que el esclavo se ponga al día */ usleep(200000); } while ($intentos++ < 10); /* La consulta ha sido ejecutada en un esclavo, el resultado está en la caché */ assert($res); var_dump($res->fetch_assoc()); /* Servido desde la caché */ $res = $mysqli->query("SELECT id FROM test"); ?> ]]> El ejemplo muestra cómo usar la caché. Primero, se ha de establecer la calidad del servicio al de consistencia final y tener en cuenta explícitamente el almacenamiento en la caché. Esto se lleva a cabo llamando a mysqlnd_ms_set_qos. Luego, el conjunto de resultados de cada sentencia de solo lectura se almacena en la caché hasta un máximo de segundos según lo permitido por mysqlnd_ms_set_qos. El TTL real es menor o igual al valor establecido con mysqlnd_ms_set_qos. El valor pasado a la función establece la edad máxima (en segundos) de los datos proporcionados. Para calcular el valor real del TTL, se comprueba la demora de replicación de un servidor y se resta del valor dado. Si, por ejemplo, la edad máxima es de 60 segundos y el esclavo notifica una demora de 10 segundos, el TTL resultante es de 50 segundos. El TTL se calcula de forma individual para cada consulta de la caché. Lectura de sus escrituras y almacenamiento en la caché combinados query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1)") ) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Explícitamente permite la consistencia final y el almacenamiento en caché (TTL <= 60 segundos) */ if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL, MYSQLND_MS_QOS_OPTION_CACHE, 60)) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Para que este ejemplo funcione, debemos esperar a que el esclavo se ponga al día. Estilo de fuerza bruta. */ $intentos = 0; do { /* comprobar si el esclavo posee la tabla */ if ($res = $mysqli->query("SELECT id FROM test")) { break; } else if ($mysqli->errno) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* esperar a que el esclavo se ponga al día */ usleep(200000); } while ($intentos++ < 10); assert($res); /* La consulta ha sido ejecutada en un esclavo, el resultado está en la caché */ var_dump($res->fetch_assoc()); /* Servido desde la caché */ if (!($res = $mysqli->query("SELECT id FROM test"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } var_dump($res->fetch_assoc()); /* Actualización en el maestro */ if (!$mysqli->query("UPDATE test SET id = 2")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Lectura de sus escrituras */ if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION)) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Obtener los últimos datos */ if (!($res = $mysqli->query("SELECT id FROM test"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } var_dump($res->fetch_assoc()); ?> ]]> La calidad del servicio se puede cambiar en cualquier momento para evitar el uso de la caché. Si fuera necesario, se podría cambiar para leer sus escrituras (consistencia de sesión). En este caso, la caché no se usará y se leerán datos actuales.
Tolerancia a fallos Por omisión, el complemento no intenta la tolerancia a fallos si falla al conectarse a un equipo anfitrión. Esto evita los problemas relacionados con el estado de la conexión. Se recomienda tratar de forma manual los errores de conexión de una manera similar a una transacción fallida. Se debería capturar el error, reconstruir el estado de la conexión y volver a ejecutar la consulta tal como se muestra abajo. Si el estado de la conexión no es la cuestión, de forma alternativa se puede habilitar la tolerancia a fallos automática y silenciosa. Dependiendo de la configuración, la tolerancia a fallos automática y silenciosa se intentará en el maestro antes de emitir un error, o se intentará conectar a otros esclavos, dada la consulta permitida para ello, antes de intentar conectarse a un maestro. Ya que la tolerancia a fallos automática no es infalible, no se trata en esta guía rápida. En su lugar, los detalles se proporcionan en la sección de conceptos posterior. Tolerancia a fallos manual, opcional automática Tolerancia a fallos manual query($sql))) { /* específico del complemento: comprobar errores de conexión */ switch ($enlace->errno) { case 2002: case 2003: case 2005: printf("Error de conexión - ¡intentando con el siguiente esclavo!\n"); /* el equilibrador de carga elegirá el siguiente esclavo */ $res = $enlace->query($sql); break; default: /* no hay errores de conexión, la tolerancia a fallos es poco probable que ayude */ die(sprintf("SQL error: [%d] %s", $enlace->errno, $enlace->error)); break; } } if ($res) { var_dump($res->fetch_assoc()); } ?> ]]>
Particionamiento y fragmentación La agrupación (clustering) de bases de datos se realiza por varias razones. Los clúster pueden mejorar la disponibilidad, la tolerancia a fallos, e incrementar el rendimiento aplicando un enfoque de "divide y vencerás", ya que el trabajo se distribuye en varias máquinas. La agrupación (clustering) a veces se combina con particionamiento y fragmentación para dividir aún más una tarea compleja grande en unidades más pequeñas y manejables. El complemento mysqlnd_ms pretende dar soporte a una gran variedad de clústeres de bases de datos de MySQL. Algunos sabores de clúster de bases de datos de MySQL poseen métodos internos para el particionamiento y la fragmentacion, los cuales podrían ser transparentes de usar. El complemetno admite los dos enfoques más comunes: filtración de tabla de Replicación de MySQL, y la fragmentación (particionamiento basada en aplicación). La Replicación de MySQL admite el particionamiento como filtros que permiten crear esclavos que replican todas las bases de datos o específicas del maestro, o tablas. Es entonces responsabilidad de la aplicación elegir un esclavo según las reglas de los filtros. Se puede usar el filtro node_groups de mysqlnd_ms para dar soporte manual a esto, o usar el filtro de tablas experimental. El particionamiento manual o fragmentación está soportado a través del filtro de agrupación de nodos, y de las sugerencias SQL a partir de 1.5.0. El filtro node_groups permite asignar un nombre simbólico a un grupo de servidores maestros y esclavos. En el ejemplo, el maestro master_0 y slave_0 forman un grupo con el nombre de Partition_A. Es su responsabilidad decidir lo que haga un grupo. Por ejemplo, se podrían usar grupos de nodos para la fragmentación, y usar los nombres de grupos para direccionar fragmentos como Shard_A_Range_0_100. Clúster de grupos de nodos Particionamiento manual usando sugerencias SQL query($sql))) { printf("[%d] %s", $mysqli->errno, $mysqli->error); return false; } $fila = $res->fetch_assoc(); printf("%d - %s - %s\n", $fila['_thread'], $fila['_hint'], $sql); return true; } $mysqli = new mysqli("myapp", "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())); } /* Permitir siempre esclavos */ select($mysqli, "slave_0"); select($mysqli, "slave_1"); /* solamente permtir los servidores del grupo de nodos "Partition_A" */ select($mysqli, "slave_1", "/*Partition_A*/"); select($mysqli, "slave_1", "/*Partition_A*/"); ?> ]]> Por omisión, el complemento usará todos los servidores maestros y esclavos configurados para la ejecución de consultas. Pero si una consulta comienza con una sugerencia SQL como /*node_group*/, el complemento considerará únicamente los servidores enumerados en node_group para la ejecución de consultas. Así, las consultas SELECT prefijadas con /*Partition_A*/ úncamente serán ejecutadas en slave_0.
MySQL Fabric Requisitos de versión y estado Se comenzó a trabajar en el soporte para MySQL Fabricen la versión 1.6. Considere el soporte con una calidad prealfa. El manual podría no enumerar todas las limitaciones de la/s característica/s. Aún se está trabajand en ello. La fragmentación es el único caso de uso soportado por el complemento hasta la fecha. Conceptos de MySQL Fabric Por favor, verfique el manual de referencia de MySQL para más información sobre MySQL Fabric y cómo configurarlo. El manual de PHP asumen que se es familiar con los conceptos e ideas básicos de MySQL Fabric. MySQL Fabric es un sistema para gestionar granjas de servidores MySQL para lograr una Alta Disponibilidad y opcionalmente soporte para fragmentación. Técnicamente, es un middleware para gestionar y monitorizar servidores MySQL. Los clientes consultan a MySQL Fabric para obtener listas de servidores MySQL, sus estados y sus roles. Por ejemplo, los clientes pueden solicitar una lista de esclavos para un grupo de Replicacion MySQL y para ver si están listos para manejar peticiones SQL. Otro ejemplo es un clúster de servidores de MySQL fragmentados donde el cliente busca conocer a qué fragmento consultar para una tabla y clave de de fragmentación dados. Si se configura para utilizar Fabric, el complemento usa XML RCP sobre HTTP para obtener la lista en tiempo de ejecución desde un host deto MySQL Fabric. La llamada XML a un procedimiento remoto se realiza en segundo plano y de forma transparente desde el punto de vista de los desarrolladores. En lugar de enumerar los servidores MySQL directamente en el fichero de configuración del complemento, este contiene una lista de uno o más host de MySQL Fabric. Configuración del complemento: hosts de Fabric en lugar de servidores MySQL Los usuarios utilizan las nuevas funciones mysqlnd_ms_fabric_select_shard y mysqlnd_ms_fabric_select_global para cambiar al conjunto de servidores responsables de una clave de fragmento dada. Luego, el complemento elige un servidor apropiado para ejecutar las consultas. Al hacer esto, el complemento se encarga del conjunto de reglas adicionales de equilibrado de carga. El ejemplo de abajo asume que MySQL Fabric ha sido configurado al fragmento de tabla test.fabrictest usando la columna id de la tabla como clave de fragmento. Particionameinto manual usando sugerencias SQL query("CREATE TABLE test.fabrictest(id INT NOT NULL PRIMARY KEY)")) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Cambiar la conexión al fragmento apropiado e insertar un registro */ mysqlnd_ms_fabric_select_shard($mysqli, "test.fabrictest", 10); if (!($res = $mysqli->query("INSERT INTO fabrictest(id) VALUES (10)"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } /* Intentar leer el recién insertado registro */ mysqlnd_ms_fabric_select_shard($mysqli, "test.fabrictest", 10); if (!($res = $mysqli->query("SELECT id FROM test WHERE id = 10"))) { die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); } ?> ]]> El ejemplo crea una tabla fragmentada, inserta un registro y lo lee después. Todas las operaciones de lenguaje de definición de datos (DDL) sobre una tabla fragmentada deben aplicarse a lo que se llama el grupo de servidores global. Antes de crear o alterar una tabla fragmentada, se llama a mysqlnd_ms_fabric_select_global para cambiar la conexión dada a los servidores correspondientes del grupo global. Las senetencias SQL de manipulación de datos (DML) deben ser enviadas a los fragmentos directamente. La función mysqlnd_ms_fabric_select_shard cambia una conexión para manejar el fragmento de una cierta clave de fragmento.