From ee9d6ef3e1b127f760c537d3c45beb0eaf0ed0c6 Mon Sep 17 00:00:00 2001 From: Ulf Wendel Date: Tue, 13 Dec 2011 19:27:58 +0000 Subject: [PATCH] Extending quickstart section to have at least some examples and documentation ready when the 1.2.0-alpha release is done... in a few minutes. More documentation coming this week. git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@320964 c90b9560-bf6c-de11-be94-00142212c4b1 --- reference/mysqlnd_ms/concepts.xml | 11 + reference/mysqlnd_ms/quickstart.xml | 617 ++++++++++++++++++++++++++++ 2 files changed, 628 insertions(+) diff --git a/reference/mysqlnd_ms/concepts.xml b/reference/mysqlnd_ms/concepts.xml index 5f7dfc32ff..3ea118941a 100755 --- a/reference/mysqlnd_ms/concepts.xml +++ b/reference/mysqlnd_ms/concepts.xml @@ -1115,6 +1115,17 @@ version = 5.6.2-m5-log + + Server-side global transaction ID support + + The plugin is prepared to support MySQL servers which implement global + transaction ID support and maintain a global transaction ID themselves. + Client-side injection would not be necessary with such servers. However, + without any server supporting it yet and in heterogenous environments with + old MySQL servers, client-side injection is a valuable, although not ideal, + option. + +
diff --git a/reference/mysqlnd_ms/quickstart.xml b/reference/mysqlnd_ms/quickstart.xml index 4d81459c33..39e61ebfc5 100755 --- a/reference/mysqlnd_ms/quickstart.xml +++ b/reference/mysqlnd_ms/quickstart.xml @@ -777,6 +777,623 @@ $mysqli->close();
+
+ Service level and consistency + + Version requirement + + Service levels have been introduced in mysqlnd_ms version 1.2.0-alpha. + + + + Different types of MySQL cluster solutions offer different service and + data consistency levels to their users. An asynchronous MySQL replication cluster + offers eventual consistency by default. A read executed on an asynchronous slave + may return current, stale or no data at all, depending on whether the slave + has replayed all master changes or not. + + + Applications using an MySQL replication cluster need to be designed to work + correctly with eventual consistent data. In some cases, however, stale data + is not acceptable. In those cases only certain slaves or even only master accesses are + allowed to achieve the required quality of service from the cluster. + + + As of PECL/mysqlnd_ms 1.2.0 the plugin is capable of selecting automatically + MySQL replication nodes that can deliver session consistency or + strong consistency. Session consistency means that one client can read its writes. + Other clients may or may not see the clients' write. Strong consistency means + that all clients will see all writes from the client. + + + + Session consistency: read your writes + + + + + + + + Requesting session consistency + +query("INSERT INTO orders(order_id, item) VALUES (1, 'christmas tree, 1.8m')")) { + /* Please use better error handling in your code */ + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); +} + +/* Request session consistency: read your writes */ +if (!mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION)) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* Plugin picks a node which has the changes, here: master */ +if (!$res = $mysqli->query("SELECT item FROM orders WHERE order_id = 1")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +var_dump($res->fetch_assoc()); + +/* Back to eventual consistency: stale data allowed */ +if (!mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* Plugin picks any slave, stale data is allowed */ +if (!$res = $mysqli->query("SELECT item, price FROM specials")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); +?> +]]> + + + + + Service levels can be set in the plugins configuration file and at runtime + using mysqlnd_ms_set_qos(). In the example the function is used to enforce + session consistency (read your writes) for all future statements until further notice. + The SELECT statement on the orders table + is run on the master to ensure the previous write can be seen by the client. + Read-write splitting logic has been adapted to fulfill the service level. + + + After the application has read its changes from the orders table + it returns to the default service level, which is eventual consistency. Eventual + consistency puts no restrictions on choosing a node for statement execution. + Thus, the SELECT statement on the specials + table is executed on a slave. + + + + Maximum age/slave lag + + + + + + + + Limiting slave lag + +errno, $mysqli->error)); + +/* Plugin picks any slave, which may or may not have the changes */ +if (!$res = $mysqli->query("SELECT item, price FROM daytrade")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + + +/* Back to default: use of all slaves and masters permitted */ +if (!mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); +?> +]]> + + + + + The eventual consistency service level can be used with an optional + parameter to set a maximum slave lag for choosing slaves. If set, + the plugin checks SHOW SLAVE STATUS for all + configured slaves. In case of the example, only slaves + for which Slave_IO_Running=Yes, + Slave_SQL_Running=Yes and + Seconds_Behind_Master <= 4 + is true are considered for executing the statement + SELECT item, price FROM daytrade. + + + Checking SHOW SLAVE STATUS is done transparently from + an applications perspective. Errors, if any, are reported as + warnings. No error will be set on the connection handle. Even if all + SHOW SLAVE STATUS SQL statements executed by + the plugin fail, the execution of the users statement is not stopped, given + that master fail over is enabled. Thus, no application changes are required. + + + Expensive and slow operation + + Checking SHOW SLAVE STATUS for all slaves adds overhead + to the application. It is an expensive and slow background operation. + Try to minimize the use of it. Unfortunately, a MySQL replication cluster + does not give clients the possibility to request a list of candidates + from a central instance. + Thus, a more efficient way of checking the slaves lag is not available. + + + Please, note the limitations and properties of SHOW SLAVE STATUS + as explained in the MySQL reference manual. + + + + To prevent mysqlnd_ms from emitting a warning if no slaves can be found + that lag no more than four seconds behind the master, it is necessary to + enable master fail over in the plugins configuration file. If no slaves + can be found and fail over is turned on, the plugin picks a master for + executing the statement. + + + If no slave can be found and fail over is turned off, the plugin emits + a warning, it does not execute the statement and it sets an error + on the connection. + + + + Fail over not set + + + + + + + + No slave within time limit + +errno, $mysqli->error)); + +/* Plugin picks any slave, which may or may not have the changes */ +if (!$res = $mysqli->query("SELECT item, price FROM daytrade")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + + +/* Back to default: use of all slaves and masters permitted */ +if (!mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_EVENTUAL)) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); +?> +]]> + + &example.outputs; + + + + + + + Fail over logic is work in progress + + The details of the fail over logic may change in future versions. + + +
+
+ Global transaction IDs + + Version requirement + + Global transaction ID injection has been introduced in mysqlnd_ms version 1.2.0-alpha. + The feature is not required for synchronous clusters, such as MySQL Cluster. + Use it with asynchronous clusters such as classical MySQL replication. + + + + In its most basic form a global transaction ID (GTID) is a counter in a table on the + master. The counter is incremented whenever a transaction is comitted on the master. + Slaves replicate the table. The counter serves two puposes. In case of a + master failure, it helps the database administrator to identify the most recent slave + for promoting it to the new master. The most recent slave is the one with the + highest counter value. Applications can use the global transaction ID to search + for slaves which have replicated a certain write (identified by a global transaction ID) + already. + + + PECL/mysqlnd_ms can inject SQL for every comitted transaction to increment a GTID counter. + The so created GTID is accessible by the application to identify an applications + write operation. This enables the plugin to deliver session consistency (read your writes) + service level by not only quering masters but also slaves which have replicated + the change already. Read load is taken away from the master. + + + Client-side global transaction ID injection has some limitations. Please, + read the concepts section + carefully to fully understand the principles and ideas + behind it, before using in production environments. The background knowledge + is not required to continue with the quickstart. + + + First, create a counter table on your master server and insert a record into it. + The plugin does not assist creating the table. + Database administrators must make sure it exists. Depending on the error + reporting mode, the plugin will silently ignore the lack of the table or bail out. + + + + Create counter table on master + + + + + + + In the plugins configuration file set the SQL to update the + global transaction ID table using on_commit + from the global_transaction_id_injection + section. Make sure the table name used for the UPDATE + statement is fully qualified. In the example, + test.trx is used to refer to table trx + in the schema test. Use the table that was created in + the previous step. It is important to set the fully qualified table name + because the connection on which the injection is done may use a different + default database. Make sure the user that opens the connection + is allowed to execute the UPDATE. + + + Enable reporting of errors that may occur when mysqlnd_ms does global + transaction ID injection. + + + + Plugin config: SQL for client-side GTID injection + + + + + + + + Transparent global transaction ID injection + +query("DROP TABLE IF EXISTS test")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* auto commit mode, transaction on master, GTID must be incremented */ +if (!$mysqli->query("CREATE TABLE test(id INT)")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* auto commit mode, transaction on master, GTID must be incremented */ +if (!$mysqli->query("INSERT INTO test(id) VALUES (1)")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* auto commit mode, read on slave, no increment */ +if (!($res = $mysqli->query("SELECT id FROM test"))) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +var_dump($res->fetch_assoc()); +?> +]]> + + &example.outputs; + + + string(1) "1" +} +]]> + + + + + The example runs three statements in auto commit mode on the master, causing + three transactions on the master. For every such statement, the plugin will + inject the configured UPDATE transparently before executing + the users SQL statement. After the example script has finished the global + transaciton ID counter on the master has been incremented by three. + + + The fourth SQL statement executed in the example, a SELECT, + does not trigger an increment. Only transactions (writes) executed on a master + shall increment the GTID counter. + + + SQL for global transaction ID: efficient solution wanted! + + The SQL used for the client-side global transaction ID emulation is inefficient. + It is optimized for clearity not for performance. Do not use it for production + environments. Please, help finding an efficient solution for inclusion in the manual. + We appreciate your input. + + + + + Plugin config: SQL for fetching GTID + + + + + + + + Obtaining GTID after injection + +query("DROP TABLE IF EXISTS test")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +printf("GTID after transaction %s\n", mysqlnd_ms_get_last_gtid($mysqli)); + +/* auto commit mode, transaction on master, GTID must be incremented */ +if (!$mysqli->query("CREATE TABLE test(id INT)")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +printf("GTID after transaction %s\n", mysqlnd_ms_get_last_gtid($mysqli)); +?> +]]> + + &example.outputs; + + + + + + + Applications can ask mysqlnd_ms for a global transaction ID which + belongs to the last write transactions. The function mysqlnd_ms_get_last_gtid() + may be called after the GTID has been incremented. The + function returns the GTID obtained when executing the SQL statement from + the fetch_last_gtid entry of the + global_transaction_id_injection section from + the plugins configuration file. Applications are advices not to run the SQL + statement themselves as this bares the risk of accidently causing an implicit + GTID increment. Also, if the function is used, it is easy to migrate + an application from one SQL statement for fetching a transaction ID to another, + for example, if any MySQL server ever features built-in global transaction ID support. + + + The quickstart shows a SQL statement which will return a GTID equal or greater + to that created for the previous statement. It is exactly the GTID created + for the previous statement if no other clients have incremented the GTID in the + time span between the statement execution and the SELECT + to fetch the GTID. Otherwise, it is greater. + + + + Plugin config: Checking for a certain GTID + += #GTID", + "report_error":true + } + } +} +]]> + + + + + + Session consistency service level and GTID combined + +query("DROP TABLE IF EXISTS test") || + !$mysqli->query("CREATE TABLE test(id INT)") || + !$mysqli->query("INSERT INTO test(id) VALUES (1)")) + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); + +/* GTID as an identifier for the last write */ +$gtid = mysqlnd_ms_get_last_gtid($mysqli); + +/* Session consistency (read your writes): try to read from slaves not only master */ +if (false == mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION, MYSQLND_MS_QOS_OPTION_GTID, $gtid)) { + die(sprintf("[006] [%d] %s\n", $mysqli->errno, $mysqli->error)); +} + +/* Either run on master or a slave which has replicated the INSERT */ +if (!($res = $mysqli->query("SELECT id FROM test"))) { + die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error)); +} + +var_dump($res->fetch_assoc()); +?> +]]> + + + + + A GTID returned from mysqlnd_ms_get_last_gtid() + can be used as an option for the session consistency service level. + Session consistency delivers read your writes. Session consistency can + be requested by calling mysqlnd_ms_set_qos(). + In the example, the plugin will execute the SELECT + statement either on the master or on a slave which has replicated + the previous INSERT already. + + + PECL/mysqlnd_ms will transparently check every configured slave if + it has replicated the INSERT by checking the slaves + GTID table. The check is done running the SQL set with the + check_for_gtid option from the + global_transaction_id_injection section of + the plugins configuration file. Please note, that this is a slow and + expensive procedure. Applications should try to use it sparsely and only + if read load on the master becomes to high otherwise. + +