Initial set of documentation for PECL/mysqlnd_ms. PECL/mysqlnd_ms is a replication and load balancing plugin for the mysqlnd library. It has been added to PECL some eight months ago as an example of a mysqlnd plugin. Meanwhile it is more than example code but almost really useful. The PECL extension is in alpha state. An alpha release should follow soon.

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@310315 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Ulf Wendel 2011-04-18 15:04:51 +00:00
parent cc40e134d2
commit 1d9e890195
12 changed files with 2651 additions and 0 deletions

211
reference/mysqlnd_ms/book.xml Executable file
View file

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<book xml:id="book.mysqlnd-ms" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Mysqlnd replication and load balancing plugin</title>
<titleabbrev>mysqlnd_ms</titleabbrev>
<preface xml:id="intro.mysqlnd-ms">
&reftitle.intro;
<para>
The mysqlnd replication and load balancing plugin (<literal>mysqlnd_ms</literal>)
adds easy to use MySQL replication support to all PHP MySQL extensions that use
<link linkend="book.mysqlnd">mysqlnd</link>.
</para>
<para>
As of version PHP 5.3.3 the MySQL native driver for PHP
(<literal>mysqlnd</literal>)
features an internal plugin C API. C plugins, such as the replication and
load balancing plugin, can extend the functionality of
<link linkend="book.mysqlnd">mysqlnd</link>.
</para>
<para>
The MySQL native driver for PHP is a C library which ships together with
PHP as of PHP 5.3.0. It serves as a drop-in replacement for the
MySQL Client Library (AKA libmysql/libmysqlclient). Using mysqlnd has
several advantages: no extra downloads because it comes with PHP,
PHP license, lower memory consumption in certain cases,
new functionality such as asynchronous queries.
</para>
<para>
Mysqlnd plugins such as the replication and load balancing plugin operate
mostly transparent from an user perspective. The replication and load balancing
plugin supports all PHP applications
and all PHP MySQL extensions (
<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>).
It does not change existing APIs. Therefore, it can easily be used with
existing PHP applications.
</para>
<note>
<para>
The mysqlnd replication and load balancing plugin is in alpha status.
It is not feature complete.
</para>
</note>
<section xml:id="mysqlnd-ms.key_features">
<title>Key Features</title>
<para>
<itemizedlist>
<listitem>
<para>
Transparent and therefore easy to use
</para>
<para>
<itemizedlist>
<listitem>
<para>
supports all PHP MySQL extensions
</para>
</listitem>
<listitem>
<para>
no API changes
</para>
</listitem>
<listitem>
<para>
very little, if any, application changes required, dependent on the usage scenario required
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
<listitem>
<para>
Featured read-write split strategies
</para>
<para>
<itemizedlist>
<listitem>
<para>
Automatic detection of SELECT, supports SQL hints to overrule automatism
</para>
</listitem>
<listitem>
<para>
user-defined
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
<listitem>
<para>
Featured load balancing strategies
</para>
<para>
<itemizedlist>
<listitem>
<para>
Round Robin: choose different slave in round robin fashion for every slave request.
</para>
</listitem>
<listitem>
<para>
Random: choose random slave for every slave request.
</para>
</listitem>
<listitem>
<para>
Random once (sticky): choose random slave once to run all slave requests for the duration of a web request.
</para>
</listitem>
<listitem>
<para>
User-defined. The application can register callbacks with mysqlnd_ms.
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section xml:id="mysqlnd-ms.limitations">
<title>Limitations</title>
<para>
The plugin does not support MySQL replication setups with more
than one master server.
</para>
<para>
The built-in read/write-split mechanism is very basic. Every
query which starts with <literal>SELECT</literal>
is considered a read request to be sent to a MySQL slave server.
All other queries, including, for example, <literal>SHOW</literal>
statements, are considered as write requests to be sent to the MySQL master server.
The build-in behaviour can be overruled using
<link linkend="mysqlnd-ms.constants">SQL hints</link> or an user-defined
<link linkend="function.mysqlnd-ms-set-user-pick-server">callback function</link>.
</para>
<para>
The read/write splitter is not aware of multi-statements. Multi-statements
are considered as one statement. The decision of where to run the statement
will be based on the beginning of the statement string.
</para>
<para>
The plugin does not support native prepared statements. Please
note that <link linkend="ref.pdo-mysql">PDO_MySQL</link> is using a
client-side prepared statement emulation by default. Client-side emulated
prepared statements are fully supported by the replication and load balancing
plugin because the emulation is not using native prepared statements.
If you are using PHP based database abstraction, please consult the
vendor manual to learn if a client-side prepared statement emulation is
used.
</para>
<note>
<para>
Application must be aware of the consequences of connection switches performed
for load balancing purpose. Please check the documentation on
<link linkend="mysqlnd-ms.pooling">connection pooling and switching</link>,
<link linkend="mysqlnd-ms.transaction">transaction handling</link>,
<link linkend="mysqlnd-ms.failover">failover</link>
<link linkend="mysqlnd-ms.loadbalancing">load balancing</link> and
<link linkend="mysqlnd-ms.rwsplit">read-write splitting</link> carefully.
</para>
</note>
</section>
<section xml:id="mysqlnd-ms.name">
<title>On the name</title>
<para>
The shortcut <literal>mysqlnd_ms</literal>
stands for <literal>mysqlnd master slave plugin</literal>. The name
was choosen for a quick-and-dirty proof-of-concept. In the beginning
the developers did not expect to continue using the code base.
</para>
</section>
</preface>
&reference.mysqlnd-ms.quickstart;
&reference.mysqlnd-ms.concepts;
&reference.mysqlnd-ms.setup;
&reference.mysqlnd-ms.constants;
&reference.mysqlnd-ms.reference;
</book>
<!-- 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
-->

302
reference/mysqlnd_ms/concepts.xml Executable file
View file

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<chapter xml:id="mysqlnd-ms.concepts" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Concepts</title>
<section xml:id="mysqlnd-ms.architecture">
<title>Architecture</title>
<para>
The mysqlnd replication and load balancing plugin is
implemented as a PHP extension.
It is written in C and operates under the hood of PHP. During the
startup of the PHP interpreter, in the module init phase of the
PHP engine, it gets registered as a
<link linkend="book.mysqlnd">mysqlnd</link> plugin to replace selected
mysqlnd C methods.
</para>
<para>
At PHP run time it inspects queries send from
mysqlnd (PHP) to the MySQL server. If a query is recognized as read-only
it will be sent to one of the configured slave servers. Statements are
considered read-only if they either start with <literal>SELECT</literal>,
the SQL hint <literal>/*ms=slave*/</literal> or a slave had been choose for
running the previous query and the query starts with the SQL hint
<literal>/*ms=last_used*/</literal>. In all other cases the query will
be sent to the MySQL replication master server.
</para>
<para>
The plugin takes care internally of opening and closing the database connections
to the master server and the slave servers. From an application
point of view there continues to be only one connection handle. However,
internally, this one public connection handle represents a pool of
internal connections managed by the plugin. The plugin proxies queries
to the master server and the slave ones using multiple connections.
</para>
<para>
Database connections have a state consisting, for example, transaction
status, transaction settings, character set settings, temporary tables.
The plugin will try to maintain the same state among all internal
connections, whenever this can be done in an automatic and transparent way.
In cases where it is not easily possible to maintain state among all
connections, such as when using <literal>BEGIN TRANSACTION</literal>, the
plugin leaves it to the user to handle. Please, find further details below.
</para>
</section>
<section xml:id="mysqlnd-ms.pooling">
<title>Connection pooling and switching</title>
<para>
The replication and load balancing plugin changes the semantics of a PHP
MySQL connection handle. The existing API of the PHP MySQL extensions
(<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>) are not changed in
a way that functions are added or removed. But their behaviour
changes when using the plugin. Existing applications do not need to
be adapted to a new API. But they may need to be modified because of
the behaviour changes.
</para>
<para>
The plugin breaks the one-by-one relationship between a
<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link> connection
handle and a MySQL wire connection. If using the plugin a
<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link> connection
handle represents a local pool of connections to the configured
MySQL replication master and the MySQL replication slave servers.
The plugin redirects queries to the master and slave servers.
At some point in time one and the same PHP connection handle
may point to the MySQL master server. Later on, it may point
to one of the slave servers or still the master. Manipulating
and replacing the wire connection referenced by a PHP MySQL
connection handle is not a transparent operation.
</para>
<para>
Every MySQL connection has a state. The state of the connections in
the connection pool of the plugin can differ. Whenever the
plugin switches from one wire connection to another, the current state of
the user connection may change. The applications must be aware of this.
</para>
<para>
The following listshows what the connection state consists of.
</para>
<para>
<itemizedlist>
<listitem>
<simpara>
Transaction status
</simpara>
</listitem>
<listitem>
<simpara>
Temporary tables
</simpara>
</listitem>
<listitem>
<simpara>
Table locks
</simpara>
</listitem>
<listitem>
<simpara>
Session system variables and session user variables
</simpara>
</listitem>
<listitem>
<simpara>
Session system variables and session user variables
</simpara>
</listitem>
<listitem>
<simpara>
Prepared statements
</simpara>
</listitem>
<listitem>
<simpara>
<literal>HANDLER</literal> variables
</simpara>
</listitem>
<listitem>
<simpara>
Locks acquired with <literal>GET_LOCK()</literal>
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
The plugins philosophy is to align the state of connections in the
pool only if the state is under full control of the plugin, or if it is
necessary for security reasons. Just a few actions that change the
state of the connection fall into this category. The plugin does broadcast
the following state changing client calls to all currently open
connections in the connection pool: <literal>change_user</literal>,
<literal>select_db</literal>, <literal>set_charset</literal>,
<literal>set_server_option</literal>, <literal>set_client_option</literal>,
<literal>autocommit</literal>.
</para>
<para>
The plugin does not proxy or
<quote>remember</quote> settings to apply them on connections
opened in the future. This is important to remember, if
using
<link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link>.
Lazy connections are connections which are not
opened before the client sends the first connection.
Use of lazy connections is the default plugin action.
</para>
<para>
Connection switches happen right before queries are run. The plugin does
not switch the current connection until the moment in time when
the next statement is executed.
</para>
<para>
Please, do not miss the MySQL reference manual chapter on
replication features and issues. Some restrictions you hit may not be related
to the PHP plugin but are properties of the MySQL replication system.
</para>
</section>
<section xml:id="mysqlnd-ms.transaction">
<title>Transaction handling</title>
<para>
Transaction handling is fundamentally changed.
A SQL transaction is a unit of work run on one database server. The
unit of work consists of one or more SQL statements.
</para>
<para>
By default the plugin is not aware of SQL transactions. The plugin may
switch connections for load balancing at any point in time. Connection
switches may happen in the middle of a transaction. This is against the
nature of a SQL transaction. By default the plugin is not transaction safe.
</para>
<para>
At the time of writing, applications using SQL transactions together with
the plugin must use SQL hints to disable connection switches in the middle
of a SQL transaction. Please, find details in the examples section.
</para>
<para>
Future versions of the plugin might offer special settings to make
plugin transaction safe.
</para>
</section>
<section xml:id="mysqlnd-ms.failover">
<title>Failover</title>
<para>
Connection failover handling is left to the user. The application is responsible
for checking return values of the database functions it calls and reacting
to possible errors. If, for example, the plugin recognizes a query as a read-only
query to be sent to the slave servers and the slave server selected by the
plugin is not available, the plugin will raise an error after not executing
the statement.
</para>
<para>
It is up to
the application to handle the error and, if need be, re-issue the query to
trigger selection of another slave server for statement execution. The plugin
will make no attempts to failover automatically because the plugin
cannot ensure that an automatic failover will not change the state of
the connection. For example, the application may have issued a query
which depends on SQL user variables which are bound to a specific connection.
Such a query might return wrong results if the plugin would switch the
connection implicitly as part of automatic failover. To ensure correct
results the application must take care of the failover and rebuild
the required connection state. Therefore, by default, no automatic failover
is done by the plugin.
</para>
<para>
An user who does not change the connection state after opening a connection
may activate automatic master failover.
</para>
<para>
The failover policy is configured in the plugins configuration file by help
of the
<literal><link linkend="ini.mysqlnd-ms-plugin-config.failover">failover</link></literal>
configuration directive.
</para>
</section>
<section xml:id="mysqlnd-ms.loadbalancing">
<title>Load balancing</title>
<para>
Four load balancing strategies are supported to distribute read-only
statements over the configured MySQL slave servers:
<literal>random</literal>, <literal>random_once</literal> (default),
<literal>roundrobin</literal>, <literal>user</literal>.
</para>
<para>
The load balancing policy is configured in the plugins configuration
file using the
<link linkend="ini.mysqlnd-ms-plugin-config.pick">pick[]</link>
configuration directive.
</para>
</section>
<section xml:id="mysqlnd-ms.rwsplit">
<title>Read-write splitting</title>
<para>
The plugin runs read-only statements on the configured MySQL slaves and
all other queries on the MySQL master. Statements are
considered read-only if they either start with <literal>SELECT</literal>,
the SQL hint <literal>/*ms=slave*/</literal> or a slave had been chosen for
running the previous query and the query starts with the SQL hint
<literal>/*ms=last_used*/</literal>. In all other cases the query will
be send to the MySQL replication master server.
</para>
<para>
SQL hints are a special kind of standard compliant SQL comments. The plugin
does check every statement for certain SQL hints. The SQL hints are described
together with the <link linkend="mysqlnd-ms.constants">constants</link>
exported by the extension. Other systems
involved in the statement processing, such as the MySQL server, SQL firewalls
or SQL proxies are unaffected by the SQL hints because those systems are
supposed to ignore SQL comments.
</para>
<para>
The built-in read-write splitter can be replaced by a user-defined one, see also
<function>mysqlnd_ms_set_user_pick_server</function>.
</para>
<para>
A user-defined read-write splitter can ask the built-in logic to make
a proposal where to sent a statement by invoking
<function>mysqlnd_ms_is_select</function>.
</para>
<note>
<para>
The built-in read-write splitter is not aware of multi-statements.
Multi-statements are seen as one statement. The splitter will check the
beginning of the statement to decide where to run the statement. If, for example,
a multi-statement begins with
<literal>SELECT 1 FROM DUAL; INSERT INTO test(id) VALUES (1); ...</literal>
the plugin will run it on a slave although the statement is not read-only.
</para>
</note>
</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
-->

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<section xml:id="mysqlnd-ms.installation" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.install;
<note>
<para>
The mysqlnd replication and load balancing plugin is in alpha status.
It is not feature complete.
</para>
</note>
<para>
&pecl.moved;
</para>
<para>
&pecl.info;
<link xlink:href="&url.pecl.package;mysqlnd_ms">&url.pecl.package;mysqlnd_ms</link>
</para>
<para>
&pecl.windows.download;
</para>
</section>
<!-- 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
-->

View file

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<appendix xml:id="mysqlnd-ms.constants" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.constants;
&extension.constants;
<para>
<emphasis role="bold">SQL hint related</emphasis>
</para>
<para>
<example>
<para>
The mysqlnd replication and load balancing plugin (<literal>mysqlnd_ms</literal>)
does read/write splitting to direct write queries to a MySQL master server and
read-only queries to MySQL slave servers in a MySQL replication setup.
The plugin has a built-in read/write split logic. The logic is very basic.
All queries which start with <literal>SELECT</literal> are considered read-only
queries which shall be send to a MySQL slave server listed in
the plugin configuration file. All other queries shall be directed to the
MySQL master server specified in the plugin configuration file.
</para>
<para>
User supplied SQL hints can be used to overrule automatic read/write splitting
to gain full control on the process. SQL hints are standards compliant
SQL comments. The plugin will scan the beginning of a query string for
a SQL comment with certain contents which control query redirection.
Other systems involved in the query processing are unaffected by the
plugins SQL hints because other systems will ignore the SQL comments.
</para>
<para>
The plugin supports three SQL hints to direct queries to the MySQL slave
servers, the MySQL master server and the last used MySQL server. SQL hints
must be placed at the beginning to be recognized by the plugin.
</para>
<para>
It is recommended to use the string constants
<constant>MYSQLND_MS_MASTER_SWITCH</constant>,
<constant>MYSQLND_MS_SLAVE_SWITCH</constant> and
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant> instead of their literal
values for better portability.
</para>
<programlisting role="php">
<![CDATA[
<?php
/* Use constants for maximum portability */
$master_query = "/*" . MYSQLND_MS_MASTER_SWITCH . "*/SELECT id FROM test";
/* Valid but less portable: using literal instad of constant */
$slave_query = "/*ms=slave*/SHOW TABLES";
printf("master_query = '%s'\n", $master_query);
printf("slave_query = '%s'\n", $slave_query);
?>
]]>
</programlisting>
&examples.outputs;
<screen>
<![CDATA[
master_query = /*ms=master*/SELECT id FROM test
slave_query = /*ms=slave*/SHOW TABLES
]]>
</screen>
</example>
</para>
<para>
<variablelist>
<varlistentry>
<term>
<constant>MYSQLND_MS_MASTER_SWITCH</constant>
(<type>string</type>)
</term>
<listitem>
<simpara>
SQL hint used to send a query to the MySQL replication master server.
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term>
<constant>MYSQLND_MS_SLAVE_SWITCH</constant>
(<type>string</type>)
</term>
<listitem>
<simpara>
SQL hint used to send a query to one of the MySQL replication slave servers.
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term>
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant>
(<type>string</type>)
</term>
<listitem>
<simpara>
SQL hint used to send a query to the last used MySQL server. The last
used MySQL server can either be a master or a slave server in a
MySQL replication setup.
</simpara>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
<function>mysqlnd_ms_is_select</function> related
</para>
<para>
<variablelist>
<varlistentry>
<term>
<constant>MYSQLND_MS_QUERY_USE_MASTER</constant>
(<type>integer</type>)
</term>
<listitem>
<simpara>
If <function>mysqlnd_ms_is_select</function> returns
<constant>MYSQLND_MS_QUERY_USE_MASTER</constant> for a given query, the
built-in read/write split mechanism recommends sending the query to
a MySQL replication master server.
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term>
<constant>MYSQLND_MS_QUERY_USE_SLAVE</constant>
(<type>integer</type>)
</term>
<listitem>
<simpara>
If <function>mysqlnd_ms_is_select</function> returns
<constant>MYSQLND_MS_QUERY_USE_SLAVE</constant> for a given query, the
built-in read/write split mechanism recommends sending the query to
a MySQL replication slave server.
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term>
<constant>MYSQLND_MS_QUERY_USE_LAST_USED</constant>
(<type>integer</type>)
</term>
<listitem>
<simpara>
If <function>mysqlnd_ms_is_select</function> returns
<constant>MYSQLND_MS_QUERY_USE_LAST_USED</constant> for a given query, the
built-in read/write split mechanism recommends sending the query to
the last used server.
</simpara>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
<emphasis role="bold">Other</emphasis>
</para>
<para>
The plugins version number can be obtained using
<constant>MYSQLND_MS_VERSION</constant> or
<constant>MYSQLND_MS_VERSION_ID</constant>.
<constant>MYSQLND_MS_VERSION</constant>
is the string representation of the numerical version number
<constant>MYSQLND_MS_VERSION_ID</constant>, which is an integer such as 10000.
Developers can calculate the version number as follows.
</para>
<para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Version (part)</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry>Major*10000</entry>
<entry>1*10000 = 10000</entry>
</row>
<row>
<entry>Minor*100</entry>
<entry>0*100 = 0</entry>
</row>
<row>
<entry>Patch</entry>
<entry>0 = 0</entry>
</row>
<row>
<entry>MYSQLND_MS_VERSION_ID</entry>
<entry>10000</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
<para>
<variablelist>
<varlistentry>
<term>
<constant>MYSQLND_MS_VERSION</constant>
(<type>string</type>)
</term>
<listitem>
<simpara>
Plugin version string, for example, <quote>1.0.0-prototype</quote>.
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term>
<constant>MYSQLND_MS_VERSION_ID</constant>
(<type>integer</type>)
</term>
<listitem>
<simpara>
Plugin version number, for example, 10000.
</simpara>
</listitem>
</varlistentry>
</variablelist>
</para>
</appendix>
<!-- 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
-->

View file

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xml:id="function.mysqlnd-ms-get-stats" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>mysqlnd_ms_get_stats</refname>
<refpurpose>Returns query distribution and connection statistics</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<type>array</type>
<methodname>mysqlnd_ms_get-stats</methodname>
<void />
</methodsynopsis>
<para>
Returns an array of statistics collected by the replication
and load balancing plugin.
</para>
<para>
The PHP configuration setting
<literal><link linkend="ini.mysqlnd-ms.collect_statistics">mysqlnd_ms.collect_statistics</link></literal>
controls the collection of statistics. The collection of statistics
is disabled by default for performance reasons.
</para>
<para>
The scope of the statistics is the
<literal>PHP</literal> process.
Depending on your deployment model a
<literal>PHP</literal> process may handle one or multiple requests.
</para>
<para>
Statistics are aggregated for connections. It is not possible
to tell how much queries originating from
<literal>mysqli</literal>,
<literal>PDO_MySQL</literal> or
<literal>mysql</literal> API calls have
contributed to the aggregated data values.
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
&no.function.parameters;
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
Returns <literal>NULL</literal> if the
the PHP configuration directive
<link linkend="ini.mysqlnd-ms.enable"><literal>mysqlnd_ms.enable</literal></link>
has disabled the plugin. Otherwise, returns array of statistics.
</para>
<para>
Array of statistics
</para>
<informaltable>
<tgroup cols="3">
<colspec colwidth="10%"/>
<colspec colwidth="70%"/>
<colspec colwidth="20%"/>
<thead>
<row>
<entry>Statistic</entry>
<entry>Description</entry>
<entry>Version</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>use_slave</literal>
</entry>
<entry>
Number of statements considered as read-only by the built-in query analyzer.
Neither statements which begin with a SQL hint to force
use of slave nor statements directed to a slave by an user-defined
callback are included. The total number of statements sent to the slaves is
<literal>use_slave</literal> + <literal>use_slave_sql_hint</literal> +
<literal>use_slave_callback</literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_master</literal>
</entry>
<entry>
Number of statements not considered as read-only by the built-in query analyzer.
Neither statements which begin with a SQL hint to force
use of master nor statements directed to a master by an user-defined
callback are included. The total number of statements sent to the master is
<literal>use_master</literal> + <literal>use_master_sql_hint</literal> +
<literal>use_master_callback</literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_slave_sql_hint</literal>
</entry>
<entry>
Number of statements sent to a slave because statement begins with
the SQL hint to force use of slave.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_master_sql_hint</literal>
</entry>
<entry>
Number of statements sent to a master because statement begins with
the SQL hint to force use of master.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_last_used_sql_hint</literal>
</entry>
<entry>
Number of statements sent to server which has run the previous statement,
because statement begins with the SQL hint to force use of previously
used server.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_slave_callback</literal>
</entry>
<entry>
Number of statements sent to a slave because an user-defined callback
has chosen a slave server for statement execution.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>use_master_callback</literal>
</entry>
<entry>
Number of statements sent to a master because an user-defined callback
has chosen a master server for statement execution.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>non_lazy_connections_slave_success</literal>
</entry>
<entry>
Number of successfully opened slave connections from
configurations not using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
The total number of successfully opened slave connections
is <literal>non_lazy_connections_slave_success</literal> +
<literal>lazy_connections_slave_success</literal>
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>non_lazy_connections_slave_failed</literal>
</entry>
<entry>
Number of failed slave connection attempts from
configurations not using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
The total number of failed slave connection attempts
is <literal>non_lazy_connections_slave_failed</literal> +
<literal>lazy_connections_slave_failed</literal>
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>non_lazy_connections_master_success</literal>
</entry>
<entry>
Number of successfully opened master connections from
configurations not using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
The total number of successfully opened master connections
is <literal>non_lazy_connections_master_success</literal> +
<literal>lazy_connections_master_success</literal>
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>non_lazy_connections_master_failed</literal>
</entry>
<entry>
Number of failed master connection attempts from
configurations not using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
The total number of failed master connection attempts
is <literal>non_lazy_connections_master_failed</literal> +
<literal>lazy_connections_master_failed</literal>
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>lazy_connections_slave_success</literal>
</entry>
<entry>
Number of successfully opened slave connections from
configurations using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>lazy_connections_slave_failed</literal>
</entry>
<entry>
Number of failed slave connection attempts from
configurations using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>lazy_connections_master_success</literal>
</entry>
<entry>
Number of successfully opened master connections from
configurations using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
<row>
<entry>
<literal>lazy_connections_master_failed</literal>
</entry>
<entry>
Number of failed master connection attempts from
configurations using
<literal><link linkend="ini.mysqlnd-ms-plugin-config.lazy_connections">lazy connections</link></literal>.
</entry>
<entry>Since 1.0.0.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</refsect1>
<refsect1 role="examples">
&reftitle.examples;
<para>
<example>
<title><function>mysqlnd_ms_get_stats</function> example</title>
<programlisting role="php">
<![CDATA[
<?php
printf("mysqlnd_ms.collect_statistics = %d\n", ini_get("mysqlnd_ms.collect_statistics"));
var_dump(mysqlnd_ms_get_stats());
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
mysqlnd_ms.collect_statistics = 0
array(13) {
["use_slave"]=>
string(1) "0"
["use_master"]=>
string(1) "0"
["use_slave_forced"]=>
string(1) "0"
["use_master_forced"]=>
string(1) "0"
["use_last_used_forced"]=>
string(1) "0"
["non_lazy_connections_slave_success"]=>
string(1) "0"
["non_lazy_connections_slave_failure"]=>
string(1) "0"
["non_lazy_connections_master_success"]=>
string(1) "0"
["non_lazy_connections_master_failure"]=>
string(1) "0"
["lazy_connections_slave_success"]=>
string(1) "0"
["lazy_connections_slave_failure"]=>
string(1) "0"
["lazy_connections_master_success"]=>
string(1) "0"
["lazy_connections_master_failure"]=>
string(1) "0"
}
]]>
</screen>
</example>
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member>
<link linkend="mysqlnd-ms.configuration">Runtime configuration</link>
</member>
<member>
<link linkend="ini.mysqlnd-ms.collect_statistics">mysqlnd_ms.collect_statistics</link>
</member>
<member>
<link linkend="ini.mysqlnd-ms.enable">mysqlnd_ms.enable</link>
</member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- 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
-->

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xml:id="function.mysqlnd-ms-query-is-select" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>mysqlnd_ms_query_is_select</refname>
<refpurpose>Find whether to send the query to the master, the slave or the last used MySQL server</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<type>int</type>
<methodname>mysqlnd_ms_query_is_select</methodname>
<methodparam><type>string</type><parameter>query</parameter></methodparam>
</methodsynopsis>
<para>
Finds whether to send the query to the master, the slave or the
last used MySQL server.
</para>
<para>
The plugins built-in read/write split mechanism
will be used to analyze the query string to make a recommendation where
to send the query. The built-in read/write split mechanism is very
basic and simple. The plugin will recommend sending all queries to the
MySQL replication master server but those which begin with
<literal>SELECT</literal>, or begin with a SQL hint which
enforces sending the query to a slave server. Due to the basic
but fast algorithm the plugin may propose to run some read-only
statements such as <literal>SHOW TABLES</literal> on the replication master.
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<variablelist>
<varlistentry>
<term><parameter>query</parameter></term>
<listitem>
<para>
Query string to test.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
A return value of <constant>MYSQLND_MS_QUERY_USE_MASTER</constant>
indicates that the query should be send to the MySQL replication
master server. The function returns a value of
<constant>MYSQLND_MS_QUERY_USE_SLAVE</constant> if the query can be run
on a slave because it is considered read-only. A value of
<constant>MYSQLND_MS_QUERY_USE_LAST_USED</constant> is returned to recommend
running the query on the last used server. This can either be a MySQL
replication master server or a MySQL replication slave server.
</para>
</refsect1>
<refsect1 role="examples">
&reftitle.examples;
<para>
<example>
<title><function>mysqlnd_ms_query_is_select</function> example</title>
<programlisting role="php">
<![CDATA[
<?php
function is_select($query)
{
switch (mysqlnd_ms_query_is_select($query))
{
case MYSQLND_MS_QUERY_USE_MASTER:
printf("'%s' should be run on the master.\n", $query);
break;
case MYSQLND_MS_QUERY_USE_SLAVE:
printf("'%s' should be run on a slave.\n", $query);
break;
case MYSQLND_MS_QUERY_USE_LAST_USED:
printf("'%s' should be run on the server that has run the previous query\n", $query);
break;
default:
printf("No suggestion where to run the '%s', fallback to master recommended\n", $query);
break;
}
}
is_select("INSERT INTO test(id) VALUES (1)");
is_select("SELECT 1 FROM DUAL");
is_select("/*" . MYSQLND_MS_LAST_USED_SWITCH . "*/SELECT 2 FROM DUAL");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
INSERT INTO test(id) VALUES (1) should be run on the master.
SELECT 1 FROM DUAL should be run on a slave.
/*ms=last_used*/SELECT 2 FROM DUAL should be run on the server that has run the previous query
]]>
</screen>
</example>
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member>
<link linkend="mysqlnd-ms.constants">Predefined Constants</link>
</member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- 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
-->

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xml:id="function.mysqlnd-ms-set-user-pick-server" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>mysqlnd_ms_set_user_pick_server</refname>
<refpurpose>Sets a callback for user-defined read/write splitting</refpurpose>
</refnamediv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<type>bool</type>
<methodname>mysqlnd_ms_set_user_pick_server</methodname>
<methodparam><type>string</type><parameter>function</parameter></methodparam>
</methodsynopsis>
<para>
Sets a callback for user-defined read/write splitting. The plugin will
call the callback only if <literal>pick[]=user</literal> is the default
rule for server picking in the relevant section of the plugins configuration
file.
</para>
<para>
The plugins built-in read/write query split mechanism decisions can be
overwritten in two ways. The easiest way is to prepend the query string
with the SQL hints <constant>MYSQLND_MS_MASTER_SWITCH</constant>,
<constant>MYSQLND_MS_SLAVE_SWITCH</constant> or
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant>. Using SQL hints one can
control, for example, whether a query shall be send to the MySQL replication
master server or one of the slave servers. By help of SQL hints it is
not possible to pick a certain slave server for query execution.
</para>
<para>
Full control on server selection can be gained using a callback function.
Use of a callback is recommended to expert users only because the callback
has to cover all cases otherwise handled by the plugin.
</para>
<para>
The plugin will invoke the callback function for selecting a server from the
lists of configured master and slave servers. The callback function
inspects the query to run and picks a server for query execution by returning
the hosts URI, as found in the master and slave list.
</para>
<para>
If the lazy connections are enabled and the callback choses a slave server for
which no connection has been established so far and establishing the connection
to the slave fails, the plugin will return an error upon the next action
on the failed connection, for example, when running a query. It is the
responsibility of the application developer to handle the error. For example,
the application can re-run the query to trigger a new server selection and
callback invocation. If so, the callback must make sure to select
a different slave, or check slave availability, before returning to
the plugin to prevent an endless loop.
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<variablelist>
<varlistentry>
<term><parameter>function</parameter></term>
<listitem>
<para>
The function to be called. Class methods may also be invoked
statically using this function by passing
<literal>array($classname, $methodname)</literal> to this parameter.
Additionally class methods of an object instance may be called by passing
<literal>array($objectinstance, $methodname)</literal> to this parameter.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 role="returnvalues">
&reftitle.returnvalues;
<para>
Host to run the query on. The host URI is to be taken from the
master and slave connection lists passed to the callback function.
If callback returns a value neither found in the master nor in the slave
connection lists the plugin will fallback to the second pick method configured
via the <literal>pick[]</literal> setting in the plugin configuration file.
If not second pick method is given, the plugin falls back to the build-in
default pick method for server selection.
</para>
</refsect1>
<refsect1 role="examples">
&reftitle.examples;
<para>
<example>
<title><function>mysqlnd_ms_set_user_pick_server</function> example</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[] = localhost
slave[] = 192.168.2.27:3306
slave[] = 192.168.78.136:3306
pick[] = user
]]>
</programlisting>
<programlisting role="php">
<![CDATA[
<?php
function pick_server($connected, $query, $master, $slaves, $last_used)
{
static $slave_idx = 0;
static $num_slaves = NULL;
if (is_null($num_slaves))
$num_slaves = count($slaves);
/* default: fallback to the plugins build-in logic */
$ret = NULL;
printf("User has connected to '%s'...\n", $connected);
printf("... deciding where to run '%s'\n", $query);
$where = mysqlnd_ms_query_is_select($query);
switch ($where)
{
case MYSQLND_MS_QUERY_USE_MASTER:
printf("... using master\n");
$ret = $master[0];
break;
case MYSQLND_MS_QUERY_USE_SLAVE:
/* SELECT or SQL hint for using slave */
if (stristr($query, "FROM table_on_slave_a_only"))
{
/* a table which is only on the first configured slave */
printf("... access to table available only on slave A detected\n");
$ret = $slaves[0];
}
else
{
/* round robin */
printf("... some read-only query for a slave\n");
$ret = $slaves[$slave_idx++ % $num_slaves];
}
break;
case MYSQLND_MS_QUERY_LAST_USED:
printf("... using last used server\n");
$ret = $last_used;
break;
}
printf("... ret = '%s'\n", $ret);
return $ret;
}
mysqlnd_ms_set_user_pick_server("pick_server");
$mysqli = new mysqli("myapp", "root", "root", "test");
if (!($res = $mysqli->query("SELECT 1 FROM DUAL")))
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
else
$res->close();
if (!($res = $mysqli->query("SELECT 2 FROM DUAL")))
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
else
$res->close();
if (!($res = $mysqli->query("SELECT * FROM table_on_slave_a_only")))
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
else
$res->close();
$mysqli->close();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
User has connected to 'myapp'...
... deciding where to run 'SELECT 1 FROM DUAL'
... some read-only query for a slave
... ret = 'tcp://192.168.2.27:3306'
User has connected to 'myapp'...
... deciding where to run 'SELECT 2 FROM DUAL'
... some read-only query for a slave
... ret = 'tcp://192.168.78.136:3306'
User has connected to 'myapp'...
... deciding where to run 'SELECT * FROM table_on_slave_a_only'
... access to table available only on slave A detected
... ret = 'tcp://192.168.2.27:3306'
]]>
</screen>
</example>
</para>
</refsect1>
<refsect1 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member>
<function>mysqlnd_ms_query_is_select</function>
</member>
</simplelist>
</para>
</refsect1>
</refentry>
<!-- 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
-->

128
reference/mysqlnd_ms/ini.xml Executable file
View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<section xml:id="mysqlnd-ms.configuration" xmlns="http://docbook.org/ns/docbook">
&reftitle.runtime;
&extension.runtime;
<para>
<table>
<title>Mysqlnd_ms &ConfigureOptions;</title>
<tgroup cols="4">
<thead>
<row>
<entry>&Name;</entry>
<entry>&Default;</entry>
<entry>&Changeable;</entry>
<entry>&Changelog;</entry>
</row>
</thead>
<tbody>
<row>
<entry>mysqlnd_ms.enable</entry>
<entry>0</entry>
<entry>its PHP_INI_* value</entry>
<entry><!-- leave empty, this will be filled by an automatic script --></entry>
</row>
<row>
<entry>mysqlnd_ms.force_config_usage</entry>
<entry>0</entry>
<entry>its PHP_INI_* value</entry>
<entry><!-- leave empty, this will be filled by an automatic script --></entry>
</row>
<row>
<entry>mysqlnd_ms.ini_file</entry>
<entry></entry>
<entry>its PHP_INI_* value</entry>
<entry><!-- leave empty, this will be filled by an automatic script --></entry>
</row>
<row>
<entry>mysqlnd_ms.collect_statistics</entry>
<entry></entry>
<entry>its PHP_INI_* value</entry>
<entry><!-- leave empty, this will be filled by an automatic script --></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
&ini.descriptions.title;
<para>
<variablelist>
<varlistentry xml:id="ini.mysqlnd-ms.enable">
<term>
<parameter>mysqlnd_ms.enable</parameter>
<type>integer</type>
</term>
<listitem>
<para>
Enables or disables the plugin. If set to disabled, the extension will not plug
into
<link linkend="book.mysqlnd">mysqlnd</link> to proxy internal
<link linkend="book.mysqlnd">mysqlnd</link> C API calls.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms.force-config-usage">
<term>
<parameter>mysqlnd_ms.force_config_usage</parameter>
<type>integer</type>
</term>
<listitem>
<para>
If enabled the plugin checks if the host (server) parameter value of any
MySQL connection attempt matches a section name from the plugin configuration
file. If not, the connection attempt is blocked.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms.ini-file">
<term>
<parameter>mysqlnd_ms.ini_file</parameter>
<type>string</type>
</term>
<listitem>
<para>
Plugin specific configuration file.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms.collect_statistics">
<term>
<parameter>mysqlnd_ms.collect_statistics</parameter>
<type>integer</type>
</term>
<listitem>
<para>
Enables or disables the collection of statistics. The collection of
statistics is disabled by default for performance reasons.
Statistics are returned by the function
<function>mysqlnd_ms_get_stats</function>.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
<!-- 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
-->

View file

@ -0,0 +1,611 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: $ -->
<chapter xml:id="mysqlnd-ms.quickstart" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Quickstart and Examples</title>
<para>
The mysqlnd replication load balancing plugin is easy to use.
The quickstart will demo typical use-cases and give practical advice on getting
started.
</para>
<para>
It is strongly recommended to read the reference sections in addition to the
quickstart. The quickstart tries to avoid discussing theoretical concepts
and limitations. Instead, it will link to the reference sections. It is safe
to begin with the quickstart. However, before using the plugin in mission critical
environments we urge you to read additionally the background information from the
reference sections.
</para>
<section xml:id="mysqlnd-ms.quickstart.configuration">
<title>Setup</title>
<para>
The plugin is implemented as a PHP extension.
Please, follow the
<link linkend="mysqlnd-ms.installation">installation instructions</link> to
install the
<link xlink:href="&url.pecl.package;mysqlnd_ms">PECL/mysqlnd_ms</link> extension.
Then, load the extension into PHP and activate the plugin in the PHP configuration
file using the PHP configuration directive named
<link linkend="ini.mysqlnd-ms.enable">mysqlnd_ms.enable</link>.
</para>
<para>
The plugin is using its own configuration file. Use the PHP
configuration directive
<link linkend="ini.mysqlnd-ms.ini-file">mysqlnd_ms.ini_file</link>
to set the full path to the plugin-specific configuration file.
This file must be readable by PHP.
</para>
<para>
<example>
<title>Enabling the plugin (php.ini)</title>
<programlisting role="ini">
<![CDATA[
mysqlnd_ms.enable=1
mysqlnd_ms.ini_file=/path/to/mysqlnd_ms_plugin.ini
]]>
</programlisting>
</example>
</para>
<para>
Create a plugin-specific configuration file. Save the file to the path
set by the PHP configuration directive
<link linkend="ini.mysqlnd-ms.ini-file">mysqlnd_ms.ini_file</link>.
The plugin-specific file must be readable by PHP.
</para>
<para>
The plugins configuration file is divided into one or more sections.
Each section has a name, for example, <literal>myapp</literal>. Every section
makes its own set of configuration settings.
</para>
<para>
A section must at least list the MySQL replication master server.
The plugin supports using only one master server per section. Multi-master
MySQL replication setups are not supported.
Use the configuration directive
<link linkend="ini.mysqlnd-ms-plugin-config.master">master[]</link>
to set the hostname and the port or socket of the MySQL master server.
</para>
<para>
<example>
<title>Minimal plugin-specific configuration file (mysqlnd_ms_plugin.ini)</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
]]>
</programlisting>
</example>
</para>
<para>
It is allowed to set no MySQL slave server but it is not recommended to do.
You should always configure at least one slave server as well. Slave servers
are set using the configuration directive
<link linkend="ini.mysqlnd-ms-plugin-config.slave">slave[]</link>.
A configuration section may contain no, one or multiple
<literal>slave[]</literal> directives to set no, one or multiple
MySQL slaves.
</para>
<para>
<example>
<title>Recommended minimal plugin-specific config (mysqlnd_ms_plugin.ini)</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306
]]>
</programlisting>
</example>
</para>
<para>
If there are
at least two servers in total, the plugin can start to load balance and switch
connections. Switching connections is not always transparent and can cause
issues in certain cases. The reference section on
<link linkend="mysqlnd-ms.pooling">connection pooling and switching</link>,
<link linkend="mysqlnd-ms.transaction">transaction handling</link>,
<link linkend="mysqlnd-ms.failover">fail over</link>
<link linkend="mysqlnd-ms.loadbalancing">load balancing</link> and
<link linkend="mysqlnd-ms.rwsplit">read-write splitting</link> gives details.
For now you can continue reading the quickstart. Potential pitfalls
will be described later in the quickstart guide.
</para>
<para>
It is responsibility of the application to handle potential issues caused
by connection switches. By always configuring a master and at lease one slave
server you can be sure to detect issues early because switches become
possible.
</para>
<para>
The MySQL master and MySQL slave servers, which you configure, do not need to
be part of MySQL replication setup. For testing purpose you can use single
MySQL server and make it known to the plugin as a master and slave server
as shown below. This could help you to detect many potential issues with
connection switches. However, such a setup will not be prone to the issues
caused by replication lag.
</para>
<para>
<example>
<title>Using one server as a master and as a slave (testing only!)</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=127.0.0.1:3306
]]>
</programlisting>
</example>
</para>
</section>
<section xml:id="mysqlnd-ms.quickstart.usage">
<title>Running statements</title>
<para>
The plugin can be used with any PHP MySQL extension
(<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>)
compiled to use the <link linkend="book.mysqlnd">mysqlnd</link> library.
PECL/mysqlnd_ms plugs into the <link linkend="book.mysqlnd">mysqlnd</link> library.
It does not change the PHP MySQL extensions and their API.
</para>
<para>
Whenever a connection to MySQL is being opened, the plugin compares the host
parameter value of the connect call with the section names
from the plugin specific configuration file. If, for example, the
plugin specific configuration file has a section <literal>myapp</literal>
the section should be referenced by opening a MySQL connection to the
host <literal>myapp</literal>
</para>
<para>
<example>
<title>Plugin specific configuration file (mysqlnd_ms_plugin.ini)</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Opening a load balanced connection</title>
<programlisting role="php">
<![CDATA[
<?php
/* Load balanced following "myapp" section rules from the plugins config file */
$mysqli = new mysqli("myapp", "username", "password", "database");
$pdo = new PDO('mysql:host=myapp;dbname=database', 'username', 'password');
$mysql = mysql_connect("myapp", "username", "password");
?>
]]>
</programlisting>
</example>
</para>
<para>
All of three connections opened in the example will be load balanced.
The plugin will send read-only statements to the MySQL slave server with the
IP <literal>192.168.2.27</literal> and listening on port <literal>3306</literal>
for MySQL client connection. All other statements will be directed to the
MySQL master server running on the host <literal>localhost</literal> accepting
MySQL client connection on the Unix domain socket <literal>/tmp/mysql.sock</literal>.
The plugin will use the user name <literal>username</literal> and the password
<literal>password</literal> to connect to any of the MySQL servers listed in
the section <literal>myapp</literal> of the plugins configuration file. Upon
connect, the plugin will select <literal>database</literal> as the current
schemata. The username, password and schema name are taken from the connect
API calls and used for all servers. In other words: you must use the same
username and password for every MySQL server listed in a plugin configuration
file section.
</para>
<para>
The plugin does not change the API for running statements.
<link linkend="mysqlnd-ms.rwsplit">Read-write splitting</link>
works out of the box. The following example assumes that there is no
significant replication lag between the master and the slave.
</para>
<para>
<example>
<title>Executing statements</title>
<programlisting role="php">
<![CDATA[
<?php
/* Load balanced following "myapp" section rules from the plugins config file */
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Statements will be run on the master */
if (!$mysqli->query("DROP TABLE IF EXISTS test")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
if (!$mysqli->query("CREATE TABLE test(id INT)")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* read-only: statement will be run on a slave */
if (!($res = $mysqli->query("SELECT id FROM test")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
} else {
$row = $res->fetch_assoc();
$res->close();
printf("Slave returns id = '%s'\n", $row['id'];
}
$mysqli->close();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Slave returns id = '1'
]]>
</screen>
</example>
</para>
<note>
<para>
The plugin does not support native prepared statements. Prepared
statements are not load balanced. Most users of
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>
will be unaffected by this restriction because
<link linkend="ref.pdo-mysql">PDO_MYSQL</link> is using a
client-side prepared statement emulation by default.
</para>
</note>
</section>
<section xml:id="mysqlnd-ms.quickstart.connectionpooling">
<title>Connection state</title>
<para>
The plugin changes the semantics of a PHP MySQL connection handle.
A connection handle does no longer represent a single MySQL client-server
network connection but a connection pool. The connection pool consists
of a master connection and none, one or multiple slave connections.
</para>
<para>
Every connection from the connection pool has its own state. For example,
SQL user variables, temporary tables and transactions are part of the state.
Please, find a complete list of what belongs to the state of a connection
at the concepts page on
<link linkend="mysqlnd-ms.pooling">connection pooling and switching</link>.
If the plugin decides to switch connections for load balancing the
application could be given connection which has a different state.
Applications must be made aware of this!
</para>
<para>
<example>
<title>Plugin config with one slave and one master</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Pitfall: connection state and SQL user variables</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Connection 1, connection bound SQL user variable, no SELECT thus run on master */
if (!$mysqli->query("SET @myrole='master'")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Connection 2, run on slave because SELECT */
if (!($res = $mysqli->query("SELECT @myrole AS _role"))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
} else {
$row = $res->fetch_assoc();
$res->close();
printf("@myrole = '%s'\n", $row['_role']);
}
$mysqli->close();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
@myrole = ''
]]>
</screen>
</example>
</para>
<para>
The example openes a load balanced connection and executes two statements.
The first statement <literal>SET @myrole='master'</literal> does not begin
with the string <literal>SELECT</literal>. Therefore the plugin does not
recognize it as a read-only query which shall be run on a slave. The
plugin runs the statement on the connection to the master. The statement
sets a SQL user variable which is bound to the master connection. The
state of the master connection has been changed.
</para>
<para>
The next statement is <literal>SELECT @myrole AS _role</literal>.
The plugin does recognize it as a read-only query and sends it to
the slave. The statement is run on a connection to the slave. This
second connection does not have any SQL user variables bound to it.
It has a different state than the first connection to the master.
The requested SQL user variable is not set. The example script prints
<literal>@myrole = ''</literal>.
</para>
<para>
It is the responsibility of the application developer to take care
of the connection state. The plugin does not monitor all
connection state changing activities. Monitoring all possible cases would
be a very CPU intensive task, if it could be done at all.
</para>
<para>
The pitfalls can easily be worked around using SQL hints.
</para>
</section>
<section xml:id="mysqlnd-ms.quickstart.sqlhints">
<title>SQL Hints</title>
<para>
SQL hints can be used to force the plugin to pick a certain server from
the connection pool. Hinting the plugin to use a certain server can solve
issues caused by connection switches and connection state.
</para>
<para>
SQL hints are standard compliant SQL comments. Because
SQL comments are supposed to be ignored by SQL processing systems they
do not infere with other programs such as the MySQL Server, the MySQL Proxy
or a firewall.
</para>
<para>
Three SQL hints are supported by the plugin:
<constant>MYSQLND_MS_MASTER_SWITCH</constant>,
<constant>MYSQLND_MS_SLAVE_SWITCH</constant> and
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant>.
<constant>MYSQLND_MS_MASTER_SWITCH</constant> makes the plugin run a statement
on the master, <constant>MYSQLND_MS_SLAVE_SWITCH</constant> enforces the use
of the slave and <constant>MYSQLND_MS_MASTER_SWITCH</constant> will run a
statement on the same server that has been used for running the previous
statement.
</para>
<para>
The plugin scans the beginning of a statement for the existance of a SQL
hint. SQL hints are only recognized if they appear at the very beginning of
the statement.
</para>
<para>
<example>
<title>Plugin config with one slave and one master</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>SQL hints to prevent connection switches</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Connection 1, connection bound SQL user variable, no SELECT thus run on master */
if (!$mysqli->query("SET @myrole='master'")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Connection 1, run on master because of SQL hint */
if (!($res = $mysqli->query(sprintf("/*%s*/SELECT @myrole AS _role", MYSQLND_MS_LAST_USED_SWITCH)))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
} else {
$row = $res->fetch_assoc();
$res->close();
printf("@myrole = '%s'\n", $row['_role']);
}
$mysqli->close();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
@myrole = 'master'
]]>
</screen>
</example>
</para>
<para>
In the example the session variables issue from the previous page is solved
using <constant>MYSQLND_MS_LAST_USED_SWITCH</constant> to prevent switching
from the master to a slave when running the <literal>SELECT</literal> statement.
</para>
<para>
SQL hints can also be used to run <literal>SELECT</literal> statements
on the MySQL master server. This may be desired if the MySQL slave servers
tend to be behind the master but you need current data from the database.
</para>
<para>
<example>
<title>Fighting replication lag</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Force use of master, master has always fresh and current data */
if (!$mysqli->query(sprintf("/*%s*/SELECT critical_data FROM important_table", MYSQLND_MS_MASTER_SWITCH))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
?>
]]>
</programlisting>
</example>
</para>
<para>
use case may include the creation of tables on a slave.
If no SQL hint is given, the plugin will send <literal>CREATE</literal>
and <literal>INSERT</literal> statements to the master. Use the
SQL hint <constant>MYSQLND_MS_SLAVE_SWITCH</constant> if you want to
run any such statement on a slave, for example, to build temporary
reporting tables.
</para>
<para>
<example>
<title>Table creation on a slave</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Force use of slave */
if (!$mysqli->query(sprintf("/*%s*/CREATE TABLE slave_reporting(id INT)", MYSQLND_MS_SLAVE_SWITCH))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Continue using this particular slave connection */
if (!$mysqli->query(sprintf("/*%s*/INSERT INTO slave_reporting(id) VALUES (1), (2), (3)", MYSQLND_MS_LAST_USED_SWITCH))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Don't use MYSQLND_MS_SLAVE_SWITCH which would allow switching to another slave! */
if ($res = $mysqli->query(sprintf("/*%s*/SELECT COUNT(*) AS _num FROM slave_reporting", MYSQLND_MS_LAST_USED_SWITCH))) {
$row = $res->fetch_assoc();
$res->close();
printf("There are %d rows in the table 'slave_reporting'", $row['_num']);
} else {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
$mysqli->close();
?>
]]>
</programlisting>
</example>
</para>
<para>
The SQL hint <constant>MYSQLND_MS_LAST_USED</constant> forbids switching
connection and forces the use of the previously used connection.
</para>
</section>
<section xml:id="mysqlnd-ms.quickstart.transactions">
<title>Transactions</title>
<para>
The current version of the plugin is not transaction safe,
because it is not transaction aware. SQL transactions are
units of work to be run on a single server.
The plugin does not know when the unit of work starts and when it ends.
Therefore, the plugin may decide to switch connections in the middle
of a transaction.
</para>
<para>
You must use SQL hints to work around this limitation.
</para>
<para>
<example>
<title>Plugin config with one slave and one master</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Using SQL hints for transactions</title>
<programlisting role="php">
<![CDATA[
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Not a SELECT, will use master */
if (!$mysqli->query("START TRANSACTION")) {
/* Please use better error handling in your code */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
/* Prevent connection switch! */
if (!$mysqli->query(sprintf("/*%s*/INSERT INTO test(id) VALUES (1)", MYSQLND_MS_LAST_USED_SWITCH)))) {
/* Please do proper ROLLBACK in your code, don't just die */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if ($res = $mysqli->query(sprintf("/*%s*/SELECT COUNT(*) AS _num FROM test", MYSQLND_MS_LAST_USED_SWITCH)))) {
$row = $res->fetch_assoc();
$res->close();
if ($row['_num'] > 1000) {
if (!$mysqli->query(sprintf("/*%s*/INSERT INTO events(task) VALUES ('cleanup')", MYSQLND_MS_LAST_USED_SWITCH)))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
}
} else {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->query(sprintf("/*%s*/UPDATE log SET last_update = NOW()", MYSQLND_MS_LAST_USED_SWITCH)))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
if (!$mysqli->query(sprintf("/*%s*/COMMIT", MYSQLND_MS_LAST_USED_SWITCH)))) {
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
}
$mysqli->close();
?>
]]>
</programlisting>
</example>
</para>
<para>
Future versions of the plugin may be made aware of transactions started,
rolled back and committed using API calls. Therefore, whenever possible
you should start using API calls to control transactions unlike using
SQL statements, as shown in the above example.
</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
-->

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<reference xml:id="ref.mysqlnd-ms" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Mysqlnd_ms &Functions;</title>
&reference.mysqlnd-ms.entities.functions;
</reference>
<!-- 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
-->

329
reference/mysqlnd_ms/setup.xml Executable file
View file

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<chapter xml:id="mysqlnd-ms.setup" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.setup;
<section xml:id="mysqlnd-ms.requirements">
&reftitle.required;
<para>
<literal>PHP 5.3.6</literal> or newer.
</para>
<para>
The <literal>mysqlnd_ms</literal> replication and load balancing
plugin supports all PHP applications and all availablePHP MySQL extensions
(<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link>,
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>).
The PHP MySQL extension must be configured to use
<link linkend="book.mysqlnd">mysqlnd</link> in order to be able
to use the <literal>mysqlnd_ms</literal> plugin for
<link linkend="book.mysqlnd">mysqlnd</link>.
</para>
</section>
&reference.mysqlnd-ms.configure;
&reference.mysqlnd-ms.ini;
<section xml:id="mysqlnd-ms.plugin-ini">
<title xmlns="http://docbook.org/ns/docbook">Plugin configuration file</title>
<para>
The plugin is using its own configuration file. The configuration file
holds information on the MySQL replication master server,
the MySQL replication slave servers, the server pick (load balancing) policy,
the failover strategy and the use of lazy connections.
</para>
<para>
The PHP configuration directive
<link linkend="ini.mysqlnd-ms.ini-file"><literal>mysqlnd_ms.ini_file</literal></link>
is used to set the plugins configuration file.
</para>
<para>
The configuration file minics standard <literal>php.ini</literal> format.
It consists of one or more sections. Every section defines its own unit
of settings. There is no global section for setting defaults.
</para>
<para>
Applications reference sections by their name. Applications use section names
as the host (server) parameter to the various connect methods of the
<link linkend="ref.mysqli">mysqli</link>,
<link linkend="ref.mysql">mysql</link> and
<link linkend="ref.pdo-mysql">PDO_MYSQL</link> extensions. Upon connect
the <link linkend="book.mysqlnd">mysqlnd</link> plugin compares the hostname
with all section names from the plugin configuration file. If hostname and
section name match, the plugin will load the sections settings.
</para>
<para>
<example>
<title>Using section names example</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[] = localhost
slave[] = 192.168.2.27
slave[] = 192.168.2.28:3306
[localhost]
master[] = localhost:/tmp/mysql/mysql.sock
slave[] = 192.168.3.24:3305
slave[] = 192.168.3.65:3309
]]>
</programlisting>
<programlisting role="php">
<![CDATA[
<?php
/* All of the following connections will be load balanced */
$mysqli = new mysqli("myapp", "username", "password", "database");
$pdo = new PDO('mysql:host=myapp;dbname=database', 'username', 'password');
$mysql = mysql_connect("myapp", "username", "password");
$mysqli = new mysqli("localhost", "username", "password", "database");
?>
]]>
</programlisting>
</example>
</para>
<para>
Section names are strings. It is valid to use a section name such as
<literal>192.168.2.1</literal>, <literal>127.0.0.1</literal> or
<literal>localhost</literal>. If, for example, an application
connects to <literal>localhost</literal> and a plugin
configuration section <literal>[localhost]</literal> exists, the
semantics of the connect operation are changed. The application will
no longer only use the MySQL server running on the host
<literal>localhost</literal> but the plugin will start to load balance
MySQL queries following the rules from the <literal>[localhost]</literal>
configuration section. This way you can load balance queries from
an application without changing the applications source code.
</para>
<para>
The <literal>master[]</literal>, <literal>slave[]</literal>
and <literal>pick[]</literal> configuration directives use a list-like syntax.
Configuration directives supporting list-like syntax may appear multiple
times in a configuration section. The plugin maintains the order in
which entries appear when interpreting them. For example,
the below example shows two <literal>slave[]</literal> configuration
directives in the configuration section <literal>[myapp]</literal>.
If doing round-robin load balancing for read-only queries, the plugin
will send the first read-only query to the MySQL server
<literal>mysql_slave_1</literal> because it is the first in the list.
The second read-only query will be send to the MySQL server
<literal>mysql_slave_2</literal> because it is the second in the list.
Configuration directives supporting list-like syntax result are ordered
from top to bottom in accordance to their appearance within a configuration
section.
</para>
<para>
<example>
<title>List-like syntax</title>
<programlisting role="ini">
<![CDATA[
[myapp]
master[] = mysql_master_server
slave[] = mysql_slave_1
slave[] = mysql_slave_2
]]>
</programlisting>
</example>
</para>
<para>
Here is a short explanation of the configuration directives that can be used.
</para>
<para>
<variablelist>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.master">
<term>
<parameter>master[]</parameter>
<type>string</type>
</term>
<listitem>
<para>
URI of a MySQL replication master server. The URI follows the syntax
<literal>hostname[:port|unix_domain_socket]</literal>.
</para>
<para>
The plugin supports using only one master server.
</para>
<para>
Setting a master server is mandatory. The plugin will report a
warning upon connect if the user has failed to provide a master
server for a configuration section.
The warning may read
<literal>(mysqlnd_ms) Cannot find master section in config</literal>.
Furthermore the plugin may set an error code for the connection handle such as
<literal>HY000/2000 (CR_UNKNOWN_ERROR)</literal>. The corresponding error
message depends on your language settings.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.slave">
<term>
<parameter>slave[]</parameter>
<type>string</type>
</term>
<listitem>
<para>
URI of one or more MySQL replication slave servers. The URI follows the syntax
<literal>hostname[:port|unix_domain_socket]</literal>.
</para>
<para>
The plugin supports using one or more slave servers.
</para>
<para>
Setting a slave server is mandatory. The plugin will report a
warning upon connect if the user has failed to provide at least one slave
server for a configuration section. The warning may read
<literal>(mysqlnd_ms) Cannot find slaves section in config</literal>.
Furthermore the plugin may set an error code for the connection handle such as
<literal>HY000/2000 (CR_UNKNOWN_ERROR)</literal>. The corresponding error
message depends on your language settings.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.pick">
<term>
<parameter>pick[]</parameter>
<type>string</type>
</term>
<listitem>
<para>
Load balancing (server picking) policy. Supported policies:
<literal>random</literal>, <literal>random_once</literal> (default),
<literal>roundrobin</literal>, <literal>user</literal>.
</para>
<para>
If no load balancing policy is set, the plugin will default to
<literal>random_once</literal>. The <literal>random_once</literal>
policy picks a random slave server when running the first read-only
statement. The slave server will be used for all read-only
statements until the PHP script execution ends.
</para>
<para>
The <literal>random</literal> policy will pick a random server whenever
a read-only statement is to be executed.
</para>
<para>
If using
<literal>roundrobin</literal> the plugin iterates over the list of
configured slave servers to pick a server for statement execution.
If the plugin reaches the end of the list, it wraps around to the beginning
of the list and picks the first configured slave server.
</para>
<para>
Setting more than one load balancing policy for a configuration
section makes only sense in conjunction with <literal>user</literal>
and <function>mysqlnd_ms_set_user_pick_server</function>. If the
user defined callback fails to pick a server, the plugin falls
back to the second configured load balancing policy.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.failover">
<term>
<parameter>failover</parameter>
<type>string</type>
</term>
<listitem>
<para>
Failover policy. Supported policies:
<literal>disabled</literal> (default), <literal>master</literal>.
</para>
<para>
If no failover policy is set, the plugin will not do any
automatic failover (<literal>failover=disabled</literal>). Whenever
the plugin fails to connect a server it will emit a warning and
set the connections error code and message. Thereafter it is up to
the application to handle the error and, for example, resent the
last statement to trigger the selection of another server.
</para>
<para>
If using <literal>failover=master</literal> the plugin will implicitly
failover to a slave, if available. Please check the
concepts documentation to learn about potential
pitfalls and risks of using <literal>failover=master</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.lazy_connections">
<term>
<parameter>lazy_connections</parameter>
<type>bool</type>
</term>
<listitem>
<para>
Controls the use of lazy connections. Lazy connections
are connections which are not opened before the client sends the first
connection.
</para>
<para>
It is strongly recommended to use lazy connections.
Lazy connections help to keep the the number of open connections low.
If you disable lazy connections and, for example, configure one MySQL
replication master server and two MySQL replication slaves, the
plugin will open three connections upon the first call to a
connect function although the application might use the master
connection only.
</para>
<para>
Lazy connections bare a risk if you make heavy use of actions
which change the state of a connection. The plugin does not dispatch
all state changing actions to all connections from the connection pool.
The few dispatched actions are applied to already opened connections
only. Lazy connections opened in the future are not affected.
If, for example, the connection character set is changed using a
PHP MySQL API call, the plugin will change the character set of all
currently opened connection. It will not remeber the character set
change to apply it on lazy connections opened in the future. As a
result the internal connection pool would hold connections using
different character sets. This is not desired. Remember that character
sets are taken into account for escaping.
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config.master_on_write">
<term>
<parameter>master_on_write</parameter>
<type>bool</type>
</term>
<listitem>
<para>
If set, the plugin will use the master server only after the
first statement has been executed on the master. Applications
can still send statements to the slaves using SQL hints to
overrule the automatic decision.
</para>
<para>
The setting may help with replication lag. If an application runs
an <literal>INSERT</literal> the plugin will, by default, use the
master to execute all following statements, including
<literal>SELECT</literal> statements. This helps to avoid problems
with reads from slaves which have not replicated the
<literal>INSERT</literal> yet.
</para>
</listitem>
</varlistentry>
</variablelist>
</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
-->

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!--
Do NOT translate this file
-->
<versions>
<!-- Functions -->
<function name='mysqlnd_ms_set_user_pick_server' from='PECL mysqlnd_ms &gt;= 1.0.0'/>
<function name='mysqlnd_ms_query_is_select' from='PECL mysqlnd_ms &gt;= 1.0.0'/>
<function name='mysqlnd_ms_get_stats' from='PECL mysqlnd_ms &gt;= 1.0.0'/>
</versions>
<!-- 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
-->