mirror of
https://github.com/php/doc-es.git
synced 2026-03-29 19:02:23 +02:00
git-svn-id: https://svn.php.net/repository/phpdoc/es/trunk@337407 c90b9560-bf6c-de11-be94-00142212c4b1
2356 lines
107 KiB
XML
2356 lines
107 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<!-- EN-Revision: 55f2060721a1ea31a52a8a8cec267b1777812227 Maintainer: seros Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
|
|
<chapter xml:id="mysqlnd-ms.concepts" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title>Conceptos</title>
|
|
<para>
|
|
Aquí se explican la arquitectura y los conceptos relacionados con este complemento, y
|
|
se describe el impacto que la replicación MySQL y este complemento tienen en tareas de
|
|
desarrollo al usar un clúster de bases de datos. Es necesario leer y comprender
|
|
estos conceptos para poder utilizar este complemento satisfactoriamente.
|
|
</para>
|
|
<section xml:id="mysqlnd-ms.architecture">
|
|
<title>Arquitectura</title>
|
|
<para>
|
|
El complemento de replicación y equilibrado de carga de mysqlnd está
|
|
implementado como una extensión de PHP.
|
|
Está escrito en C y opera bajo PHP. Durante el
|
|
arranque del intérprete de PHP, en la fase de inicialización de módulos del
|
|
motor de PHP, es registrado como un complemento de
|
|
<link linkend="book.mysqlnd">mysqlnd</link> para reemplazar los métodos en C
|
|
de mysqlnd seleccionados.
|
|
</para>
|
|
<para>
|
|
Durante la ejecución de PHP, inspecciona las consultas enviadas desde
|
|
mysqlnd (PHP) al servidor MySQL. Si una consulta se reconoce como de solo lectura,
|
|
será enviada a uno de los servidores esclavos configurados. Una sentencia es
|
|
considerada de solo lectura si comienza con <literal>SELECT</literal>, con
|
|
la sugerencia SQL <literal>/*ms=slave*/</literal>, o se ha elegido un esclavo para
|
|
ejecutar la consulta anterior y la consulta comienza con la sugerencia SQL
|
|
<literal>/*ms=last_used*/</literal>. En los demás casos, la consulta será
|
|
enviada al servidor maestro de replicación MySQL.
|
|
</para>
|
|
<para>
|
|
Para una mejor portabilidad, las aplicacioines deberían usar las
|
|
<link linkend="mysqlnd-ms.constants">constantes predefinidas de mysqlnd_ms</link>
|
|
<constant>MYSQLND_MS_MASTER_SWITCH</constant>,
|
|
<constant>MYSQLND_MS_SLAVE_SWITCH</constant>, y
|
|
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant>
|
|
en lugar de sus valores literales, tales como <literal>/*ms=slave*/</literal>.
|
|
</para>
|
|
<para>
|
|
El complemento maneja la apertura y el cerre de conexiones de bases de datos
|
|
a los servidores maestros y esclavos. Desde el punto de vista de
|
|
la aplicación, continua existiendo solamente un gestor de conexión. Sin embargo,
|
|
internamente, este gestor de conexión público representa una agrupación de
|
|
conexiones de red que son gestionadas por el complemento. Éste delega las consultas
|
|
al sevidor maestro y a los esclavos usando múltiples conexiones.
|
|
</para>
|
|
<para>
|
|
Las conexiones a la bases de datos tienen un estado que consiste en, por ejemplo, el estado
|
|
de las transacciones, configuración de las transacciones, configuración del conjunto de caracters, y tablas temporales.
|
|
El complemento intentará mantener el mismo estado entre todas las conexiones
|
|
internas, siempre que se pueda realizar de una forma automática y transparente.
|
|
En los casos donde sencillamente no es posible mantener el estado entre
|
|
conexiones, como al usar <literal>BEGIN TRANSACTION</literal>, el
|
|
complemento lo deja en manos del usuario.
|
|
</para>
|
|
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.pooling">
|
|
<title>Agrupación e intercambio de conexiones</title>
|
|
<para>
|
|
El complemento de replicación y equilibrado de carga cambia la semántica de un gestor de
|
|
conexión de MySQL para PHP. Las APIs existentes de las extensiones de MySQL para PHP
|
|
(<link linkend="ref.mysqli">mysqli</link>,
|
|
<link linkend="ref.mysql">mysql</link>, y
|
|
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>) no son cambiadas en la
|
|
manera de que no son añadidas ni eliminadas funciones. Pero su comportamiento
|
|
cambia al utilizar el complemento. Las aplicaciones existentes no necesitan
|
|
ser adaptadas a la nueva API, aunque podrían requerir ser modificadas a causa de
|
|
los cambios de comportamiento.
|
|
</para>
|
|
<para>
|
|
El complemento rompe la relación uno-a-uno entre un gestor de conexión de
|
|
<link linkend="ref.mysqli">mysqli</link>,
|
|
<link linkend="ref.mysql">mysql</link>, y
|
|
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>
|
|
y una conexión de red MySQL. Un gestor de conexión de
|
|
<link linkend="ref.mysqli">mysqli</link>,
|
|
<link linkend="ref.mysql">mysql</link>, y
|
|
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>
|
|
representa una agrupación local de conexiones para los servidores
|
|
maestros de replicación MySQL y esclavos de replicación MySQL configurados.
|
|
El complemento redirige las consultas a los servidores maestros y esclavos.
|
|
En algún momento, el mismo gestor de conexión de PHP
|
|
podría apuntar al servidor maestro de MySQL. Después, podría apuntar
|
|
a uno de los servidores esclavos o todavía al maestro. La manipulación
|
|
y el reemplazamiento de la conexión de red referida por un gestor de conexión de
|
|
MySQL para PHP no es una operación transparente.
|
|
</para>
|
|
<para>
|
|
Cada conexión MySQL tiene un estado. El estado de la conexión en
|
|
la agrupación de conexiones del complemento puede diferir. Siempre que el
|
|
complemento cambie de una conexión de cable a otra, el estado actual de
|
|
la conexión del usuario puede cambiar. Las aplicaciones deben considerar esto.
|
|
</para>
|
|
<para>
|
|
La siguiente lista muestra en qué consiste el estado de conexión. La lista
|
|
podría no ser completa.
|
|
</para>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Estado de transacción
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Tablas temporales
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Bloqueos de tablas
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Variables de sistema de sesión y variables de usuario de sesión
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La base de datos actual establecida mediante <literal>USE</literal> y otros estados de comandos SQL encadenados
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Sentencias preparadas
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Variables <literal>HANDLER</literal>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Bloqueos adquiridos con <literal>GET_LOCK()</literal>
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
EL intercambio de conexiones ocurre justo antes de la ejecución de las consultas. El complemento
|
|
no intercambia la conexión actual hasata que sea ejecutada la siguiente sentencia.
|
|
</para>
|
|
<note>
|
|
<title>Sobre la replicación</title>
|
|
<para>
|
|
Véase también el capítulo sobre las <link xlink:href="&url.mysql.docs.replication;">características de la replicación</link>
|
|
y temas relacionados del manual de referencia de MySQL.
|
|
Algunas restricciones pueden no estar relacionadas con el complemento de PHP, pero
|
|
son propiedades del sistema de replicación de MySQL.
|
|
</para>
|
|
</note>
|
|
<para>Mensajes de difusión</para>
|
|
<para>
|
|
La filosofía de complemento es alinear el estado de las conexiones en la
|
|
agrupación solamente si el estado está bajo el control total del complemento, o si fuera
|
|
necesario por razones de seguridad. Solamente una pocas acciones que cambian el
|
|
estado de la conexión caen en esta categoría.
|
|
</para>
|
|
<para>
|
|
La siguiente es una lista de las llamadas a la biblioteca cliente de conexiones que cambian el estado,
|
|
y son difundidas a todas las conexiones abiertas en la agrupación de conexiones.
|
|
</para>
|
|
<para>
|
|
Si va a ejecutar cualquier llamada enumerada abajo, el complemento iterará a través de todas
|
|
las conexiones maestras y esclavas abiertas. El bucle continua hasta que todos los servidores
|
|
hayan sido contactados, y no se detiene si un servidor indica un fallo.
|
|
Si es posible, el fallo se propagará a la función de la API de usuario llamada, la cual puede
|
|
ser detectada dependiendo de la función de la biblioteca subyacente que fue desencadenda.
|
|
</para>
|
|
<informaltable>
|
|
<tgroup cols="3">
|
|
<colspec colwidth="1*"/>
|
|
<colspec colwidth="7*"/>
|
|
<colspec colwidth="2*"/>
|
|
<thead>
|
|
<row>
|
|
<entry>Llamada a la bliblioteca</entry>
|
|
<entry>Notas</entry>
|
|
<entry>Versión</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<literal>change_user()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por la llamda a la API de usuario de <function>mysqli_change_user</function>.
|
|
También es desencadenada una vez que se reutiliza una conexión persistente
|
|
de <literal>mysqli</literal>.
|
|
</entry>
|
|
<entry>Desde 1.0.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>select_db</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysql_select_db</function>,
|
|
<function>mysql_list_tables</function>,
|
|
<function>mysql_db_query</function>,
|
|
<function>mysql_list_fields</function>,
|
|
<function>mysqli_select_db</function>.
|
|
Observe que <literal>USE</literal> de SQL no se monitoriza.
|
|
</entry>
|
|
<entry>Desde 1.0.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_charset()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysql_set_charset</function>.
|
|
<function>mysqli_set_charset</function>.
|
|
Observe que <literal>SET NAMES</literal> de SQL no se monitoriza.
|
|
</entry>
|
|
<entry>Desde 1.0.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_server_option()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysqli_multi_query</function>,
|
|
<function>mysqli_real_query</function>,
|
|
<function>mysqli_query</function>,
|
|
<function>mysql_query</function>.
|
|
</entry>
|
|
<entry>Desde 1.0.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_client_option()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysqli_options</function>,
|
|
<function>mysqli_ssl_set</function>,
|
|
<function>mysqli_connect</function>,
|
|
<function>mysql_connect</function>,
|
|
<function>mysql_pconnect</function>.
|
|
</entry>
|
|
<entry>Desde 1.0.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_autocommit()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysqli_autocommit</function>,
|
|
<literal>PDO::setAttribute(PDO::ATTR_AUTOCOMMIT)</literal>.
|
|
</entry>
|
|
<entry>Desde 1.0.0. PHP >= 5.4.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>ssl_set()</literal>
|
|
</entry>
|
|
<entry>
|
|
Invocada por las sigientes llamadas a la API de usuario:
|
|
<function>mysqli_ssl_set</function>.
|
|
</entry>
|
|
<entry>Desde 1.1.0.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>Difusión y conexiones retardadas</para>
|
|
<para>
|
|
El complemento no delega o
|
|
<quote>recuerda</quote> todas las configuraciones para aplicarlas a conexiones
|
|
abiertas en el futuro. Es importante recordar esto si
|
|
se usan
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.lazy-connections">conexiones retardadas</link>.
|
|
Las conexiones retardadas son conexiones que no se
|
|
abren antes de que el cliente envíe la primera conexión.
|
|
El uso de conexiones retardadas es la acción predeterminada del complemento.
|
|
</para>
|
|
<para>
|
|
Cada una de las siguientes llamadas a la biblioteca de conexiones cambia el estado, y su ejecución es
|
|
recordada para un uso posterior cuando se abren las conexiones retardadas. Esto ayuda a asegurarse de que
|
|
los estados de conexión de todas las conexiones de la agrupación de conexiones sean equiparables.
|
|
</para>
|
|
<informaltable>
|
|
<tgroup cols="3">
|
|
<colspec colwidth="1*"/>
|
|
<colspec colwidth="7*"/>
|
|
<colspec colwidth="2*"/>
|
|
<thead>
|
|
<row>
|
|
<entry>Llamada a la biblioteca</entry>
|
|
<entry>Notas</entry>
|
|
<entry>Versión</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<literal>change_user()</literal>
|
|
</entry>
|
|
<entry>
|
|
Usuario, contraseña y base de datos recordados para un uso posterior.
|
|
</entry>
|
|
<entry>Desde 1.1.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>select_db</literal>
|
|
</entry>
|
|
<entry>
|
|
Bases de datos recordada par un uso posterior.
|
|
</entry>
|
|
<entry>Desde 1.1.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_charset()</literal>
|
|
</entry>
|
|
<entry>
|
|
Las llamadas a <literal>set_client_option(MYSQL_SET_CHARSET_NAME, charset)</literal>
|
|
sobre conexiones retardadas aseguran que <literal>charset</literal> será utilizado
|
|
una vez abierta la conexión retardada.
|
|
</entry>
|
|
<entry>Desde 1.1.0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<literal>set_autocommit()</literal>
|
|
</entry>
|
|
<entry>
|
|
Añade <literal>SET AUTOCOMMIT=0|1</literal> a la lista de comandos 'init'
|
|
en una conexión retardada usando
|
|
<literal>set_client_option(MYSQL_INIT_COMMAND, "SET AUTOCOMMIT=...%quot;)</literal>.
|
|
</entry>
|
|
<entry>Desde 1.1.0. PHP >= 5.4.0.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
|
|
<caution>
|
|
<title>Estado de la conexión</title>
|
|
<para>
|
|
El estado de la conexión no sólo se cambia mediante las llamadas a la API. Incluso si
|
|
PECL mysqlnd_ms monitoriza todas las llamadas a la API, la aplicación aún deben
|
|
considerarlo. En última instancia, es responsabilidad de las aplicaciones guardar
|
|
el estado de la conexión, si fuera necesario.
|
|
</para>
|
|
</caution>
|
|
|
|
<para>Conjunto de caracteres y escape de cadenas</para>
|
|
<para>
|
|
Debido al uso de conexiones retardadas, las cuales se emplean predeterminadamente, puede ocurrir que
|
|
una aplicación intente escapar una cadena para usarla dentro de sentencias SQL antes
|
|
de que una conexión haya sido establecida. En este caso, no es posible escapar la cadena.
|
|
La función de escapado de cadenas no conoce el conjunto de caracteres a usar antes de que una
|
|
conexión haya sido establecida.
|
|
</para>
|
|
<para>
|
|
Para superar este problema, el nuevo ajuste de configuración
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.server-charset"><literal>server_charset</literal></link>
|
|
se introdujo en la versión 1.4.0.
|
|
</para>
|
|
<para>
|
|
Se debe prestar atención al escapar cadenas con ciertos conjuntos de caracteres si no es usando
|
|
el resultado en una conexión que utilice un conjunto de caracteres diferente. Observe
|
|
que PECL/mysqlnd_ms manipula las conexiones y que una conexión a nivel de aplicación
|
|
representa una agrupación de múltiples conexiones donde todas pueden tener diferentes conjuntos de caracteres predeterminados.
|
|
Se recomienda configurar los servidores involucrados para que utilicen el mismo conjunto de caracteres predeterminado.
|
|
El ajuste de configuración <literal>server_charset</literal> también ayuda con esta situación.
|
|
Si se usa <literal>server_charset</literal>, el complemento establecerá el conjunto de caracteres
|
|
dado en todas las conexiones recientemente abiertas.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.transaction">
|
|
<title>Manejo de transacciones locales</title>
|
|
<para>
|
|
El manejo de transacciones ha cambiado de manera fundamental.
|
|
Una transacción SQL es una unidad de trabajo que se ejecuta en un servidor de bases de datos. La
|
|
unidad de trabajo consiste en una o más sentencias SQL.
|
|
</para>
|
|
<para>
|
|
De manera predeterminada, el complemento no considera las transacciones SQL. El complemento podría
|
|
intercambiar conexiones para equilibrar la carga en cualquier momento. Los intercambios
|
|
de conexión puede ocurrir en mitad de una transacción. Esto va contra la naturaleza
|
|
de una transacción SQL. De manera predeterminada, el complemento no es seguro con transacciones.
|
|
</para>
|
|
<para>
|
|
A cualquier tipo de equilibrador de carga de MySQL se le debe indicar el comienzo y el final de una
|
|
transacción. Esto se puede realizar implícitamente monitorizando las llamadas a la API
|
|
o usando sugerencias SQL. El complemento admite ambas opciones, dependiendo de la
|
|
versión de PHP. La monitorización de la API requiere PHP 5.4.0 o posterior. El complemento,
|
|
al igual que cualquier otro equilibrador de carga de MySQL, no puede detectar los límites de una transacción basada
|
|
en el Protocolo Cliente Servidor de MySQL. Por lo tanto, la consideración del equilibrado de carga de
|
|
transacciones totalmente transparente no es posible. La opción menos intrusiva es la monitorización
|
|
de la API, que requiere pocos o ningún cambio en la aplicación, dependiendo de
|
|
la misma.
|
|
</para>
|
|
<para>
|
|
Se pueden encontrar ejemplos del uso de sugerencias SQL o de la monitorización de la API en la
|
|
<link linkend="mysqlnd-ms.quickstart">sección de ejemplos</link>. Los
|
|
detalles trás la monitorización de la API, la cual hace que el complemento considere las
|
|
transacciones, están descritos abajo.
|
|
</para>
|
|
<para>
|
|
Desde PHP 5.4.0, la biblioteca <link linkend="book.mysqlnd">mysqlnd</link>
|
|
permite al complemento sobrescribir la llamada a <literal>set_autocommit()</literal>
|
|
de la API en C de la biblioteca, para detectar el estado del modo
|
|
<literal>autocommit</literal>.
|
|
</para>
|
|
<para>
|
|
Las extensiones de MySQL para PHP emiten una consulta (como <literal>SET AUTOCOMMIT=0|1</literal>),
|
|
o usan la llamada a la biblioteca mysqlnd <literal>set_autocommit()</literal> para controlar
|
|
el ajuste <literal>autocommit</literal>. Si una extensión hace uso de
|
|
<literal>set_autocommit()</literal>, el complemento puede considerar las transacciones.
|
|
Hacer que el complemento considere transacciones no se puede realizar si se usa SQL para establecer el modo
|
|
'autocommit'.
|
|
La función <literal>set_autocommit()</literal> de la biblioteca es invocada por las
|
|
llamadas a la API de <function>mysqli_autocommit</function> y
|
|
<literal>PDO::setAttribute(PDO::ATTR_AUTOCOMMIT)</literal>.
|
|
</para>
|
|
<para>
|
|
La opción de configuración del complemento
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.trx-stickiness">trx_stickiness=master</link>
|
|
se puede usar para hacer que el complemento considere las transacciones. De este modo, el complemento detiene el
|
|
equilibrado de cara si 'autocommit' se deshabilita, y dirige todas las sentencias al maestro hasta que
|
|
'autocommit' se habilite.
|
|
</para>
|
|
<para>
|
|
Una aplicación que no requiera establecer las sugerencias SQL para las transacciones pero que requiera
|
|
el uso de una monitorización transparente de la API para evitar cambios en la aplicación debe asegurarse
|
|
de que los ajustes del modo autocommit son cambiados exclusivamente a través de las llamadas
|
|
a la API enumeradas.
|
|
</para>
|
|
<para>
|
|
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 <function>mysqli_autocommit</function>,
|
|
sino también a <function>mysqli_begin</function>,
|
|
<function>mysqli_commit</function> y <function>mysqli_rollback</function>.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.errorhandling">
|
|
<title>Manejo de errores</title>
|
|
<para>
|
|
Las aplicaciones que usan PECL/mysqlnd_ms deberían implementar el manejo de errores apropiado
|
|
para todas las llamadas a la API del usuario. Ya que el complemento cambia la semántica
|
|
de un gestor de conexión, las llamadas a la API pueden devolver errores inesperados. Si se usa
|
|
el complemento sobre un gestor de conexión que ya no represeta una conexión de red
|
|
individual, sino una agrupación de conexiones, se establecerá un código de error y un
|
|
mensaje de error en el gestor de conexión siempre que ocurra un error en cualquier conexión
|
|
de red subyacente.
|
|
</para>
|
|
<para>
|
|
Si se usan conexiones retardadas, que es lo predeterminado, las conexiones no son
|
|
abiertas hasta que sean necesarias para ejecutar consultas. Por lo tanto,
|
|
una llamada a la API para la ejecución de una senetencia puede devolver un error de conexion.
|
|
En el ejemplo de abajo, se provoca un error al intentar ejecutar una sentencia en un esclavo.
|
|
La apertura de la conexión esclava fallará debido a que el fichero de configuración del complemento
|
|
incluye un nombre de anfitrión no válido para el esclavo.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Provocar un error de conexión</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
{
|
|
"myapp": {
|
|
"master": {
|
|
"master_0": {
|
|
"host": "localhost",
|
|
"socket": "\/tmp\/mysql.sock"
|
|
}
|
|
},
|
|
"slave": {
|
|
"slave_0": {
|
|
"host": "nombre_host_inváido",
|
|
}
|
|
},
|
|
"lazy_connections": 1
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
La activación explícita de conexiones retardadas sólo es con propósito de demostración.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Error de conexión en la ejecución de una consulta</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$mysqli = new mysqli("myapp", "nombre_usuario", "contraseña", "base_datos");
|
|
if (mysqli_connect_errno())
|
|
/* Por supuesto, su manejo de errores es más agradable... */
|
|
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
|
|
|
|
/* Conexión 1, la conexión vincula una variable SQL de usuario, no se ejecuta ningún SELECT en el maestro */
|
|
if (!$mysqli->query("SET @myrole='master'")) {
|
|
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
|
|
}
|
|
|
|
/* Conexión 2, se ejecuta en el esclavo a causa de SELECT, provoca un error de conexión */
|
|
if (!($resultado = $mysqli->query("SELECT @myrole AS _role"))) {
|
|
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
|
|
} else {
|
|
$fila = $resultado->fetch_assoc();
|
|
$resultado->close();
|
|
printf("@myrole = '%s'\n", $fila['_role']);
|
|
}
|
|
$mysqli->close();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
PHP Warning: mysqli::query(): php_network_getaddresses: getaddrinfo failed: Name or service not known in %s on line %d
|
|
PHP Warning: mysqli::query(): [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (trying to connect via tcp://invalid_host_name:3306) in %s on line %d
|
|
[2002] php_network_getaddresses: getaddrinfo failed: Name or service not known
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
Se espera que la aplicaciones manejen los posibles errores de conexión mediante
|
|
la implementación del manejo de errores apropiado.
|
|
</para>
|
|
<para>
|
|
Dependiendo del caso en uso, las aplicaciones pueden optar por manejar errores de conexión
|
|
de forma diferente a otros errores. Los errores de conexión típicos son
|
|
<literal>2002 (CR_CONNECTION_ERROR) - Can't connect to local MySQL server through socket '%s' (%d)</literal>,
|
|
<literal>2003 (CR_CONN_HOST_ERROR) - Can't connect to MySQL server on '%s' (%d)</literal> y
|
|
<literal>2005 (CR_UNKNOWN_HOST) - Unknown MySQL server host '%s' (%d)</literal>.
|
|
Por ejemplo, la aplicación podría comprobar los códigos de error y realizar una
|
|
tolerancia a fallos manual. La filosofía del complemento no ofrece la tolerancia a fallos automática,
|
|
más allá de la tolerancia a fallos del maestro, ya que no es una operación transaparente.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Provocar un error de conexión</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
{
|
|
"myapp": {
|
|
"master": {
|
|
"master_0": {
|
|
"host": "localhost"
|
|
}
|
|
},
|
|
"slave": {
|
|
"slave_0": {
|
|
"host": "invalid_host_name"
|
|
},
|
|
"slave_1": {
|
|
"host": "192.168.78.136"
|
|
}
|
|
},
|
|
"lazy_connections": 1,
|
|
"filters": {
|
|
"roundrobin": [
|
|
|
|
]
|
|
}
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
La activación explícita de conexiones retardadas sólo es con propósito de demostración,
|
|
ya que el equilibrado de carga es de rotación en oposición al tipo predeterminado
|
|
<literal>aleatorio una vez</literal>.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Tolerancia a fallos más básica</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$mysqli = new mysqli("myapp", "nombre_usuario", "contraseña", "base_datos");
|
|
if (mysqli_connect_errno())
|
|
/* Por supuesto, su manejo de errores es más agradable... */
|
|
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
|
|
|
|
/* Conexión 1, la conexión vincula una variable SQL de usuario, no se ejecuta ningún SELECT en el maestro */
|
|
if (!$mysqli->query("SET @myrole='master'")) {
|
|
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
|
|
}
|
|
|
|
/* Conexión 2, primer esclavo */
|
|
$resultado = $mysqli->query("SELECT VERSION() AS _version");
|
|
/* Tolerancia a fallos manual */
|
|
if (2002 == $mysqli->errno || 2003 == $mysqli->errno || 2004 == $mysqli->errno) {
|
|
/* Conexión 3, falló la conexión al primer esclavo, se intenta con el siguiente */
|
|
$resultado = $mysqli->query("SELECT VERSION() AS _version");
|
|
}
|
|
|
|
if (!$resultado) {
|
|
printf("ERROR, [%d] '%s'\n", $mysqli->errno, $mysqli->error);
|
|
} else {
|
|
/* Los mensajes de error se toman de la conexión 3, por lo que no hay errores */
|
|
printf("SUCCESS, [%d] '%s'\n", $mysqli->errno, $mysqli->error);
|
|
$fila = $resultado->fetch_assoc();
|
|
$resultado->close();
|
|
printf("version = %s\n", $fila['_version']);
|
|
}
|
|
$mysqli->close();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
[1045] Access denied for user 'nombre_usuario'@'localhost' (using password: YES)
|
|
PHP Warning: mysqli::query(): php_network_getaddresses: getaddrinfo failed: Name or service not known in %s on line %d
|
|
PHP Warning: mysqli::query(): [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (trying to connect via tcp://invalid_host_name:3306) in %s on line %d
|
|
SUCCESS, [0] ''
|
|
version = 5.6.2-m5-log
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
En algunos casos, puede no ser posible la recuperación de forma sencilla de todos los errores que
|
|
ocurran en todas las conexiones de red a través de un gestor de conexión. Por ejemplo, se asume que
|
|
un gestor de conexión representa una agrupación de tres conexiones abiertas. Una conexión
|
|
a un maestro y dos conexiones a los esclavos. La aplicación cambia la
|
|
base de datos actual usando la llamada a la API de usuario <function>mysqli_select_db</function>,
|
|
la cual luego llama a la función de la biblioteca mysqlnd para cambiar el esquema.
|
|
mysqlnd_ms monitoriza la función e intenta cambiar la base de datos
|
|
ctual en todas las conexiones para armonizar sus estados. Ahora, se asume que el maestro tiene
|
|
éxito al cambiar la base de datos, y ambos esclavos fallan. Durante el error inicial
|
|
del primer esclavo, el complemento establecerá un error apropiado sobre en el
|
|
gestor de conexión. Y lo mismo se hace cuando falla el segundo esclavo al cambiar la
|
|
base de datos. El mensaje de error del primer esclavo se pierde.
|
|
</para>
|
|
<para>
|
|
Tales casos pueden ser depurados comprobando los errores de tipo
|
|
<literal>E_WARNING</literal> (véase arriba) o, si no queda otra opción, investigar
|
|
el <link linkend="mysqlnd-ms.debugging">registro de depuración y rastreo de mysqlnd_ms</link>.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.transient_errors">
|
|
<title>Errores transitorios</title>
|
|
<para>
|
|
Algunos clústeres de bases de datos distribuidas hacen usan errores transitorios. Un error
|
|
transitorio es un error temporal que es probable que dessaparezca pronto. Por definición,
|
|
es seguro para un cliente el ignorar un error transitorio y reintentar la operación
|
|
fallida en el mismo servidor de bases de datos. El reintento está libre de efectos segundarios.
|
|
Los clientes no están forzados a abortar su trabajo o recurrir a otro servidor de bases de datos
|
|
de forma inmediata. Podrían entrar en un bucle de reintentos antes de esperar a que el error
|
|
desaparezca antes de dejar el servidor de bases de datos.
|
|
Los errores transitorios pueden ser vistos, por ejemplo, al usar MySQL Cluster. Aunque
|
|
no están limitados a ninguan solución de clústeres específica per se.
|
|
</para>
|
|
<para>
|
|
<literal>PECL/mysqlnd_ms</literal> puede realizar un bucle de reintentos automático en
|
|
caso de darse un error transitorio. Esto aumento la transparencia de la distribución y, por lo tanto,
|
|
hace más sencillo migrar una aplicación ejecutando en un único servidor de bases
|
|
de datos para ejecutar un clúster de servidores de bases de datos sin tener que cambiar
|
|
el origen de la aplicación.
|
|
</para>
|
|
<para>
|
|
El bucle de reintentos automáticos repetirá la operación solicitada hasta un número
|
|
de veces configurable por el usuario y hará una pausa entre intentos por una cantidad de
|
|
tiempo configurable. Si el error desaparece durante el bucle, la aplicación nunca
|
|
lo verá. Si no, el error es reenviado a al aplicación para su manejo.
|
|
</para>
|
|
<para>
|
|
En el ejemplo de abajo se provoca un error de clave duplicada para hacer que el complemento
|
|
reintente la consulta fallida dos vedes antes de que el error sea pasado a la aplicación.
|
|
Entre los dos intentos, el complemento duerme durante 100 milisegundos.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Provocar un error transitorio</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
mysqlnd_ms.enable=1
|
|
mysqlnd_ms.collect_statistics=1
|
|
]]>
|
|
</programlisting>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
{
|
|
"myapp": {
|
|
"master": {
|
|
"master_0": {
|
|
"host": "localhost"
|
|
}
|
|
},
|
|
"slave": {
|
|
"slave_0": {
|
|
"host": "192.168.78.136",
|
|
"port": "3306"
|
|
}
|
|
},
|
|
"transient_error": {
|
|
"mysql_error_codes": [
|
|
1062
|
|
],
|
|
"max_retries": 2,
|
|
"usleep_retry": 100
|
|
}
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Bucle de reintentos del error transitorio</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$mysqli = new mysqli("myapp", "username", "password", "database");
|
|
if (mysqli_connect_errno())
|
|
/* Por supuesto, su manejo de errores es mejor... */
|
|
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
|
|
|
|
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
|
|
!$mysqli->query("CREATE TABLE test(id INT PRIMARY KEY)") ||
|
|
!$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
|
|
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
|
|
}
|
|
|
|
/* El bucle de reintentos es completamente transaparente. Verificar las estadísticas es
|
|
la única manera de conocer algo sobre los reintentos implícitos */
|
|
$stats = mysqlnd_ms_get_stats();
|
|
printf("Intentos del error transitorio antes del error: %d\n", $stats['transient_error_retries']);
|
|
|
|
/* Provocar un error de clave duplicada para ver el cambio en las estadísticas */
|
|
if (!$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
|
|
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
|
|
}
|
|
|
|
$stats = mysqlnd_ms_get_stats();
|
|
printf("Intentos del error transitorio después del error: %d\n", $stats['transient_error_retries']);
|
|
|
|
$mysqli->close();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
Intentos del error transitorio antes del error: 0
|
|
[1062] Duplicate entry '1' for key 'PRIMARY'
|
|
Intentos del error transitorio después del error: 2
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
Ya que la ejecución del bucle de reintentos es transparente desde el punt de vista
|
|
de los usuarios, el ejemplo comprueba las
|
|
<link linkend="function.mysqlnd-ms-get-stats">estadísticas</link>
|
|
proporcionadas por el complemento para aprender de ellas.
|
|
</para>
|
|
<para>
|
|
Como muetra el ejemplo, el complemento puede ser instruido para considerar cualquier error
|
|
transitorio a pesar de la semámtica de los errores de los servidores de bases de datos. El único error
|
|
que cosidera temporal un stock de servidor MySQL tiene el código de error
|
|
<constant>1297</constant>. Al configurar otror códigos de errores excepto el
|
|
<constant>1297</constant>, asegúrese de que la configuración refleja
|
|
la semántica de los códigos de error del clúster.
|
|
</para>
|
|
<para>
|
|
Las siguientes llamadas a las API en C de mysqlnd están monitorizadas por el complemento para
|
|
comprobar errores transitorios: <literal>query()</literal>,
|
|
<literal>change_user()</literal>, <literal>select_db()</literal>,
|
|
<literal>set_charset()</literal>, <literal>set_server_option()</literal>
|
|
<literal>prepare()</literal>, <literal>execute()</literal>,
|
|
<literal>set_autocommit()</literal>,
|
|
<literal>tx_begin()</literal>, <literal>tx_commit()</literal>,
|
|
<literal>tx_rollback()</literal>, <literal>tx_commit_or_rollback()</literal>.
|
|
Las llamadas a la API de usuario correspondientes tienen nombres similares.
|
|
</para>
|
|
<para>
|
|
El tiemp máximo que el complemento puede dormir durante el bucle de reintentos depende de la
|
|
función en cuestión. Un bucle de reintentos para <literal>query()</literal>,
|
|
<literal>prepare()</literal> o <literal>execute()</literal> dormirá
|
|
hasta <literal>max_retries * usleep_retry</literal> milisegundos.
|
|
</para>
|
|
<para>
|
|
Sin embargo, las funciones que
|
|
<link linkend="mysqlnd-ms.pooling">controlan el estado de conexión</link>
|
|
son despachadas para todas las conexiones. Los ajustes del bucle de reintentos están aplicados
|
|
a cada conexión sobre la cual el comando se está ejecutando. Así, tales funciones
|
|
podrían interrumpir la ejecución del programa durante más tiempo que una función que se ejecute
|
|
sobre únicamente un servidor. Por ejemplo, <literal>set_autocommit()</literal> es
|
|
despachada para conexiones y podría dormir hasta
|
|
<literal>(max_retries * usleep_retry) * number_of_open_connections)</literal>
|
|
milisegundos. Por favor, mantenga esto en mento al configurar tiempos de dormir largos
|
|
y números de reintentos grandes. Con los ajustes preestablecidos de
|
|
<literal>max_retries=1</literal>, <literal>usleep_retry=100</literal> y
|
|
<literal>lazy_connections=1</literal> es improbable que vea
|
|
un retarno de más de un segundo.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.failover">
|
|
<title>Tolerancia a fallos</title>
|
|
<para>
|
|
De forma predeterminada, el manejo de la toleracia a fallos de conexiones se le deja al usuario.
|
|
La aplicación es responsable
|
|
de comprobar los valores devueltos de las funciones de bases de datos a las que llama y
|
|
reaccionar a posibles errores. Si, por ejemplo, el complemento reconoce una consulta como de
|
|
solo lectura para ser enviada a los servidores esclavos, y el servidor esclavo seleccionado por
|
|
el complemento no está disponible, el complemento emitirá un error después de no ejecutar
|
|
la sentencia.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Predeterminado: tolerancia a fallos manual</emphasis>
|
|
</para>
|
|
<para>
|
|
Es responsabilidad de
|
|
la aplicación manejar el error y, si fuera necesario, reemitir la consulta para
|
|
desencadenar la selección de otro servidor esclavo para la ejecución de la sentencia.
|
|
El complemento no intentará la tolerancia a fallos automáticamente, ya que
|
|
no puede estar seguro de que una tolerancia a fallos automática cambie el estado de
|
|
la conexión. Por ejemplo, la aplicación podría haber emitido una consulta
|
|
que depende de variables SQL del usuario, las cuales están vinculadas a una conexión específica.
|
|
Tal conexión podría devolver resultados incorrectos si el complemento intercambia la
|
|
conexión implícitamente como parte de la tolerancia a fallos automática. Para asegurase de obtener
|
|
resultados correctos, la aplicación debe ocuparse de la tolerancia a fallos, y reconstruir
|
|
el estado de conexión requerido. Por lo tanto, de forma predeterminada, el complemento no realiza la
|
|
tolerancia a fallos automática.
|
|
</para>
|
|
<para>
|
|
Un usuario que no cambie el estado de la conexión después de abrir una conexión
|
|
puede activar la tolerancia a fallos automática. Observe que la lógica de la tolerancia a fallos
|
|
automática está limitada a intentos de conexión. La tolerancia a fallos automática no se utiliza para
|
|
conexiones ya establecidas. No hay manera de ordenar al complemento que intente la
|
|
toleranca a fallos con una conexión que ya ha sido establecida con MySQL anteriormente.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Tolerancia a fallos automática</emphasis>
|
|
</para>
|
|
<para>
|
|
La política de la tolerancia a fallos se configura en el fichero de configuración del complemento,
|
|
usando la directiva de configuración
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.failover">failover</link>.
|
|
</para>
|
|
<para>
|
|
La tolerancia a fallos automática y silenciosa se puede habilitar a través de la directiva de
|
|
configuración <link linkend="ini.mysqlnd-ms-plugin-config-v2.failover">failover</link>.
|
|
La tolerancia a fallos automática se puede configurar para
|
|
intentarla en exactamente un maestro después del fallo de un esclavo, o, alternativamente, iterando
|
|
sobre los esclavos y maestros antes de devolver un error al usuario. El número
|
|
de intentos de conexión se puede limitar, y los equipos anfitriones fallidos se pueden excluir
|
|
de los intentos de equilibrado de carga futuros. La limitación del número de reintentos y
|
|
el recuerdo de los equipos anfitriones fallidos son considerados características experimentales, aunque son
|
|
razonablemente estables. La sintaxis y la semántica puede cambiar en versiones futuras.
|
|
</para>
|
|
<para>
|
|
Observe que desde la versión 1.5.0 la tolerancia a fallos automática está deshabilitada
|
|
durante una transacción si está habilitada la adhesión de transacciones y
|
|
se han detectado los límites de la transacción. El complento no intercambiará
|
|
conexiones durante una transacción. Tampoco realizará la tolerancia a fallos
|
|
automática ni silenciosa. En su lugar se lanzará un error. Es entoces responsabilidad
|
|
del usuario el manejo del fallo de una transacción. Por favor, consulte la documetación de
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.trx-stickiness"><literal>trx_stickiness</literal></link>
|
|
para saber cómo hacer esto.
|
|
</para>
|
|
<para>
|
|
Se proporciona un ejemplo básico de tolerancia a fallos manual en la sección
|
|
<link linkend="mysqlnd-ms.errorhandling">manejo de errores</link>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Servidores de emergencia</emphasis>
|
|
</para>
|
|
<para>
|
|
Al usar el <link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-random">equilibrado de carga ponderado</link>,
|
|
introducido en PECL/mysqlnd 1.4.0, es posible
|
|
configurar servidores de emergencia que son utilizados escasamente durante operaciones normales.
|
|
A un servidor de emergencia que se usa principalmente como objetivo en el peor de los casos de tolerancia a fallos de emergencia
|
|
se le puede asignar un peso o prioridad muy bajos en relación a los demás servidores.
|
|
Siempre y cuando todos los servidores estén activos y la ejecución de la mayoría del trabajo
|
|
sea asignada a los servidores que tienen valores de peso altos. Pocas peticiones
|
|
serán redirigidas a un sistema de emergencia que tenga un valor de peso muy bajo.
|
|
</para>
|
|
<para>
|
|
Cuando ocurre un fallo en los servidores con una prioridad alta, aún se puede realizar la tolerancia a fallos
|
|
en el sistema de emergencia, al cual se le ha dado una prioridad baja de equilibrado de carga asignándole un
|
|
peso bajo. La tolerancia a fallos puede ser manual o automática. Si se realiza
|
|
automáticamente se puede combinar con la
|
|
opción
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.failover"><literal>remember_failed</literal></link>.
|
|
</para>
|
|
<para>
|
|
En este punto, no es posible ordenar al equilibrador de carga que no dirija ninguna
|
|
petición al sistema de emergencia. Esto podría no ser una limitación dado que
|
|
el mayor peso que se puede asignar a un servidor es 65535. Dados dos esclavos,
|
|
uno de los cuales actuaría como de emergencia y al que se le ha asignado un peso de 1,
|
|
el sistema de emergencia tendrá que manejar mucho menos del 1 por ciento del total del trabajo.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Tolerancia a fallos y copia primaria</emphasis>
|
|
</para>
|
|
<para>
|
|
Observe que si se utiliza un clúster de copia primario, tal como la Replicación MySQL, es
|
|
difícil realizar la tolerancia a fallos de conexión en caso de que falle un maestro.
|
|
En cualquier momento, sólo hay un maestro en el clúster para un conjunto de datos dado.
|
|
El maestro es un único punto de fallo. Si el maestro falla, los clientes no tienen un objetivo donde
|
|
realizar la tolerancia a fallos en peticiones de escritura. En caso de que un maestro apague la base de datos,
|
|
el administrador debe ocuparse de la situación y actualizar la configuración
|
|
del cliente, si fuera necesario.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.loadbalancing">
|
|
<title>Equilibrado de carga</title>
|
|
<para>
|
|
Se admiten cuatro estrategias de equilibrado de carga para distribuir
|
|
sentencias sobre los servidores esclavos de MySQL configurados:
|
|
</para>
|
|
<para>
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>aleatorio</term>
|
|
<listitem>
|
|
<para>
|
|
Se elige un servidor aleatorio cada vez que se ejecute una sentencia.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>aleatorio una vez (predeterminado)</term>
|
|
<listitem>
|
|
<para>
|
|
Se elige un servidor aleatorio después de ejecutar la primera sentencia,
|
|
y se utiliza la decisión tomada para el resto de las peticiones de PHP.
|
|
</para>
|
|
<para>
|
|
Es lo predeterminado, y de menor impacto para el estado de conexión.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>rotación</term>
|
|
<listitem>
|
|
<para>
|
|
Se itera sobre la lista de servidores configurados.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>definido por el usuario mediante una llamada de retorno</term>
|
|
<listitem>
|
|
<para>
|
|
Se utiliza para implementar cualquier otra estrategia.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</para>
|
|
<para>
|
|
La política del equilibrado de carga se configura en el fichero de cofiguración
|
|
de los complementos usando
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-random">random</link>,
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-roundrobin">roundrobin</link>,
|
|
y <link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-user">user</link>
|
|
como <link linkend="mysqlnd-ms.filter">filtro</link>.
|
|
</para>
|
|
<para>
|
|
Los servidores se pueden priorizar asignándoles un peso. Un servidor al que se le haya dado
|
|
un peso de dos obtendrá el doble de peticiones que un servidor que se le haya dado el
|
|
peso predeterminado de uno. La prioridad puede ser muy útil en entornos
|
|
heterogéneos. Por ejemplo, si se quiere asignar más peticiones a
|
|
una máquina potente que una menos potente. O se pueden tener configurados
|
|
servidores que están cerca o lejos del cliente, y así exponer diferentes latencias.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.rwsplit">
|
|
<title>División de lectura-escritura</title>
|
|
<para>
|
|
El complemento ejecuta las senteciss de solo lectura en los esclavos MySQL cofigurados, y
|
|
las demás consultas en el maestro MySQL. Una sentencia se
|
|
considera de solo lectura si empieza con <literal>SELECT</literal>,
|
|
con la sugerencia SQL <literal>/*ms=slave*/</literal>, o si un esclavo ha sido elegido para
|
|
ejecutar la consulta anterior y la consulta empiza con la sugerencia SQL
|
|
<literal>/*ms=last_used*/</literal>. El los demás casos, la consulta
|
|
será enviada al servidor maestro de replicación MySQL. Se recomienda
|
|
usar las constantes <constant>MYSQLND_MS_SLAVE_SWITCH</constant>,
|
|
<constant>MYSQLND_MS_MASTER_SWITCH</constant> y <constant>MYSQLND_MS_LAST_USED_SWITCH</constant>
|
|
en lugar de <literal>/*ms=slave*/</literal>. Véase también la
|
|
<link linkend="mysqlnd-ms.constants">lista de constantes de mysqlnd_ms</link>.
|
|
</para>
|
|
<para>
|
|
Las sugerencias SQL son un tipo especial de comentarios que cumplen el estándar SQL. El
|
|
complemento revisa cada sentencia en busca de ciertas sugerencias SQL. Las sugerencias SQL están
|
|
descritas en la documentación de las <link linkend="mysqlnd-ms.constants">constantes de mysqlnd_ms</link>,
|
|
constantes que son exportadas por la extensión. Otros sistemas involucrados
|
|
en el procesamiento de sentencias, como el servidor MySQL, los cortafuegos SQL,
|
|
y los proxys SQL, no se ven afectados por las sugerencias SQL, ya que estos sistemas están
|
|
diseñados para ignorar los comentarios SQL.
|
|
</para>
|
|
<para>
|
|
El divisor de lectura-escritura interno puede ser reemplazado por un filtro definido por el usuario.
|
|
Véase también la documentación de
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-user">filtros de usuario</link>.
|
|
</para>
|
|
<para>
|
|
Un divisor de lectura-escritura definido por el usuario puede solicitar la lógica interna para
|
|
enviar una sentencia a una ubicación específica invocando a
|
|
<function>mysqlnd_ms_is_select</function>.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
El divisor de lectura-escritura interno no considera las sentencias múltiples.
|
|
Éstas se ven como una sola sentencia. El divisor comprobará el
|
|
comienzo de una sentencia para decidir dónde ejecutarla. Si, por ejemplo,
|
|
una sentencia múltiple comienza con
|
|
<literal>SELECT 1 FROM DUAL; INSERT INTO test(id) VALUES (1); ...</literal>
|
|
el complemento la ejecutará en un esclavo aunque la sentencia no sea de solo lectura.
|
|
</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.filter">
|
|
<title>Filtro</title>
|
|
<note>
|
|
<title>Requisitos de versión</title>
|
|
<para>
|
|
Los filtros existen a partir de la versión 1.1.0-beta de mysqlnd_ms.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
<link linkend="mysqlnd-ms.plugin-ini-json">filtros</link>.
|
|
Las aplicaciones de PHP que implementen un clúster de replicación MySQL deben identificar primero
|
|
un grupo de servidores en el clúster que podrían ejecutar una sentencia antes
|
|
de que ésta sea ejecutada por uno de los candidatos. En otras palabras: se debe filtrar
|
|
una lista de servidores definidos hasta que esté disponible solamente un servidor.
|
|
</para>
|
|
<para>
|
|
El proceso de filtrado puede incluir el uso de uno o más filtros, los cuales pueden estar
|
|
encadenados. Son ejecutados en el orden en el que están definidos en el fichero de configuración
|
|
del complemento.
|
|
</para>
|
|
<note>
|
|
<title>Aclaración: comparación entre cadena de filtros y tuberías</title>
|
|
<para>
|
|
El concepto de filtros encadenados puede ser comparado con el uso de tuberías para conectar
|
|
utilidades de línea de comandos en un sistema operativo intérprete de comandos. Por ejemplo,
|
|
un flujo de entrada se pasa al procesador, se filtra, y luego se transfiere
|
|
para su salida. Entonces, la salida se pasa al siguiente comando,
|
|
el cual está conectado al anterior mediante el operador de tubería.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
Filtros disponibles:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Filtros de equilibrado de carga:
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filters">random</link> y
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filters">roundrobin</link>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Filtros de selección:
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filters">user</link>,
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filters">user_multi</link>,
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filters">quality_of_service</link>.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
<para>
|
|
El filtro <literal>random</literal> implementa las políticas de equilibrado de carga
|
|
'aleatoria' y 'aleatorio una vez'. El equilibrado de carga 'rotacional' se puede configurar
|
|
a través del filtro <literal>roundrobin</literal>. Estblecer una 'llamada de retorno
|
|
definida por el usuario' para la selección del servidor es posible con filtro <literal>user</literal>.
|
|
El filtro <literal>quality_of_service</literal> busca nodos del clúster
|
|
capaces de cumplir con un cierto servicio, por ejemplo, la lectura de sus escrituras, o
|
|
no demorar más segundos de lo permitido con respecto al maestro.
|
|
</para>
|
|
<para>
|
|
Los filtros puede aceptar parámetros para cambiar su comportamiento.
|
|
El filtro <literal>random</literal> acepta el parámetro opcional
|
|
<literal>sticky</literal>. Si se establece a 'true', el filtro cambia
|
|
el equilibrado de carga de 'aleatorio' a 'aleatorio una vez'. 'Aleatorio' escoge un servidor aleatorio
|
|
cada vez qeu se vaya a ejecutar una sentencia. 'Aleatorio una vez' escoge un servidor aleatorio
|
|
cuando la primera sentencia va a ser ejecutada y utiliza el mismo
|
|
servidor para el resto de peticiones de PHP.
|
|
</para>
|
|
<para>
|
|
Una de los puntos fuertes del concepto de filtro es la posibilidad de
|
|
encadenar filtros. Este punto fuerte no se hace inmediatamente visible debido a que
|
|
los filtros <literal>random</literal>, <literal>roundrobin</literal> y
|
|
<literal>user</literal> deben de producir no más de un servidor.
|
|
Si un filtro reduce la lista de candidatos para la ejecución de una sentencia a
|
|
un único servidor, tiene poco sentido usar ese servidor como
|
|
entrada de otro filtro para reducir aún más la lista de candidatos.
|
|
</para>
|
|
<para>
|
|
Un ejemplo de una secuencia de filtros que fallará:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Sentencia a ejecutar: <literal>SELECT 1 FROM DUAL</literal>. Pasada a todos los filtros.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Todos los nodos configurados son pasados como entrada al primer filtro.
|
|
Nodos maestro: <literal>master_0</literal>.
|
|
Nodos esclavo:<literal>slave_0</literal>, <literal>slave_1</literal>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Filtro: <literal>random</literal>, argumento <literal>sticky=1</literal>.
|
|
Elige un esclavo aleatorio una vez para utilizarlo para el resto de peticiones de PHP.
|
|
Salida: <literal>slave_0</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La salida de <literal>slave_0</literal> y la sentencia a ejecutar
|
|
son pasadas como entradas al siguiente filtro. Aquí: <literal>roundrobin</literal>,
|
|
la lista de servidores pasada para filtrar es: <literal>slave_0</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Filtro: <literal>roundrobin</literal>. La lista de servidores consisten en
|
|
un solo servidor, la rotación siempre devolverá el mismo servidor.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
Si se intenta usar esta secuencia de filtros,
|
|
el complemento puede emitir una advertencia como <literal>(mysqlnd_ms) Error while creating
|
|
filter '%s' . Non-multi filter '%s' already created. Stopping in %s on
|
|
line %d</literal>. Además, se puede establecer un error apropiado para el gestor
|
|
de conexión.
|
|
</para>
|
|
<para>
|
|
Existe un segundo tipo de filtro: el filtro múltiple. Un filtro múltiple emite cero, uno o múltiples
|
|
servidores después del procesamiento. El filtro <literal>quality_of_service</literal>
|
|
es un ejemplo. Si la calidad de servicio solicitada establece un límite superior para la demora del
|
|
esclavo y más de un esclavo se demora menos que el número de segundos permitido,
|
|
el filtro devuelve más de un nodo del clúster. Un filtro múltiple debe ser seguido de otro
|
|
para reducir aún más la lista de candidatos para la ejecución de sentencias hasta que se encuentre
|
|
un candidato.
|
|
</para>
|
|
<para>
|
|
Una secuencia de filtros con el filtro múltiple <literal>quality_of_service</literal>
|
|
seguido por un filtro de equilibrado de carga.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Sentencia a ejecutar: <literal>SELECT sum(price) FROM orders WHERE order_id = 1</literal>.
|
|
Pasada a todos los filtros.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Todos los nodos configurados son pasados como entrada para el primer filtro.
|
|
Nodos maestro: <literal>master_0</literal>.
|
|
Nodos esclavo: <literal>slave_0</literal>, <literal>slave_1</literal>,
|
|
<literal>slave_2</literal>, <literal>slave_3</literal>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Filtro: <literal>quality_of_service</literal>, conjunto de reglas: session_consistency (lectura de sus escrituras)
|
|
Salida: <literal>master_0</literal>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La salida de <literal>master_0</literal>
|
|
y la sentencia a ejecutar
|
|
son pasadas como entrada al siguiente filtro, el cual es <literal>roundrobin</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Filtro: <literal>roundrobin</literal>. La lista de servidores consiste en un
|
|
servidor. La rotación selecciona <literal>master_0</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
Una secuencia de filtros no debe finalizar con un filtro múltiple. Si se intenta usar
|
|
una sencuencia de filtros que finaliza con un filtro múltiple, el complemento puede emitir
|
|
una advertencias como <literal>(mysqlnd_ms) Error in configuration. Last filter is multi
|
|
filter. Needs to be non-multi one. Stopping in %s on line %d</literal>.
|
|
Además, se puede establecer un error apropiado para el gestor
|
|
de conexión.
|
|
</para>
|
|
<para>
|
|
<note>
|
|
<title>Especulación sobre el futuro: filtrado de replicación MySQL</title>
|
|
<para>
|
|
En futuras versiones, pueden existir filtros múltiples adicionales.
|
|
Por ejemplo podría existir un filtro <literal>table</literal>
|
|
para soportar el filtrado de replicación MySQL. Esto podría permitir
|
|
la definición de reglas para qué base de datos o tabla van a ser replicadas a cual
|
|
nodo de un clúster de replicación. Se asume que el clúster de replicación
|
|
consiste en cuatro esclavos (<literal>slave_0</literal>, <literal>slave_1</literal>,
|
|
<literal>slave_2</literal>, <literal>slave_3</literal>), dos de los cuales replican una base de datos llamada
|
|
<literal>ventas</literal> (<literal>slave_0</literal>, <literal>slave_1</literal>).
|
|
Si la aplicación pregunta a la base de datos <literal>esclavos</literal>, el
|
|
hipotético filtro <literal>table</literal> reduciría la lista de posibles
|
|
servidores a <literal>slave_0</literal> y <literal>slave_1</literal>. Ya que
|
|
la salida y la lista de candidatos consiste en más de un servidor, es
|
|
posible y necesario añadir filtros adicionales a la lista de candidatos, por ejemplo, usando
|
|
un filtro de equilibrado de carga para identificar un servidor para la ejecución de sentencias.
|
|
</para>
|
|
</note>
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.qos-consistency">
|
|
<title>Nivel de servicio y consistencia</title>
|
|
<note>
|
|
<title>Requisitos de versión</title>
|
|
<para>
|
|
Los niveles de servicio han sido introducidos en la versión 1.2.0-alpha de mysqlnd_ms.
|
|
<function>mysqlnd_ms_set_qos</function>
|
|
requiere PHP 5.4.0 o posterior.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
El complemento se puede usar con diferentes tipos de clústeres de bases de datos MySQL.
|
|
Los diferentes clústeres pueden proporcionar diferentes niveles de servicios a las aplicaciones.
|
|
Los niveles de servicios pueden ser agrupados por los niveles de consistencia de datos que
|
|
se puedan alcanzar. El complemento está al tanto de la:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>consistencia final</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>consistencia de sesión</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>consistencia fuerte</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
Dependiendo de cómo se use un clúster, puede ser posbile alcanzar niveles de servicios
|
|
más altos que el predeterminado. Por ejemplo, una lectura desde un esclavo de replicación
|
|
MySQL asíncrono es consistencia final. Así, uno podría decir que el nivel de consistencia
|
|
predeterminado de un clúster de replicación MySQL es consistencia final.
|
|
Sin embargo, si el maestro solamente es utilizado por un cliente para lectura y escritura durante una
|
|
sesión, entonces se da la consistencia de sesión (lectura de sus escrituras). PECL mysqlnd 1.2.0
|
|
abstrae al usuario los detalles de la elección de un nodo apropiado para cualquiera de los
|
|
niveles de servicios de arriba mencionados.
|
|
</para>
|
|
<para>
|
|
Los niveles de servicios se pueden establecer a través del filtro 'qualify-of-service' en el
|
|
<link linkend="mysqlnd-ms.plugin-ini-json">fichero de configuración del complemento</link>
|
|
y en tiempo de ejecución utilizando la función
|
|
<function>mysqlnd_ms_set_qos</function>.
|
|
</para>
|
|
<para>
|
|
El complemento define los diferentes niveles de servicios como sigue:
|
|
</para>
|
|
<para>
|
|
La consistencia final es el servicio predeterminado proporcionado por un clúster
|
|
asíncrono, como la clásica replicación MySQL. Una operación de lectura ejecutada
|
|
en un nodo arbitrario puede o no devolver datos antiguos. El punto de vista
|
|
de las aplicaciones de los datos es consistente final.
|
|
</para>
|
|
<para>
|
|
La consistencia de sesión se da si un cliente siempre puede leer sus propias escrituras.
|
|
Un clúster de replicación MySQL asíncrona puede proporcionar consistencia de sesión si los clientes
|
|
siempre usan el maestro después del la primera escritura, o nunca consultan un esclavo que aún
|
|
no ha replicado la operación de escritura de los clientes.
|
|
</para>
|
|
<para>
|
|
La interpretación del complemento de la consistencia fuerte es que todos los cliente siempre
|
|
ven las escrituras consignadas de todos los demás clientes. Esto es lo predeterminado cuando
|
|
se usa el Clúster MySQL o cualquier otro clúster que ofrezca
|
|
distribución de datos asíncrona.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Parámetros de los niveles de servicios</emphasis>
|
|
</para>
|
|
<para>
|
|
Los niveles de servicos de consistencia final y consistencia de sesión aceptan parámetros.
|
|
</para>
|
|
<para>
|
|
La consistencia final es el servicio proporcionado por la replicación MySQL clásica.
|
|
De forma predeterminada, todos los nodos tienen derecho a peticiones de lectura. El parámetro opcional
|
|
<literal>age</literal> puede ser proporcionado para filtrar nodos que se demoren más que un cierto número de
|
|
segundos con respecto al maestro. El complemento utiliza <literal>SHOW SLAVE STATUS</literal>
|
|
para medir la demora. Vea el manual de referencia de MySQL para aprender sobre la precición y
|
|
fiabilidad del comando <literal>SHOW SLAVE STATUS</literal>.
|
|
</para>
|
|
<para>
|
|
La consistencia de sesión (lectura de sus escritura) acepta el parámetro opcional <literal>GTID</literal>
|
|
para considerar la lectura no sólo desde el maestro, sino también desde los esclavos
|
|
que ya han replicado una escritura en particular descrita por su identificador de transacciones.
|
|
De esta forma, cuando se utiliza la replicación MySQL asíncrona, las peticiones de lectura pueden uar el
|
|
equilibrado de carga sobre los esclavos mientras todavía se asegura la consistencia de sesión.
|
|
</para>
|
|
<para>
|
|
Lo último requiere el uso de la
|
|
<link linkend="mysqlnd-ms.gtid">inyección de IDs de transacciones globales en el lado del cliente</link>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Ventajas de este nuevo enfoque</emphasis>
|
|
</para>
|
|
<para>
|
|
El nuevo enfoque sustituye el uso de sugerencias SQL y de la opción de configuración
|
|
<literal>master_on_write</literal> en varios sentidos. Si una aplicicón
|
|
se ejecuta en lo más alto de un clúster de replicación MySQL asíncrono, no puede acceptar datos
|
|
antiguos para ciertas lecturas, es más sencillo indicarle al complemento que elija los nodos
|
|
apropiados que prefijar todas las sentencias de lectura en cuestión con sugerencias SQL
|
|
para forzar el uso del maestro. Además, el comlemento puede ser capaz de
|
|
utilizar esclavos seleccionados para la lectura.
|
|
</para>
|
|
<para>
|
|
La opción de configuración <literal>master_on_write</literal> hace que el complemento
|
|
use el maestro después de la primera escritura (consistencia de sesión, lectura de sus escrituras).
|
|
En algunos casos, la consistencia de sesión puede no ser necesaria para el resto de la sesión,
|
|
sólo para unas pocas operaciones de lectura. Así, <literal>master_on_write</literal>
|
|
puede resultar en más carga de lectura en el maestro de la necesaria. En estos casos,
|
|
es mejor solicitar un nivel de servicios superior al predeterminado solamente para aquellas lecturas
|
|
que realmente lo necesiten. Una vez que las lecturas se han realizado, la apliacion puede vovler
|
|
al nivel de servicios predetereminado. El cambio entre niveles de servicio solo es posible
|
|
con el uso de <function>mysqlnd_ms_set_qos</function>.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Consideraciones de rendimiento</emphasis>
|
|
</para>
|
|
<para>
|
|
Un clúster de replicación MySQL no puede indicarle a los clientes qué esclavos son capaces
|
|
de proporcionar cuales niveles de servicios. Por lo tanto, en algunos casos,
|
|
los clientes necesitan consultar a los esclavos para comprobar sus estados.
|
|
PECL mysqlnd_ms ejecuta de forma transparente el SQL necesario en segundo
|
|
plano. Sin embargo, es una operación cara y lenta. Las sentencias SQL
|
|
se ejecutan si se combina la consistencia final con una edad (demora del esclavo) límite y
|
|
si la consistencia de sesión se combina con un ID de transacciones global.
|
|
</para>
|
|
<para>
|
|
Si la consistencia final se combina con una edad (demora del esclavo) máxima, el complemento
|
|
seleccionará candidatos para la ejecución de sentencias y equilibrará la carga para cada sentencia
|
|
como sigue. Si la sentencia es una escritura, todos los maestros son considerados como candidato. Los esclavos
|
|
no se comprueban y no son considerados como candidatos. Si la sentencia es una lectura, el
|
|
complemento ejecuta de forma transparente <literal>SHOW SLAVE STATUS</literal> en cada conexión
|
|
esclava. Iterará sobre todas las conexiones, enviará la sentencia y luego iniciará
|
|
la comprobación de los resultados. Normalmente, esto es ligeramente más rápido qe iterar sobre todas las conexiones
|
|
en las que se envía una consulta para cada conexión y que el complemento espere sus resultados.
|
|
Un esclavo es considerado un candidato si <literal>SHOW SLAVE STATUS</literal> notifica que
|
|
<literal>Slave_IO_Running=Yes</literal>,
|
|
<literal>Slave_SQL_Running=Yes</literal> y
|
|
<literal>Seconds_Behind_Master</literal> es menor o igual que la edad máxima permitida.
|
|
En caso de que ocurra un error SQL, el complemento emite una advertencia, aunque no establece un error en
|
|
la conexión. Éste no se establece para que el complemento se pueda usar como sustituto.
|
|
</para>
|
|
<para>
|
|
Si la consistencia de sesión se combina con un ID de transacciones global, el complemento ejecutará
|
|
la sentencia establecida con la entrada <literal>fetch_last_gtid</literal> de la
|
|
sección <literal>global_transaction_id_injection</literal> del fichero de configuración del complemento.
|
|
Los demás detalles son idénticos a los descritos arriba.
|
|
</para>
|
|
<para>
|
|
En la versión 1.2.0 no se realizan optimizaciones adicionales para ejecutar consultas en segundo plano.
|
|
Futuras versiones pueden contener optimizaciones, dependiendo de la demanda de los usuarios.
|
|
</para>
|
|
<para>
|
|
Si no se establece ningún parámetro ni ninguna opción, no es necesaria ninguna sentencia SQL. En este caso,
|
|
el complemento considera que todos los nodos son del tipo mostrado abajo.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>consistencia final, no se establecen opciones adicionales: todos los maestros, todos los esclavos</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>consistencia de sesión, no se establecen opciones adicionales: todos los maestros</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>consistencia fuerte (no se permiten opcioines): todos los maestros</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Estrangulamiento</emphasis>
|
|
</para>
|
|
<para>
|
|
El filtro de calidad de servicio se puede combinar con
|
|
<link linkend="mysqlnd-ms.gtid">IDs de transacciones globales</link> para
|
|
estrangular clientes. El estrangulamiento reduce la carga de escritura en el maestro
|
|
desacelerando los clientes. Si se solicita la consistencia de sesión y
|
|
se usa el identificador de transacciones global para comprobar el estado de
|
|
un esclavo, esta comprobación se puede hacer de dos maneras. Por omisión, un esclavo
|
|
se comprueba y se salta inmediatamente si no concuerda con
|
|
el criterio de la consistencia de sesión. De forma alternativa, el
|
|
complemento puede esperar a que un esclavo se ponga al día con el maestro hasta
|
|
que la consistencia de sesión sea posible. Para habilitar el estrangulamiento
|
|
se debe establecer la
|
|
opción de configuración
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.gtid">wait_for_gtid_timeout</link>.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.gtid">
|
|
<title>IDs de transacciones globales</title>
|
|
<note>
|
|
<title>Requisitos de versión</title>
|
|
<para>
|
|
La inyección de IDs de transacciones globales en el lado del cliente existe a partir de la versión 1.2.0-alpha de mysqlnd_ms.
|
|
Los límites de una transacción son detectados monitorizando las llamadas a la API. Esto es posible
|
|
a partir de PHP 5.4.0. Por favor, vea también <link linkend="mysqlnd-ms.transaction">Manejo de transacciones</link>.
|
|
</para>
|
|
<para>
|
|
A partir de MySQL 5.6.5-m8, el servidor MySQL introduce los identificadores de transacciones globales internos.
|
|
La característica del ID de transacciones global interno de MySQL es soportada por
|
|
<literal>PECL/mysqlnd_ms</literal> 1.3.0-alpha o
|
|
posterior. No es necesario la monitorización de los límites de transacciones en el lado del cliente ni
|
|
ninguna configuración si se utiliza la característica del servidor.
|
|
</para>
|
|
<para>
|
|
Observe que todas las versiones de producción de MySQL 5.6 no proporcionan
|
|
clientes con suficiente información para usar GTID para forzar la consistencia de sesión.
|
|
En el peor de los casos, el complemento eligirá solamente el maestro.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
<emphasis role="bold">Idea y emulación en el lado del cliente</emphasis>
|
|
</para>
|
|
<para>
|
|
<literal>PECL/mysqlnd_ms</literal> también realiza la inyección transparente de IDs de transacciones globales en el lado del cliente.
|
|
En su forma más básica, un identificador de transacciones global es un contador que se
|
|
incrementa en cada transacción ejecutada en el maestro. El contador se guarda
|
|
en una tabla del maestro. Los esclavos replican la tabla del contador.
|
|
</para>
|
|
<para>
|
|
En caso de que falle un maestro, el adminstrador de la base de datos puede identificar fácilmente el
|
|
esclavo más reciente para promoverlo como nuevo maestro. El esclavo más reciente tiene
|
|
el identificador de transacciones más alto.
|
|
</para>
|
|
<para>
|
|
Los desarrolladores de aplicaciones pueden solicitar al complemento el identificador de transacciones global
|
|
(GTID) para su última operación de escritura que tuvo éxito. El complemento devolverá
|
|
un identificador que hace referencia a una transacción no más antigua que la última operación
|
|
de escritura del cliente. Entonces, el GTID puede ser pasado como parámetro
|
|
al filtro de calidad de servicio (QoS) como una opción para la consistencia de sesión.
|
|
La consistencia de sesión se asegura de la lectura de sus escrituras. El filtro se asegura de que
|
|
todas las lecturas son dirigidas al maestro o a un esclavo que haya replicado la escritura
|
|
referida por el GTID.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Cuándo se realiza la inyección</emphasis>
|
|
</para>
|
|
<para>
|
|
El complemento mantiene de forma transparente la tabla del GTID del maestro.
|
|
En el modo 'autocommit', el complemento inyecta una sentencia <literal>UPDATE</literal>
|
|
antes de ejecutar las sentencias del usuario por cada uso del maestro. En el modo
|
|
de transacción manual, la inyección se realiza antes de que la aplicación llame a
|
|
<literal>commit()</literal> para cerrar la transacción. La opción de configuración
|
|
<literal>report_error</literal> de la sección del GTID del fichero de configuración del
|
|
complemento se utiliza para controlar si una inyección fallida debería abortar la operación
|
|
actual o ser ignorada de forma silenciosa (lo predeterminado).
|
|
</para>
|
|
<para>
|
|
Por favor, observe los
|
|
requisitos de versión de PHP para la
|
|
<link linkend="mysqlnd-ms.transaction">monitorización de los límites de transacciones</link>
|
|
y sus limitaciones.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Limitaciones</emphasis>
|
|
</para>
|
|
<para>
|
|
La inyección de IDs de transacciones globales en el lado del cliente tiene defectos. Los problemas
|
|
potenciales no son específicos de <literal>PECL/mysqlnd_ms</literal>,
|
|
sino que son de naturaleza general.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Las tablas de IDs de transacciones globales deben ser desplegadas en todos los maestros y en todas las réplicas.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
El GTID puede tener agujeros. Únicamente los clientes de PHP que utilicen el complemento
|
|
mantendrán la tabla. Los demás clientes no lo harán.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La detección de los límites de las transacciones en el lado del cliente solamente está basada en llamadas a la API.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
La detección de los límites de las transacciones en el lado del cliente no toma en cuenta
|
|
la consigna implícita. Algunas setnencias SQL de MySQL causan una consignación implícita,
|
|
por lo que no pueden ser revertidas.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Utilizar el identificador de transacciones global en el lado del servidor</emphasis>
|
|
</para>
|
|
<para>
|
|
Desde <literal>PECL/mysqlnd_ms</literal> 1.3.0-alpha
|
|
se admite la característica del identificador de
|
|
transacciones global interno de MySQL 5.6.5-m8 o posterior. El uso de esta característica del servidor
|
|
elimna todas las limitaciones enumeradas arriba. Por favor, lea el Manual de Referencia de MySQL
|
|
para conocer las limitaciones y las precondiciones del uso de identificadores de transacciones globales
|
|
internos.
|
|
</para>
|
|
<para>
|
|
Si se utiliza la emulación del lado del cliente o la funcionalidad interna del
|
|
servidor, es una cuestión que no está directamente relacionada con el complemento, por lo que no
|
|
se trata en profundidad. No hay planes para eliminar la emulación del lado del cliente, por lo que
|
|
se puede continuar usándola, si la solución del lado del servidor no es una opción. Éste podría
|
|
ser el caso de entornos heterogéneos con un servidor MySQL antiguo, o si cualquiera de las
|
|
limitaciones de la solución del lado del servidor no son acpetables.
|
|
</para>
|
|
<para>
|
|
Desde la perspectiva de las aplicaciones casi no hay diferencia en el uso de una estrategia
|
|
u otra. Difieren las siguientes propiedades.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
La emulación en el lado del cliente, como se muestra en el manual, utiliza un número de secuencia
|
|
de transacciones global fácil de comparar. Los maestros múltiples no se tratan para no complicar los ejemplos del manual.
|
|
</simpara>
|
|
<simpara>
|
|
La característica interna del lado del servidor utiliza una combinación de un identificador de servidor
|
|
y un número de secuencia como identificador de transacciones global. La comparación no
|
|
puede hacerse mediante álgebra numérica. En su lugar, se debe usar una función SQL. Por favor,
|
|
lea el Manual de referencia de MySQL para más detalles.
|
|
</simpara>
|
|
<simpara>
|
|
No se puede usar la característica interna del lado del servidor de MySQL 5.6 para asegurar la consistencia de sesión
|
|
bajo ninguna circunstancia. No la utilice para la característica de calidad de servicio. Aquí se expone un ejemplo
|
|
sencillo de por qué no proporcionará resultado fiables. Hay más casos límites que no se pueden
|
|
cubrir con la funcionalidad limitada exportada por el servidor. Actualmente, los clientes solamente pueden
|
|
preguntar al maestro de replicación de MySQL por una lista de todos los ID de transacciones globales ejecutadas.
|
|
Si un esclavo está configurado para no replicar todas las transacciones, por ejemplo, porque están establecidos
|
|
los filtros de replicación, el esclavo nunca mostrará el mismo conjunto de ID de transacciones globales ejecutadas.
|
|
Aunque el esclavo podría haber replicado las escrituras de un cliente y podría ser un candidato
|
|
para una lectura consistente, nunca será considerado por el complemento. En cuanto a la escritura, el complemento
|
|
aprende del maestro que el historial de transacciones de servidores completo consiste en GTID=1..3.
|
|
No hay forma de que el complemento pregunte por el GTID de la transacción de escritura en sí, digamos GTID=3.
|
|
Se asume que el esclavo no replica la transacciones GTID=1..2, sino solamente GTID=3 debido a una
|
|
característica de replicación. Entonces, el historial de transacciones de esclavos es GTID=3. Sin embargo, el complemento intenta
|
|
encontrar un nodo que tenga un historial de transacción de GITD=1...3. Aunque el esclavo haya replicado
|
|
la escritura del cliente y la consistencia de sesión podría alcanzarse al leer desde el esclavo, el
|
|
complemento no lo considerará. Esto no es un fallo de la implementación del complemento, sino
|
|
una laguna de la característica del lado del servidor. Observe que es este es un caso trivial para ilustrar el
|
|
problema; existen otros más. En resumen, se sugiere no intentar usar los GTID internos de MySQL 5.6
|
|
para forzar la consistencia de sesión. Tarde o temprano, el equilibrado de carga dejará de funcionar de manera apropiada
|
|
y el complemento dirigirá todas las peticiones de consistencia de sesión al maestro.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Las estadísticas de los IDs de transacciones globales están disponibles únicamente con la emulación
|
|
en el lado del cliente debido a que monitorizan dicha emulación.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<note>
|
|
<title>Identificadores de transacciones globales en sistemas distribuidos</title>
|
|
<para>
|
|
Los identificadores de transacciones globales pueden servir para múltiples propósitos en el contexto de
|
|
sistemas distribuidos, como un clúster de bases de datos. Los identificadores de transacciones globales se
|
|
pueden usar para, por ejemplo, la identificación de transacciones en todo el sistema,
|
|
el ordenameiento global de transacciones, el mecanismo de pulso, y
|
|
la comprobación del estado de replicación de las réplicas. <literal>PECL/mysqlnd_ms</literal>, un software basado
|
|
en un controlador del lado del cliente, enfoca el uso de GTIDs para tareas que puedan ser
|
|
tratadas en el cliente, como la comprobación del estado de replicación de las réplicas
|
|
para configuraciones de replicación asíncronas.
|
|
</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.concept_cache">
|
|
<title>Integración de una caché</title>
|
|
<note>
|
|
<title>Requisitos de versión</title>
|
|
<para>
|
|
Esta característica requiere el uso de <literal>PECL/mysqlnd_ms</literal> 1.3.0-beta o posterior
|
|
y de <literal>PECL/mysqlnd_qc</literal> 1.1.0-alpha o posterior.
|
|
<literal>PECL/mysqlnd_ms</literal> debe ser
|
|
compilado para soportar esta característica. Se requiere PHP 5.4.0 o posterior.
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<title>Configuración: orden de caraga de las extensiones</title>
|
|
<para>
|
|
<literal>PECL/mysqlnd_ms</literal> debe cargarse antes que
|
|
<literal>PECL/mysqlnd_qc</literal>, cuando se utilizan
|
|
extensiones compartidas.
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<title>Estabilidad de la característica</title>
|
|
<para>
|
|
La integración de la caché es de calidad beta.
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<title>Clústeres de MySQL adecuados</title>
|
|
<para>
|
|
Esta característica está pensada para usarla con la Repliación MySQL (copia primaria).
|
|
Actualmente, no se admiten otros tipos de clústeres MySQL. Los usuarios
|
|
de tales clústeres deben controlar PECL/mysqlnd_qc manualmente si están
|
|
insteresados en el almacenamiento en caché de consultas en el lado del servidor.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
El enfoque principal de <literal>PECL/mysqlnd_ms</literal> es el soporte para los clústeres de replicación
|
|
MySQL (copia primaria asíncrona). Los esclavos de un clúster de replicación MySQL
|
|
pueden o no reflejar las últimas actualizaciones del servidor.
|
|
Los esclavos son asíncronos y pueden demorarse con respecto al maestro. Una lectura desde un esclavo
|
|
se considera que es consistencia final desde una perspectiva total del clúster.
|
|
</para>
|
|
<para>
|
|
El mismo nivel de consistencia es ofrecido por el almacenamiento en caché, usando la estrategia
|
|
de invalidación de tiempo de vida (TTL). Se pueden servir datos actuales o antiguos. Finalmente,
|
|
los datos buscados en la caché no estarán disponibles, por lo que es necesario acceder al origen
|
|
de la caché.
|
|
</para>
|
|
<para>
|
|
Dado que un esclavo de Replicación MySQL (secundario asíncrono) y una caché local que maneja
|
|
TTL proporcionan el mismo nivel de servicio, es posible reemplzar de forma transparente
|
|
un acceso a una base de datos remota con un acceso a la caché local para obtener mejores posibilidades.
|
|
</para>
|
|
<para>
|
|
A partir de <literal>PECL/mysqlnd_ms</literal> 1.3.0-beta, el complemento
|
|
puede controlar de forma transparente <literal>PECL/mysqlnd_ms</literal> 1.1.0-alpha
|
|
o posterior para almacenar en caché una consulta de solo lectura, si está
|
|
explícitamente permitido, configurando una calidad de servicio apropiada a través de
|
|
<function>mysqlnd_ms_set_qos</function>.
|
|
Por favor, lea la
|
|
<link linkend="mysqlnd-ms.quickstart.cache">guía rápida</link> para ver un ejemplo de código.
|
|
Ambos complementos deben estar instalados, <literal>PECL/mysqlnd_ms</literal>
|
|
debe ser compilado para soportar la característica de la caché y se ha de utilizar PHP 5.4.0 o posterior.
|
|
</para>
|
|
<para>
|
|
Las aplicaciones tienen control total sobre el uso de la caché y pueden solicitar datos actuales
|
|
en cualquier momento si fuera necesario. Se puede habilitar y deshabilitar el uso de la caché
|
|
durante la ejecución de un script. La caché se usará si
|
|
<function>mysqlnd_ms_set_qos</function> establece la calidad del servicio
|
|
a consistencia final y habilita el uso de la caché. El uso de la caché se deshabilita
|
|
solicitando niveles de consistencia superiores, por ejemplo,
|
|
consistencia de sesión (lectura de sus datos). Una vez que la calidad del servicio ha
|
|
vuelto a consistencia final, la caché se podrá usar de nuevo.
|
|
</para>
|
|
<para>
|
|
Si la caché está habilitada para sentencias de solo lectura, <literal>PECL/mysqlnd_ms</literal> podrá inyectar
|
|
<link linkend="mysqlnd-qc.quickstart.caching">sugerencias SQL para controlar la caché</link>
|
|
mediante PECL/mysqlnd_qc. Se puede modificar la sentencia SQL si se obtiene de la aplicación.
|
|
Se supone que los tratamientos de SQL subsiguientes ignorarán las sugerencias SQL. Una sugerencia
|
|
SQL es un comentario SQL. Los comentarios no deben ser ignorados, por ejemplo, por el servidor de la base de datos.
|
|
</para>
|
|
<para>
|
|
El TTL de una entrada de la caché se calcula en función de cada sentencia. Las aplicaciones
|
|
pueden establecer un máximo de edad para los datos que quieren recuperar mediante
|
|
<function>mysqlnd_ms_set_qos</function>. La edad establece un límite superior aproximado
|
|
de los segundos que los datos devueltos pueden demorarse con respecto al maestro.
|
|
</para>
|
|
<para>
|
|
La siguietne lógica se utiliza para calcular el TTL real si la caché está habilitada.
|
|
La lógica toma en cuenta la demora del esclavo aproximada para elegit un TTL. Si,
|
|
por ejemplo, hay dos esclavos que se retrasan 5 y 10 y la edad máxima
|
|
permitida es 60 segundos, el TTL se establece a 50 segundos. Observe que
|
|
el establecimiento de la edad no es más que una aproximación.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Se comprueba si la sentencia es de solo lectura. Si no lo es, no se almacenará en caché.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Si la caché está habilitada, se comprueba la demora de todos los esclavos configurados.
|
|
Se establecen las conexiones esclavas si no existieran hasta ahora y se utilizan conexiones
|
|
retardadas.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Se envía la sentencia <literal>SHOW SLAVE STATUS</literal> a todos los esclavos. No se espera
|
|
a que el primer esclavo replique antes de enviarla al segundo esclavo. Los clientes
|
|
a menudo esperan bastante tiempo para las réplicas, por lo que se envían todas las peticiones de una vez antes
|
|
obtenerlas en un segudo escenario.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Se itera sobre todos los esclavos. Para cada esclavo se espera su réplica. No se inicia
|
|
la comprobación de otro esclavo antes de que el esclavo actual haya replicado.
|
|
Se comprueba si <literal>Slave_IO_Running=Yes</literal> y <literal>Slave_SQL_Running=Yes</literal>.
|
|
Si ambas condiciones son verdaderas, se obtiene el valor de <literal>Seconds_Behind_Master</literal>.
|
|
En caso de cualquier error o de que las condiciones fallen, se establece un error en la conexión esclava.
|
|
Se salta cualquier conexión esclava para el resto del filtrado de conexiones.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Se busca el valor máximo de <literal>Seconds_Behind_Master</literal> de
|
|
todos los esclavos que hayan pasado las condiciones previas. Se resta el valor de
|
|
la edad máxima de <function>mysqlnd_ms_set_qos</function> proporcionada por el usuario.
|
|
Se utiliza el resultado como un TTL.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
El filtrado puede ordenar todos los esclavos. Si se hace, se usa la edad máxima como
|
|
TTL, ya que la demora máxima encontrada es igual a cero. Es perfectamente válido
|
|
ordenar todos los esclavos. En adelante, depende de filtro subsiguiente
|
|
decidir qué hacer. El filtro de equilibrado de carga interno eligirá al
|
|
maestro.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Se inyectan las sugerencias SQL apropiadas para habilitar el almacenamiento en caché por patre de <literal>PECL/mysqlnd_qc</literal>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Se procede con el filtrado de conexiones, p.ej., se aplican las reglas del equilibrado de carga para
|
|
escoger un esclavo.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
PHP carga <literal>PECL/mysqlnd_qc</literal> después de
|
|
<literal>PECL/mysqlnd_ms</literal>. Así, verá
|
|
todas las modificaciones de consultas hechas por <literal>PECL/mysqlnd_ms</literal> y
|
|
almacenará en caché la consulta, si se le ordenó hacerlo.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
El algoritmo puede parecer costoso. <literal>SHOW SLAVE STATUS</literal> es una
|
|
operación muy rápida. Dado un número suficiente de peticiones y de usos de la caché por segundo, el coste de
|
|
comprobar la demora de los esclavos puede superar fácilmente el coste de la decisiones de la caché.
|
|
</para>
|
|
<para>
|
|
Las sugerencias sobre un algoritmo mejor siempre son bienvenidas.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.supportedclusters">
|
|
<title>Clústeres admitidos</title>
|
|
<para>
|
|
Cualquier aplicación que utilice cualquier tipo de clúster MySQL tiene que enfrentarse con las mismas tareas:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Identificar los nodos que pueden ejecutar una sentencia dada con
|
|
el nivel de servicio requerido
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Equilibrar la carga de peticiones dentro de la lista de candidatos
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Usar la tolerancia a fallos automática dentro de los candidatos, si fuera necesario
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
El complemento está optimizado para realizar estas tareas en el contexto de un clúster de
|
|
replicación MySQL asíncrono clásico que consiste en un único maestro y
|
|
varios esclavos (copiar primaria). Al utilizar la replicación MySQL asíncrona clásica
|
|
todas las tareas enumeradas arriba deben ser controladas en el lado del cliente.
|
|
</para>
|
|
<para>
|
|
Otros tipos de clústeres MySQL pueden cluster may have lower requirements on the application side.
|
|
Po ejemplo, si todos los nodos del clúster pueden responder a peticiones de lectura y de escritura, no
|
|
es necesario que se realice la división de lectura-escritura (multi-maestro, todos actualizan).
|
|
Si todos los nodos del clúster son sincrónicos, proporcionarán autmáticamente la mayor
|
|
calidad de servcio posible, lo que se hace eligiendo un nodo más sencillo.
|
|
En este caso, el complemento puede servir a la aplicación después de una reconfiguración
|
|
para deshabilitar ciertas características, como la división de lectura-escritura interna.
|
|
</para>
|
|
<note>
|
|
<title>El enfoque de la documentación</title>
|
|
<para>
|
|
El enfoque de la documentación describe el uso del complemento con los clústeres
|
|
de replicación MySQL asíncronos clásicos (copia primaria). El soporte para este
|
|
tipo de clústeres ha sido el objetivo de desarrollo original. El uso de otros
|
|
clústeres se describe brevemente abajo. Por favor, observe que
|
|
aún está en desarrollo.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
<emphasis role="bold">Copia primaria (Replicación de MySQL)</emphasis>
|
|
</para>
|
|
<para>
|
|
Este es el caso de uso primario del complemento. Siga las indicaciones dadas en la descripción de cada característica.
|
|
</para>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Configurar un maestro y uno o más esclavos.
|
|
Los <link linkend="mysqlnd-ms.plugin-ini-json.server-list-syntax">detalles de configuraicón del servidor</link>
|
|
se dan en la sección de configuración.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Utilizar la política de equilibrado de carga aleatorio junto con la bandera
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.filter-random">sticky</link>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Si no se planea usar las llamadas a la API de
|
|
<link linkend="mysqlnd-ms.quickstart.qos-consistency">niveles de servicio</link>,
|
|
añada la bandera
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.master-on-write">master on write</link>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Por favor, concienciese de las propiedades de la tolerancia de fallos automática antes de
|
|
añadir una directiva <link linkend="ini.mysqlnd-ms-plugin-config-v2.failover">failover</link>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Considere el uso de <link linkend="ini.mysqlnd-ms-plugin-config-v2.trx-stickiness">trx_stickiness</link>
|
|
para ejecutar transacciones solamente en el primario. Lea cuidadosamente cómo funciona
|
|
antes de confiar en él.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Habilitar el complemento (php.ini)</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
mysqlnd_ms.enable=1
|
|
mysqlnd_ms.config_file=/path/to/mysqlnd_ms_plugin.ini
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Configuración básica del complemento (mysqlnd_ms_plugin.ini) para la Replicación de MySQL</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
{
|
|
"myapp": {
|
|
"master": {
|
|
"master_1": {
|
|
"host": "localhost",
|
|
"socket": "\/tmp\/mysql57.sock"
|
|
}
|
|
},
|
|
"slave": {
|
|
"slave_0": {
|
|
"host": "127.0.0.1",
|
|
"port": 3308
|
|
},
|
|
"slave_1": {
|
|
"host": "192.168.2.28",
|
|
"port": 3306
|
|
}
|
|
},
|
|
"filters": {
|
|
"random": {
|
|
"sticky": "1"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Copia primaria con multiprimarios (MMM - MySQL Multi Master)</emphasis>
|
|
</para>
|
|
<para>
|
|
La Replicación de MySQL permite crear topologías de clústeres con múltiples maestros (primarios).
|
|
Los conflictos escritura-escritura no los maneja el sistema de replicación. No es una configuración de actualización en cualquier sitio.
|
|
Por tanto, los datos deben ser particionados manualmente y los clientes deben ser redirigidos de acuerdo
|
|
a las reglas de particionamiento. La configuración recomendada es igual a la de fragmentación de más abajo.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Fragmentación manual, posiblemente combinada con copia primaria y múltiples primarios</emphasis>
|
|
</para>
|
|
<para>
|
|
Use sugerencias SQL y el filtro de grupos de nodos para clústeres que usen particionamiento de datos
|
|
pero que dejen la redirección de consultas al cliente. La configuración del ejemplo muestra una configuración
|
|
multimaestro con dos fragmentos.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Múltiples primarios - multimaestro (php.ini)</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
mysqlnd_ms.enable=1
|
|
mysqlnd_ms.config_file=/path/to/mysqlnd_ms_plugin.ini
|
|
mysqlnd_ms.multi_master=1
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Copia primaria con múltiples primarios y particionamiento</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
{
|
|
"myapp": {
|
|
"master": {
|
|
"master_1": {
|
|
"host": "localhost",
|
|
"socket": "\/tmp\/mysql57.sock"
|
|
}
|
|
"master_2": {
|
|
"host": "192.168.2.27",
|
|
"socket": "3306"
|
|
}
|
|
},
|
|
"slave": {
|
|
"slave_1": {
|
|
"host": "127.0.0.1",
|
|
"port": 3308
|
|
},
|
|
"slave_2": {
|
|
"host": "192.168.2.28",
|
|
"port": 3306
|
|
}
|
|
},
|
|
"filters": {
|
|
"node_groups": {
|
|
"Partition_A" : {
|
|
"master": ["master_1"],
|
|
"slave": ["slave_1"]
|
|
},
|
|
"Partition_B" : {
|
|
"master": ["master_2"],
|
|
"slave": ["slave_2"]
|
|
}
|
|
},
|
|
"roundrobin": []
|
|
}
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
El complemento también se puede usar con una colección suelta de fragmentos no relacionados. Para
|
|
tal clúster, configure solamente maestros y deshabilite la división de lectura-escritura. Los nodos de
|
|
tal clúster son llamados maestros en la configuración del complemento ya que aceptan
|
|
tanto lecturas como escrituras para su particionamiento.
|
|
</para>
|
|
<para>
|
|
<emphasis role="bold">Usar clústeres de actualización en cualquier sitio sincrónicos tales como el Clúster de MySQL</emphasis>
|
|
</para>
|
|
<para>
|
|
El Clúster de MySQL es una solución de clúster sincrónico. Todos los nodos del clúster aceptan
|
|
peticiones de lectura y escritura. En el contexto del complemento, todos los nodos
|
|
son considerados maestros.
|
|
</para>
|
|
<para>
|
|
Use únicamente el equilibrado de carga y la tolerancia a fallos.
|
|
</para>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Deshabilite la <link linkend="mysqlnd-ms.rwsplit">división interna de lectura-esritura</link>.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Configure maestros solamente.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Considere la estrategia del equilibrado de carga aleatorio una vez, que es la predeterminada del complemento.
|
|
Si se usa esta estrategia, solamente se configuran los maestros y no se usan sugerencias SQL
|
|
para forzar el uso de un nodo en particular, no ocurriran intercambios de conexiones durante
|
|
una petición web. Por tanto, no se requiere un trato especial
|
|
para las transacciones. El complemento eligirá un maestro al inicio del
|
|
script de PHP y lo usará hasta que finalice dicho script.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
No establezca la calidad de servicio. Todos los nodos tienen todos los datos. Esto
|
|
proporciona automáticamente la mejor calidad de servicio posible (consistencia fuerte).
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
No habilite la inyección de transacciones global en el lado del cliente. Ni es necesario
|
|
para ayudar con la tolerancia a fallos del lado del servidor ni para asistir al
|
|
filtro de calidad de servicio a elegir un nodo apropiado.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
Deshabilitar la división interna de lectura-escritura
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Establezca
|
|
<link linkend="mysqlnd-ms.configuration"><literal>mysqlnd_ms.disable_rw_split=1</literal></link>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
No use <link linkend="mysqlnd-ms.rwsplit">sugerencias SQL</link>
|
|
para forzar el uso de esclavos
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
Configure únicamente maestros.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Establezca
|
|
<link linkend="mysqlnd-ms.configuration"><literal>mysqlnd_ms.multi_master=1</literal>.</link>
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>No configure ningún esclavo.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Establezca
|
|
<literal><link linkend="mysqlnd-ms.plugin-ini-json">failover=loop_before_master</link></literal>
|
|
en el fichero de configuración del complemento para evitar advertencias sobre listas de esclavos vacías
|
|
y para hacer que la lógica de tolerancia a fallos itere sobre todos los maestros configurados antes de emitir un error.
|
|
</simpara>
|
|
<simpara>
|
|
Observe las advetencias sobre la tolerancia a fallos automática dadas en las secciones anteriores.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Múltiples primarios - multimaestro (php.ini)</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
mysqlnd_ms.enable=1
|
|
mysqlnd_ms.config_file=/path/to/mysqlnd_ms_plugin.ini
|
|
mysqlnd_ms.multi_master=1
|
|
mysqlnd_ms.disable_rw_split=1
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Clúster de actualización en cualquier sitio sincrónico</title>
|
|
<programlisting role="ini">
|
|
<![CDATA[
|
|
|
|
"myapp": {
|
|
"master": {
|
|
"master_1": {
|
|
"host": "localhost",
|
|
"socket": "\/tmp\/mysql57.sock"
|
|
},
|
|
"master_2": {
|
|
"host": "192.168.2.28",
|
|
"port": 3306
|
|
}
|
|
},
|
|
"slave": {
|
|
},
|
|
"filters": {
|
|
"roundrobin": {
|
|
}
|
|
},
|
|
"failover": {
|
|
"strategy": "loop_before_master",
|
|
"remember_failed": true
|
|
}
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
Si se ejecuta un clúster de actualización en cualquier sitio que no tenga particionamiento interno para
|
|
evitar hot spots y altas tasas de colisiones, considere usar el filtro de grupos de nodos
|
|
para seguir las actualizaciones en una tabla de acceso frecuente en uno de los nodos. Esto podría
|
|
ayudar a reducir las tasas de colisión y proporcionar así una mejora del rendimiento.
|
|
</para>
|
|
</section>
|
|
|
|
<section xml:id="mysqlnd-ms.concept_xa_trx">
|
|
<title>Transacciones XA/distribuidas</title>
|
|
<note>
|
|
<title>Requerimientos de versión</title>
|
|
<para>
|
|
Las funciones relacionadas con XA han sido introducidas en la versión 1.6.0-alpha de <literal>PECL/mysqlnd_ms</literal>.
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<title>Se buscan los primeros adaptadores</title>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
Abajo se muestra una lista de algunas restricciones de la característica.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>La característica aún no es compatible con el soporte para MySQL Fabric. Aún es pronto para resolver esta limitación.</para>
|
|
<para>El identificador de transacción de XA actualmente está restrigido a números. Esta limitación se resolverá a petición, es una simplificación empleada durante la implementación inicial.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<title>Restricciones del servidor de MySQL</title>
|
|
<para>
|
|
El soporte para XA mediante el servidor de MySQL tiene algunas restricciones. Aún más,
|
|
el registro binario de servidores podría podría carecer de los cambios realizados por transacciones XS en caso
|
|
de ciertos errores. Véase el manual de MySQL para más detalles.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
Las transacciones XA/distribuidas pueden generar múltiples servidores de MySQL.
|
|
Por lo tanto, podrían parecer la herramienta
|
|
perfecta para clústeres de MySQL framentados, por ejemplo, los clústeres administrados por MySQL Fabric.
|
|
<literal>PECL/mysqlnd_ms</literal> oculta la mayoría de los comandos de SQL
|
|
para controlar las transacciones XA y realiza tareas administrativas automáticas en caso
|
|
de error, proveyendo así al usuario de una API completa. Los usuarios deberían
|
|
configurar el complemento cuidadosamente y ser conscientes de las restricciones del servidor antes
|
|
de utilizar la característica.
|
|
</para>
|
|
<para>
|
|
<example>
|
|
<title>Patrón general para transacciones XA</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$mysqli = new mysqli("myapp", "username", "password", "database");
|
|
|
|
/* BEGIN */
|
|
mysqlnd_ms_xa_begin($mysqli, 1 /* xa id */);
|
|
|
|
/* ejecutar consultas en varios servidores */
|
|
$mysqli->query("UPDATE some_table SET col_a = 1");
|
|
...
|
|
|
|
/* COMMIT */
|
|
mysqlnd_ms_xa_commit($link, 1);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
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. Durante la primera fase, los servidores participantes
|
|
comienzan una transacción y el cliente lleva a cabo su trabajo. A esta fase le sigue
|
|
una segunda fase de votación. Durante la votación, los servidores hacen primero una promesa firme
|
|
de que están listos para consignar el trabajo incluso en caso de fallos inesperados
|
|
propios. Si un servidor falla en esta fase, aún recordará la transacción
|
|
abortada después de recobrarse y esperar a que el cliente decida si
|
|
será consignada o revertida.
|
|
</para>
|
|
<para>
|
|
Si un cliente que ha iniciado una transacción global falla después de que todos los
|
|
servidores participantes dieron su promesa de estar listos para consignar, dichos servidores
|
|
deben esperar a una decisión. A los servidores no se les permite decidir unilateralmente sobre
|
|
la transacción.
|
|
</para>
|
|
<para>
|
|
Si un cliente que falla o se desconecta de un participante, un servidor que falla u ocurre un error
|
|
de servidor durante la primera fase del protocolo no es crítico. En la mayoría de los casos, el servidor
|
|
se olvidará de la transacción XA y su trabajo será revertirla. Además,
|
|
el complemento intenta alcanzar tantos participantes como pueda para ordenar
|
|
al servidor a revertir el trabajo inmediatamente. No es posible deshabilitar esta reversión
|
|
implícita llevada a cabo por <literal>PECL/mysqlnd_ms</literal> en caso de error
|
|
durante la primera fase del protocolo. Esta decisión del diseño se ha hecho para
|
|
mantener la implementación simple.
|
|
</para>
|
|
<para>
|
|
Un error durante la segunda fase del protocolo de consignación puede desembocar en una
|
|
situación más grave. Los servidores no olvidarán en ningún caso
|
|
las transacciones preparadas no finalizadas. El complemento no
|
|
intentará resolver estos casos inmediatamente, sino que esperará a la recolección de basura
|
|
opcional en sengundo plano para asegurar el progreso del protocolo de consignación. Se asume
|
|
que una solución tomará bastante tiempo ya que podría incluir la espera
|
|
de la recuperación de un fallo de un servidor participante. Este intervalo de tiempo podría
|
|
ser mayor que el esperado por un desarrollador y un usuario final cuando se intenta consignar una
|
|
transacción global con <function>mysqlnd_ms_xa_commit</function>. Por tanto,
|
|
que la función devuelva con la transacción global sin finalizar aún
|
|
requiere atención. Se ha de advertir en este punto que aún no está
|
|
claro si la transacción global será consignada o revertida más adelante.
|
|
</para>
|
|
<para>
|
|
Los errores durante la segunda fase se pueden ignorar, manejándolos por uno mismo o resolviéndolos
|
|
la lógica de la recolección de basura interna. No se recomineda ignorarlos
|
|
debido a que se podrían experimentar transacciones globales no finalizadas en los servidores que bloqueen
|
|
recursos virtualmente de forma indefinida. El manejo de los errores requiere conocer los
|
|
participantes, comprobar su estado y enviarles los conmandos SQL apropiados.
|
|
No existe una llamada a la API de usuario que exponga esta información. Se tendrá que
|
|
configurar un almacén de estado y hacer que el complemento registre sus acciones en él para recibir
|
|
la información deseada.
|
|
</para>
|
|
<para>
|
|
Véase la
|
|
<link linkend="mysqlnd-ms.quickstart.xa_transactions">guía rápida</link> y
|
|
los <link linkend="ini.mysqlnd-ms-plugin-config-v2.xa.state-store">ajustes del
|
|
fichero de configuración del complemento</link> relacionados para un ejemplo de cómo configurar un estado.
|
|
Además de configurar un almacén de estados, se han de crear algunas tablas SQL.
|
|
Las definiciones de las tablas se dan en la descripción de los ajustes de configuración
|
|
del complemento.
|
|
</para>
|
|
<para>
|
|
Establecer y configurar un almacén de estados también es una precondición para utilizar la
|
|
recolección de basura interna para transacciones XA que fallen durante la
|
|
segunda fase de consignación. Registrar información sobre transacciones XA en curso es
|
|
una tarea extra inevitable. La tarea extra consiste en actualizar el almacén
|
|
de estados después de cada operación que cambie el estado de una transacción global
|
|
en sí (iniciada, consignada, revertida, errores y abortos), la adición de
|
|
participantes (host, opcionalmente el usuario y contraseña requeridos para conectarse) y cualquier
|
|
cambio del estado de un participante. Observar que, dependiendo de la configuración y las
|
|
politicas de seguridad, estos registros podrían considerarse delicados.
|
|
Por tanto, se recomienda restringir el acceso al almacén de estados. A menos que el
|
|
almancén de estados mismo esté sobrecargado, la escritura de la información de estado podría contribuir
|
|
notablemente al tiempo de ejecución, pero debería ser por lo demás solamente un factor menor.
|
|
</para>
|
|
<para>
|
|
Es posible que el esfuerzo que toma la implementación de rutinas propias para manejar
|
|
transacciones XA que fallen durante la segunda fase de consignación exceda los beneficios
|
|
del uso de la característica de XA de <literal>PECL/mysqlnd_ms</literal> desde un comienzo.
|
|
Por tanto, el manual se centra solamente en el empleo de la recolección de basura interna.
|
|
</para>
|
|
<para>
|
|
Se puede provocar la recolección de basura manual o automáticamente en sengundo plano.
|
|
Se puede llamar a <function>mysqlnd_ms_xa_gc</function> inmediatamente después de que
|
|
una consignación falle un intento de resolver cualquier transacción global fallida aunque aún abierta
|
|
tan pronto como sea posible. También se podría decidir deshabilitar la recolección de basura
|
|
automática en segundo plano, implementar un conjunto de reglas propio para invocar a la recolección
|
|
de basura interna y ejecutarla cuando se desee.
|
|
</para>
|
|
<para>
|
|
Por omisión, el complemento iniciará la recolección de basura con una cierta probabilidad
|
|
en las extensiones del método interno <literal>RSHUTDOWN</literal>. La petición
|
|
de apagado es invocada después de finalizar el script. La realización de la
|
|
recolección de basura está determinara por el cálculo de un valor aleatorio en el rango
|
|
<literal>1...1000</literal> y comparándolo con el ajuste de configuracion
|
|
<link linkend="ini.mysqlnd-ms-plugin-config-v2.xa.gc"><literal>probability</literal></link>
|
|
(predeterminado: 5). Si el ajuste es
|
|
mayor o igual que el valor aleatorio, se llevará a cabo la recolección de basura.
|
|
</para>
|
|
<para>
|
|
Una vez iniciada, la recolección de basura actúa sobre hasta
|
|
<literal>max_transactions_per_run</literal> (predeterminado: 100) transacciones globales
|
|
registradas. Los registros incluyen transaccines XA finalizadas con éxito y también no
|
|
finalizadas. Los registros para transacciones exitosas se eliminan y las no
|
|
finalizadas se intentan resolver. No hay estadísticas que ayuden
|
|
a encontrar el equilibrio exacto entre mantener una ejecución corta de la recolección de basura
|
|
limitando el número de transacciones consideradas por ejecución y prevenir que la recolección de
|
|
basura se quede retrasada, resultando en muchos registros.
|
|
</para>
|
|
<para>
|
|
Por cada transacción XA fallida, la recolección de basura realiza
|
|
<literal>max_retries</literal> (predeterminado: 5) intentos para finalizarla. Después de esto,
|
|
<literal>PECL/mysqlnd_ms</literal> se rinde. Hay dos posibles razones para esto. O
|
|
un servidor participante falla y ya no está accesible dentro de
|
|
<literal>max_retries</literal> invocaciones de la recolección de basura, o hay
|
|
una situación que la recolección de basura interna no puede manejar. Problemente, lo
|
|
último sería considerado como un error. Sin embargo, se puede forzar manualmente más
|
|
ejecuciones de recolección de basura llamando a <function>mysqlnd_ms_xa_gc</function> con el
|
|
parámetro apropiado establecido. Si incluso la ejecución de esta función no resuelve
|
|
la situación, el problema debe resolverse mediante un operador.
|
|
</para>
|
|
<para>
|
|
La función <function>mysqlnd_ms_get_stats</function>
|
|
proporciona algunas estadísticas sobre cuántas transacciones XA han sido iniciadas,
|
|
consignadas, han fallado o han sido revertidas.
|
|
</para>
|
|
|
|
</section>
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|