Concepts
Architecture 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 mysqlnd plugin to replace selected mysqlnd C methods. 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 SELECT, the SQL hint /*ms=slave*/ or a slave had been choose for running the previous query and the query starts with the SQL hint /*ms=last_used*/. In all other cases the query will be sent to the MySQL replication master server. 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. 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 BEGIN TRANSACTION, the plugin leaves it to the user to handle. Please, find further details below.
Connection pooling and switching The replication and load balancing plugin changes the semantics of a PHP MySQL connection handle. The existing API of the PHP MySQL extensions (mysqli, mysql, PDO_MYSQL) 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. The plugin breaks the one-by-one relationship between a mysqli, mysql, PDO_MYSQL connection handle and a MySQL wire connection. If using the plugin a mysqli, mysql, PDO_MYSQL 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. 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. The following list shows what the connection state consists of. The list may not be complete. Transaction status Temporary tables Table locks Session system variables and session user variables Session system variables and session user variables Prepared statements HANDLER variables Locks acquired with GET_LOCK() 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. 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. Broadcasted messages 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. List of connection state changing client library calls broadcasted to all open connections in the connection pool. Library call Notes Version change_user Called by the mysqli_change_user user API call. Also triggered upon reuse of a persistent mysqli connection. Since 1.0.0. select_db Called by the following user API calls: mysql_select_db, mysql_list_tables, mysql_db_query, mysql_list_fields, mysqli_select_db. Since 1.0.0. set_charset Called by the following user API calls: mysql_set_charset. mysqli_set_charset. Since 1.0.0. set_server_option Called by the following user API calls: mysqli_multi_query, mysqli_real_query, mysqli_query, mysql_query. Since 1.0.0. set_client_option Called by the following user API calls: mysqli_options, mysqli_ssl_set, mysqli_connect, mysql_connect, mysql_pconnect. Since 1.0.0. set_autocommit Called by the following user API calls: mysqli_autocommit, PDO::setAttribute(PDO::ATTR_AUTOCOMMIT). Since 1.0.0. PHP >= 5.4.0. ssl_set Called by the following user API calls: mysqli_ssl_set. Since 1.1.0. If any of the above listed calls is to be executed, the plugin loops over all currently open master and slave connections. The loop continues until all servers have been contacted. The loop does not break, if a server indicates a failure. If possible, the failure will be propagated to the calling user API function. Depending on the user API function, which has triggered the underlying library function, users may be able to detect the failure. Broadcasting and lazy connections The plugin does not proxy or remember all settings to apply them on connections opened in the future. This is important to remember, if using lazy connections. Lazy connections are connections which are not opened before the client sends the first connection. Use of lazy connections is the default plugin action. Settings of the following connection state changing library calls are recorded to be used when opening a lazy connection to ensure that connection state of all connections in the connection pool is comparable. Library call Notes Version change_user User, password and database recorded for future use. Since 1.1.0. select_db Database recorded for future use. Since 1.1.0. set_charset Calls set_client_option(MYSQL_SET_CHARSET_NAME, charset) on lazy connection to ensure charset will be used upon opening the lazy connection. Since 1.1.0. set_autocommit Adds SET AUTOCOMMIT=0|1 to the list of init commands of a lazy connection using set_client_option(MYSQL_INIT_COMMAND, "SET AUTOCOMMIT=...%quot;). Since 1.1.0. PHP >= 5.4.0. Please note that the connection state is not only changed by API calls. Thus, even if PECL mysqlnd_ms monitors all API calls, the application still needs to take care. Ultimately, it is in the application developers reposibility to maintain connection state, if needed.
Transaction handling 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. 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. 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. The latest version of the mysqlnd library, as found in PHP 5.4.0, allows the plugin to subclass the library C API call trx_autocommit() to detect the status of the autocommit mode. The PHP MySQL extensions either issue a query such as SET AUTOCOMMIT=0|1 or use the mysqlnd library call trx_autcommit() to control the autocommit setting. If an extension makes use of trx_autocommit(), the plugin can be made transaction aware. Transaction awareness cannot be achieved, if using SQL to set the autocommit mode. The experimental pluging configuration option trx_stickiness=master can be used to make the plugin transaction aware if using PHP 5.4.0 or newer. In this mode the plugin stops load balancing if autocommit gets disabled and directs all statements to the master until autocommit gets enabled.
Failover 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. 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. An user who does not change the connection state after opening a connection may activate automatic master failover. The failover policy is configured in the plugins configuration file by help of the failover configuration directive.
Load balancing Four load balancing strategies are supported to distribute read-only statements over the configured MySQL slave servers: random, random_once (default), roundrobin, user. The load balancing policy is configured in the plugins configuration file using the pick[] configuration directive.
Read-write splitting 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 SELECT, the SQL hint /*ms=slave*/ or a slave had been chosen for running the previous query and the query starts with the SQL hint /*ms=last_used*/. In all other cases the query will be send to the MySQL replication master server. 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 constants 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. The built-in read-write splitter can be replaced by a user-defined one, see also mysqlnd_ms_set_user_pick_server. A user-defined read-write splitter can ask the built-in logic to make a proposal where to sent a statement by invoking mysqlnd_ms_is_select. 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 SELECT 1 FROM DUAL; INSERT INTO test(id) VALUES (1); ... the plugin will run it on a slave although the statement is not read-only.