Table of Contents
mysqlnd_qc_clear_cachemysqlnd_qc_get_available_handlersmysqlnd_qc_get_cache_infomysqlnd_qc_get_core_statsmysqlnd_qc_get_normalized_query_trace_logmysqlnd_qc_get_query_trace_logmysqlnd_qc_set_cache_conditionmysqlnd_qc_set_is_selectmysqlnd_qc_set_storage_handlermysqlnd_qc_set_user_handlersCopyright 1997-2014 the PHP Documentation Group.
The mysqlnd query result cache plugin adds easy to use client-side query caching to all PHP MySQL extensions using mysqlnd.
As of version PHP 5.3.3 the MySQL native driver for PHP (
mysqlnd) features an internal plugin C API. C
plugins, such as the query cache plugin, can extend the
functionality of mysqlnd.
Mysqlnd plugins such as the query cache plugin operate transparent from a user perspective. The cache plugin supports all PHP applications and all PHP MySQL extensions ( mysqli, mysql, PDO_MYSQL). It does not change existing APIs.
No significant application changes are required to cache a query. The cache has two operation modes. It will either cache all queries (not recommended) or only those queries marked with a certain SQL hint (recommended).
Copyright 1997-2014 the PHP Documentation Group.
Transparent and therefore easy to use
supports all PHP MySQL extensions
no API changes
very little application changes required
Flexible invalidation strategy
Time-to-Live (TTL)
user-defined
Storage with different scope and life-span
Default (Hash, process memory)
MEMCACHE
sqlite
user-defined
Built-in slam defense to prevent cache stampeding.
Copyright 1997-2014 the PHP Documentation Group.
The current 1.0.1 release of PECL mysqlnd_qc does not support PHP 5.4. Version 1.1.0-alpha lifts this limitation.
Prepared statements and unbuffered queries are fully supported.
Thus, the plugin is capable of caching all statements issued with
mysqli or PDO_MySQL, which
are the only two PHP MySQL APIs to offer prepared statement
support.
Copyright 1997-2014 the PHP Documentation Group.
The shortcut mysqlnd_qc stands for
mysqlnd query cache plugin. The name was chosen
for a quick-and-dirty proof-of-concept. In the beginning the
developers did not expect to continue using the code base.
Sometimes PECL/mysqlnd_qc has also been called
client-side query result set cache.
Copyright 1997-2014 the PHP Documentation Group.
The mysqlnd query cache plugin is easy to use. This quickstart will demo typical use-cases, and provide practical advice on getting started.
It is strongly recommended to read the reference sections in addition to the quickstart. 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.
Most of the examples use the mysqli extension because it is the most feature complete PHP MySQL extension. However, the plugin can be used with any PHP MySQL extension that is using the mysqlnd library.
Copyright 1997-2014 the PHP Documentation Group.
The query cache 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, it gets registered as a mysqlnd plugin to replace selected mysqlnd C methods. Hereby, it can change the behaviour of any PHP MySQL extension (mysqli, PDO_MYSQL, mysql) compiled to use the mysqlnd library without changing the extensions API. This makes the plugin compatible with each and every PHP MySQL application. Because existing APIs are not changed, it is almost transparent to use. Please, see the mysqlnd plugin API description for a discussion of the advantages of the plugin architecture and a comparison with proxy based solutions.
Transparent to use
At PHP run time PECL/mysqlnd_qc can proxy queries send from PHP (mysqlnd) to the MySQL server. It then inspects the statement string to find whether it shall cache its results. If so, result set is cached using a storage handler and further executions of the statement are served from the cache for a user-defined period. The Time to Live (TTL) of the cache entry can either be set globally or on a per statement basis.
A statement is either cached if the plugin is instructed to
cache all statements globally using a or, if the query string
starts with the SQL hint (/*qc=on*/). The
plugin is capable of caching any query issued by calling
appropriate API calls of any of the existing PHP MySQL
extensions.
Flexible storage: various storage handler
Various storage handler are supported to offer different scopes for cache entries. Different scopes allow for different degrees in sharing cache entries among clients.
default (built-in): process memory,
scope: process, one or more web requests depending on PHP
deployment model used
APC: shared memory, scope: single
server, multiple web requests
SQLite: memory or file, scope: single
server, multiple web requests
MEMCACHE: main memory, scope: single or
multiple server, multiple web requests
user (built-in): user-defined - any,
scope: user-defined - any
Support for the APC,
SQLite and MEMCACHE
storage handler has to be enabled at compile time. The
default and user handler are
built-in. It is possible to switch between compiled-in storage
handlers on a per query basis at run time. However, it is
recommended to pick one storage handler and use it for all cache
entries.
Built-in slam defense to avoid overloading
To avoid overload situations the cache plugin has a built-in slam defense mechanism. If a popular cache entries expires many clients using the cache entries will try to refresh the cache entry. For the duration of the refresh many clients may access the database server concurrently. In the worst case, the database server becomes overloaded and it takes more and more time to refresh the cache entry, which in turn lets more and more clients try to refresh the cache entry. To prevent this from happening the plugin has a slam defense mechanism. If slam defense is enabled and the plugin detects an expired cache entry it extends the life time of the cache entry before it refreshes the cache entry. This way other concurrent accesses to the expired cache entry are still served from the cache for a certain time. The other concurrent accesses to not trigger a concurrent refresh. Ideally, the cache entry gets refreshed by the client which extended the cache entries lifespan before other clients try to refresh the cache and potentially cause an overload situation.
Unique approach to caching
PECL/mysqlnd_qc has a unique approach to caching result sets that is superior to application based cache solutions. Application based solutions first fetch a result set into PHP variables. Then, the PHP variables are serialized for storage in a persistent cache, and then unserialized when fetching. The mysqlnd query cache stores the raw wire protocol data sent from MySQL to PHP in its cache and replays it, if still valid, on a cache hit. This way, it saves an extra serialization step for a cache put that all application based solutions have to do. It can store the raw wire protocol data in the cache without having to serialize into a PHP variable first and deserializing the PHP variable for storing in the cache again.
Copyright 1997-2014 the PHP Documentation Group.
The plugin is implemented as a PHP extension. See also the installation instructions to install the PECL/mysqlnd_qc extension.
Compile or configure the PHP MySQL extension (mysqli, PDO_MYSQL, mysql) that you plan to use with support for the mysqlnd library. PECL/mysqlnd_qc is a plugin for the mysqlnd library. To use the plugin with any of the existing PHP MySQL extensions (APIs), the extension has to use the mysqlnd library.
Then, load the extension into PHP and activate the plugin in the PHP configuration file using the PHP configuration directive named mysqlnd_qc.enable_qc.
Copyright 1997-2014 the PHP Documentation Group.
There are four ways to trigger caching of a query.
mysqlnd_qc_is_select
mysqlnd_set_cache_condition for rule based automatic per query decisions
mysqlnd_qc.cache_by_default = 1
to cache all queries blindly
Use of SQL hints and
mysqlnd_qc.cache_by_default
= 1 are explained below. Please, refer to the
function reference on
mysqlnd_qc_is_select
for a description of using a callback and,
mysqlnd_qc_set_cache_condition
on how to set rules for automatic caching.
A SQL hint is a SQL standards compliant comment. As a SQL
comment it is ignored by the database. A statement is considered
eligible for caching if it either begins with the SQL hint
enabling caching or it is a SELECT statement.
An individual query which shall be cached must begin with the
SQL hint /*qc=on*/. It is recommended to use
the PHP constant
MYSQLND_QC_ENABLE_SWITCH
instead of using the string value.
not eligible for caching and not cached: INSERT
INTO test(id) VALUES (1)
not eligible for caching and not cached: SHOW
ENGINES
eligible for caching but uncached: SELECT id FROM
test
eligible for caching and cached: /*qc=on*/SELECT
id FROM test
The examples SELECT statement string is
prefixed with the
MYSQLND_QC_ENABLE_SWITCH
SQL hint to enable caching of the statement. The SQL hint must
be given at the very beginning of the statement string to enable
caching.
Example 8.2 Using the MYSQLND_QC_ENABLE_SWITCH SQL hint
mysqlnd_qc.enable_qc=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
/* Will be cached because of the SQL hint */
$start = microtime(true);
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
printf("Total time uncached query: %.6fs\n", microtime(true) - $start);
/* Cache hit */
$start = microtime(true);
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
printf("Total time cached query: %.6fs\n", microtime(true) - $start);
?>
The above examples will output something similar to:
array(1) {
["id"]=>
string(1) "1"
}
Total time uncached query: 0.000740s
array(1) {
["id"]=>
string(1) "1"
}
Total time cached query: 0.000098s
If nothing else is configured, as it is the case in the
quickstart example, the plugin will use the built-in
default storage handler. The
default storage handler uses process memory
to hold a cache entry. Depending on the PHP deployment model, a
PHP process may serve one or more web requests. Please, consult
the web server manual for details. Details make no difference
for the examples given in the quickstart.
The query cache plugin will cache all queries regardless if the
query string begins with the SQL hint which enables caching or
not, if the PHP configuration directive
mysqlnd_qc.cache_by_default
is set to 1. The setting
mysqlnd_qc.cache_by_default
is evaluated by the core of the query cache plugins. Neither the
built-in nor user-defined storage handler can overrule the
setting.
The SQL hint /*qc=off*/ can be used to
disable caching of individual queries if
mysqlnd_qc.cache_by_default
= 1 It is recommended to use the PHP constant
MYSQLND_QC_DISABLE_SWITCH
instead of using the string value.
Example 8.3 Using the MYSQLND_QC_DISABLE_SWITCH SQL hint
mysqlnd_qc.enable_qc=1
mysqlnd_qc.cache_by_default=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
/* Will be cached although no SQL hint is present because of mysqlnd_qc.cache_by_default = 1*/
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
$mysqli->query("DELETE FROM test WHERE id = 1");
/* Cache hit - no automatic invalidation and still valid! */
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
/* Cache miss - query must not be cached because of the SQL hint */
$res = $mysqli->query("/*" . MYSQLND_QC_DISABLE_SWITCH . "*/SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
?>
The above examples will output:
array(1) {
["id"]=>
string(1) "1"
}
array(1) {
["id"]=>
string(1) "1"
}
NULL
PECL/mysqlnd_qc forbids caching of statements for which at least
one column from the statements result set shows no table name in
its meta data by default. This is usually the case for columns
originating from SQL functions such as NOW()
or LAST_INSERT_ID(). The policy aims to
prevent pitfalls if caching by default is used.
Example 8.4 Example showing which type of statements are not cached
mysqlnd_qc.enable_qc=1
mysqlnd_qc.cache_by_default=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1)");
for ($i = 0; $i < 3; $i++) {
$start = microtime(true);
/* Note: statement will not be cached because of NOW() use */
$res = $mysqli->query("SELECT id, NOW() AS _time FROM test");
$row = $res->fetch_assoc();
/* dump results */
var_dump($row);
printf("Total time: %.6fs\n", microtime(true) - $start);
/* pause one second */
sleep(1);
}
?>
The above examples will output something similar to:
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:43:10"
}
Total time: 0.000540s
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:43:11"
}
Total time: 0.000555s
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:43:12"
}
Total time: 0.000549s
It is possible to enable caching for all statements including
those which has columns in their result set for which MySQL
reports no table, such as the statement from the example. Set
mysqlnd_qc.cache_no_table
= 1 to enable caching of such statements.
Please, note the difference in the measured times for the above
and below examples.
Example 8.5 Enabling caching for all statements using the
mysqlnd_qc.cache_no_table ini setting
mysqlnd_qc.enable_qc=1
mysqlnd_qc.cache_by_default=1
mysqlnd_qc.cache_no_table=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1)");
for ($i = 0; $i < 3; $i++) {
$start = microtime(true);
/* Note: statement will not be cached because of NOW() use */
$res = $mysqli->query("SELECT id, NOW() AS _time FROM test");
$row = $res->fetch_assoc();
/* dump results */
var_dump($row);
printf("Total time: %.6fs\n", microtime(true) - $start);
/* pause one second */
sleep(1);
}
?>
The above examples will output something similar to:
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:47:45"
}
Total time: 0.000546s
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:47:45"
}
Total time: 0.000187s
array(2) {
["id"]=>
string(1) "1"
["_time"]=>
string(19) "2012-01-11 15:47:45"
}
Total time: 0.000167s
Although
mysqlnd_qc.cache_no_table
= 1 has been created for use with
mysqlnd_qc.cache_by_default
= 1 it is bound it. The plugin will evaluate
the
mysqlnd_qc.cache_no_table
whenever a query is to be cached, no matter whether caching
has been enabled using a SQL hint or any other measure.
Copyright 1997-2014 the PHP Documentation Group.
The default invalidation strategy of the query cache plugin is
Time to Live (TTL). The built-in storage
handlers will use the default TTL defined by
the PHP configuration value
mysqlnd_qc.ttl
unless the query string contains a hint for setting a different
TTL. The TTL is specified
in seconds. By default cache entries expire after
30 seconds
The example sets mysqlnd_qc.ttl=3 to cache
statements for three seconds by default. Every second it updates
a database table record to hold the current time and executes a
SELECT statement to fetch the record from the
database. The SELECT statement is cached for
three seconds because it is prefixed with the SQL hint enabling
caching. The output verifies that the query results are taken
from the cache for the duration of three seconds before they are
refreshed.
Example 8.6 Setting the TTL with the mysqlnd_qc.ttl ini setting
mysqlnd_qc.enable_qc=1
mysqlnd_qc.ttl=3
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id VARCHAR(255))");
for ($i = 0; $i < 7; $i++) {
/* update DB row */
if (!$mysqli->query("DELETE FROM test") ||
!$mysqli->query("INSERT INTO test(id) VALUES (NOW())"))
/* Of course, a real-life script should do better error handling */
die(sprintf("[%d] %s\n", $mysqli->errno, $mysqli->error));
/* select latest row but cache results */
$query = "/*" . MYSQLND_QC_ENABLE_SWITCH . "*/";
$query .= "SELECT id AS _time FROM test";
if (!($res = $mysqli->query($query)) ||
!($row = $res->fetch_assoc()))
{
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
$res->free();
printf("Wall time %s - DB row time %s\n", date("H:i:s"), $row['_time']);
/* pause one second */
sleep(1);
}
?>
The above examples will output something similar to:
Wall time 14:55:59 - DB row time 2012-01-11 14:55:59 Wall time 14:56:00 - DB row time 2012-01-11 14:55:59 Wall time 14:56:01 - DB row time 2012-01-11 14:55:59 Wall time 14:56:02 - DB row time 2012-01-11 14:56:02 Wall time 14:56:03 - DB row time 2012-01-11 14:56:02 Wall time 14:56:04 - DB row time 2012-01-11 14:56:02 Wall time 14:56:05 - DB row time 2012-01-11 14:56:05
As can be seen from the example, any TTL
based cache can serve stale data. Cache entries are not
automatically invalidated, if underlying data changes.
Applications using the default TTL
invalidation strategy must be able to work correctly with stale
data.
A user-defined cache storage handler can implement any invalidation strategy to work around this limitation.
The default TTL can be overruled using the
SQL hint /*qc_tt=seconds*/. The SQL hint must
be appear immediately after the SQL hint which enables caching.
It is recommended to use the PHP constant
MYSQLND_QC_TTL_SWITCH
instead of using the string value.
Example 8.7 Setting TTL with SQL hints
<?php
$start = microtime(true);
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
printf("Default TTL\t: %d seconds\n", ini_get("mysqlnd_qc.ttl"));
/* Will be cached for 2 seconds */
$sql = sprintf("/*%s*//*%s%d*/SELECT id FROM test WHERE id = 1", MYSQLND_QC_ENABLE_SWITCH, MYSQLND_QC_TTL_SWITCH, 2);
$res = $mysqli->query($sql);
var_dump($res->fetch_assoc());
$res->free();
$mysqli->query("DELETE FROM test WHERE id = 1");
sleep(1);
/* Cache hit - no automatic invalidation and still valid! */
$res = $mysqli->query($sql);
var_dump($res->fetch_assoc());
$res->free();
sleep(2);
/* Cache miss - cache entry has expired */
$res = $mysqli->query($sql);
var_dump($res->fetch_assoc());
$res->free();
printf("Script runtime\t: %d seconds\n", microtime(true) - $start);
?>
The above examples will output something similar to:
Default TTL : 30 seconds
array(1) {
["id"]=>
string(1) "1"
}
array(1) {
["id"]=>
string(1) "1"
}
NULL
Script runtime : 3 seconds
Copyright 1997-2014 the PHP Documentation Group.
An application has three options for telling PECL/mysqlnd_qc
whether a particular statement shall be used. The most basic
approach is to cache all statements by setting
mysqlnd_qc.cache_by_default = 1. This approach
is often of little practical value. But it enables users to make
a quick estimation about the maximum performance gains from
caching. An application designed to use a cache may be able to
prefix selected statements with the appropriate SQL hints.
However, altering an applications source code may not always be
possible or desired, for example, to avoid problems with
software updates. Therefore, PECL/mysqlnd_qc allows setting a
callback which decides if a query is to be cached.
The callback is installed with the
mysqlnd_qc_set_is_select
function. The callback is given the statement string of every
statement inspected by the plugin. Then, the callback can decide
whether to cache the function. The callback is supposed to
return FALSE if the statement shall not be
cached. A return value of TRUE makes the
plugin try to add the statement into the cache. The cache entry
will be given the default TTL
(
mysqlnd_qc.ttl). If the callback returns a
numerical value it is used as the TTL instead of the global
default.
Example 8.8 Setting a callback with
mysqlnd_qc_set_is_select
mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_statistics=1
<?php
/* callback which decides if query is cached */
function is_select($query) {
static $patterns = array(
/* true - use default from mysqlnd_qc.ttl */
"@SELECT\s+.*\s+FROM\s+test@ismU" => true,
/* 3 - use TTL = 3 seconds */
"@SELECT\s+.*\s+FROM\s+news@ismU" => 3
);
/* check if query does match pattern */
foreach ($patterns as $pattern => $ttl) {
if (preg_match($pattern, $query)) {
printf("is_select(%45s): cache\n", $query);
return $ttl;
}
}
printf("is_select(%45s): do not cache\n", $query);
return false;
}
/* install callback */
mysqlnd_qc_set_is_select("is_select");
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* cache put */
$mysqli->query("SELECT id FROM test WHERE id = 1");
/* cache hit */
$mysqli->query("SELECT id FROM test WHERE id = 1");
/* cache put */
$mysqli->query("SELECT * FROM test");
$stats = mysqlnd_qc_get_core_stats();
printf("Cache put: %d\n", $stats['cache_put']);
printf("Cache hit: %d\n", $stats['cache_hit']);
?>
The above examples will output something similar to:
is_select( DROP TABLE IF EXISTS test): do not cache is_select( CREATE TABLE test(id INT)): do not cache is_select( INSERT INTO test(id) VALUES (1), (2), (3)): do not cache is_select( SELECT id FROM test WHERE id = 1): cache is_select( SELECT id FROM test WHERE id = 1): cache is_select( SELECT * FROM test): cache Cache put: 2 Cache hit: 1
The examples callback tests if a statement string matches a
pattern. If this is the case, it either returns
TRUE to cache the statement using the
global default TTL or an alternative TTL.
To minimize application changes the callback can put into and registered in an auto prepend file.
Copyright 1997-2014 the PHP Documentation Group.
A badly designed cache can do more harm than good. In the worst case a cache can increase database server load instead of minimizing it. An overload situation can occur if a highly shared cache entry expires (cache stampeding).
Cache entries are shared and reused to a different degree depending on the storage used. The default storage handler stores cache entries in process memory. Thus, a cache entry can be reused for the life-span of a process. Other PHP processes cannot access it. If Memcache is used, a cache entry can be shared among multiple PHP processes and even among multiple machines, depending on the set up being used.
If a highly shared cache entry stored, for example, in Memcache expires, many clients gets a cache miss. Many client requests can no longer be served from the cache but try to run the underlying query on the database server. Until the cache entry is refreshed, more and more clients contact the database server. In the worst case, a total lost of service is the result.
The overload can be avoided using a storage handler which limits the reuse of cache entries to few clients. Then, at the average, its likely that only a limited number of clients will try to refresh a cache entry concurrently.
Additionally, the built-in slam defense mechanism can and should
be used. If slam defense is activated an expired cache entry is
given an extended life time. The first client getting a cache
miss for the expired cache entry tries to refresh the cache
entry within the extended life time. All other clients
requesting the cache entry are temporarily served from the cache
although the original TTL of the cache entry
has expired. The other clients will not experience a cache miss
before the extended life time is over.
Example 8.9 Enabling the slam defense mechanism
mysqlnd_qc.slam_defense=1 mysqlnd_qc.slam_defense_ttl=1
The slam defense mechanism is enabled with the PHP configuration
directive
mysqlnd_qc.slam_defense.
The extended life time of a cache entry is set with
mysqlnd_qc.slam_defense_ttl.
The function
mysqlnd_qc_get_core_stats
returns an array of statistics. The statistics
slam_stale_refresh and
slam_stale_hit are incremented if slam
defense takes place.
It is not possible to give a one-fits-all recommendation on the slam defense configuration. Users are advised to monitor and test their setup and derive settings accordingly.
Copyright 1997-2014 the PHP Documentation Group.
A statement should be considered for caching if it is executed
often and has a long run time. Cache candidates are found by
creating a list of statements sorted by the product of the
number of executions multiplied by the statements run time. The
function
mysqlnd_qc_get_query_trace_log
returns a query log which help with the task.
Collecting a query trace is a slow operation. Thus, it is
disabled by default. The PHP configuration directive
mysqlnd_qc.collect_query_trace
is used to enable it. The functions trace contains one entry for
every query issued before the function is called.
Example 8.10 Collecting a query trace
mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_query_trace=1
<?php
/* connect to MySQL */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
/* dummy queries to fill the query trace */
for ($i = 0; $i < 2; $i++) {
$res = $mysqli->query("SELECT 1 AS _one FROM DUAL");
$res->free();
}
/* dump trace */
var_dump(mysqlnd_qc_get_query_trace_log());
?>
The above examples will output:
array(2) {
[0]=>
array(8) {
["query"]=>
string(26) "SELECT 1 AS _one FROM DUAL"
["origin"]=>
string(102) "#0 qc.php(7): mysqli->query('SELECT 1 AS _on...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(25)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
[1]=>
array(8) {
["query"]=>
string(26) "SELECT 1 AS _one FROM DUAL"
["origin"]=>
string(102) "#0 qc.php(7): mysqli->query('SELECT 1 AS _on...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(8)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
}
Assorted information is given in the trace. Among them timings
and the origin of the query call. The origin property holds a
code backtrace to identify the source of the query. The depth of
the backtrace can be limited with the PHP configuration
directive
mysqlnd_qc.query_trace_bt_depth.
The default depth is 3.
Example 8.11 Setting the backtrace depth with the
mysqlnd_qc.query_trace_bt_depth ini
setting
mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_query_trace=1
<?php
/* connect to MySQL */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* dummy queries to fill the query trace */
for ($i = 0; $i < 3; $i++) {
$res = $mysqli->query("SELECT id FROM test WHERE id = " . $mysqli->real_escape_string($i));
$res->free();
}
$trace = mysqlnd_qc_get_query_trace_log();
$summary = array();
foreach ($trace as $entry) {
if (!isset($summary[$entry['query']])) {
$summary[$entry['query']] = array(
"executions" => 1,
"time" => $entry['run_time'] + $entry['store_time'],
);
} else {
$summary[$entry['query']]['executions']++;
$summary[$entry['query']]['time'] += $entry['run_time'] + $entry['store_time'];
}
}
foreach ($summary as $query => $details) {
printf("%45s: %5dms (%dx)\n",
$query, $details['time'], $details['executions']);
}
?>
The above examples will output something similar to:
DROP TABLE IF EXISTS test: 0ms (1x)
CREATE TABLE test(id INT): 0ms (1x)
INSERT INTO test(id) VALUES (1), (2), (3): 0ms (1x)
SELECT id FROM test WHERE id = 0: 25ms (1x)
SELECT id FROM test WHERE id = 1: 10ms (1x)
SELECT id FROM test WHERE id = 2: 9ms (1x)
Copyright 1997-2014 the PHP Documentation Group.
PECL/mysqlnd_qc offers three ways to measure the cache
efficiency. The function
mysqlnd_qc_get_normalized_query_trace_log
returns statistics aggregated by the normalized query string,
mysqlnd_qc_get_cache_info
gives storage handler specific information which includes a list
of all cached items, depending on the storage handler.
Additionally, the core of PECL/mysqlnd_qc collects high-level
summary statistics aggregated per PHP process. The high-level
statistics are returned by
mysqlnd_qc_get_core_stats.
The functions
mysqlnd_qc_get_normalized_query_trace_log
and
mysqlnd_qc_get_core_stats
will not collect data unless data collection has been enabled
through their corresponding PHP configuration directives. Data
collection is disabled by default for performance
considerations. It is configurable with the
mysqlnd_qc.time_statistics
option, which determines if timing information should be
collected. Collection of time statistics is enabled by default
but only performed if data collection as such has been enabled.
Recording time statistics causes extra system calls. In most
cases, the benefit of the monitoring outweighs any potential
performance penalty of the additional system calls.
Example 8.12 Collecting statistics data with the
mysqlnd_qc.time_statistics ini setting
mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_statistics=1
<?php
/* connect to MySQL */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* dummy queries */
for ($i = 1; $i <= 4; $i++) {
$query = sprintf("/*%s*/SELECT id FROM test WHERE id = %d", MYSQLND_QC_ENABLE_SWITCH, $i % 2);
$res = $mysqli->query($query);
$res->free();
}
var_dump(mysqlnd_qc_get_core_stats());
?>
The above examples will output something similar to:
array(26) {
["cache_hit"]=>
string(1) "2"
["cache_miss"]=>
string(1) "2"
["cache_put"]=>
string(1) "2"
["query_should_cache"]=>
string(1) "4"
["query_should_not_cache"]=>
string(1) "3"
["query_not_cached"]=>
string(1) "3"
["query_could_cache"]=>
string(1) "4"
["query_found_in_cache"]=>
string(1) "2"
["query_uncached_other"]=>
string(1) "0"
["query_uncached_no_table"]=>
string(1) "0"
["query_uncached_no_result"]=>
string(1) "0"
["query_uncached_use_result"]=>
string(1) "0"
["query_aggr_run_time_cache_hit"]=>
string(2) "28"
["query_aggr_run_time_cache_put"]=>
string(3) "900"
["query_aggr_run_time_total"]=>
string(3) "928"
["query_aggr_store_time_cache_hit"]=>
string(2) "14"
["query_aggr_store_time_cache_put"]=>
string(2) "40"
["query_aggr_store_time_total"]=>
string(2) "54"
["receive_bytes_recorded"]=>
string(3) "136"
["receive_bytes_replayed"]=>
string(3) "136"
["send_bytes_recorded"]=>
string(2) "84"
["send_bytes_replayed"]=>
string(2) "84"
["slam_stale_refresh"]=>
string(1) "0"
["slam_stale_hit"]=>
string(1) "0"
["request_counter"]=>
int(1)
["process_hash"]=>
int(1929695233)
}
For a quick overview, call
mysqlnd_qc_get_core_stats.
It delivers cache usage, cache timing and traffic related
statistics. Values are aggregated on a per process basis for all
queries issued by any PHP MySQL API call.
Some storage handler, such as the default handler, can report
cache entries, statistics related to the entries and meta data
for the underlying query through the
mysqlnd_qc_get_cache_info
function. Please note, that the information returned depends on
the storage handler. Values are aggregated on a per process
basis.
Example 8.13 Example
mysqlnd_qc_get_cache_info
usage
mysqlnd_qc.enable_qc=1
<?php
/* connect to MySQL */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* dummy queries to fill the query trace */
for ($i = 1; $i <= 4; $i++) {
$query = sprintf("/*%s*/SELECT id FROM test WHERE id = %d", MYSQLND_QC_ENABLE_SWITCH, $i % 2);
$res = $mysqli->query($query);
$res->free();
}
var_dump(mysqlnd_qc_get_cache_info());
?>
The above examples will output something similar to:
array(4) {
["num_entries"]=>
int(2)
["handler"]=>
string(7) "default"
["handler_version"]=>
string(5) "1.0.0"
["data"]=>
array(2) {
["Localhost via UNIX socket
3306
root
test|/*qc=on*/SELECT id FROM test WHERE id = 1"]=>
array(2) {
["statistics"]=>
array(11) {
["rows"]=>
int(1)
["stored_size"]=>
int(71)
["cache_hits"]=>
int(1)
["run_time"]=>
int(391)
["store_time"]=>
int(27)
["min_run_time"]=>
int(16)
["max_run_time"]=>
int(16)
["min_store_time"]=>
int(8)
["max_store_time"]=>
int(8)
["avg_run_time"]=>
int(8)
["avg_store_time"]=>
int(4)
}
["metadata"]=>
array(1) {
[0]=>
array(8) {
["name"]=>
string(2) "id"
["orig_name"]=>
string(2) "id"
["table"]=>
string(4) "test"
["orig_table"]=>
string(4) "test"
["db"]=>
string(4) "test"
["max_length"]=>
int(1)
["length"]=>
int(11)
["type"]=>
int(3)
}
}
}
["Localhost via UNIX socket
3306
root
test|/*qc=on*/SELECT id FROM test WHERE id = 0"]=>
array(2) {
["statistics"]=>
array(11) {
["rows"]=>
int(0)
["stored_size"]=>
int(65)
["cache_hits"]=>
int(1)
["run_time"]=>
int(299)
["store_time"]=>
int(13)
["min_run_time"]=>
int(11)
["max_run_time"]=>
int(11)
["min_store_time"]=>
int(6)
["max_store_time"]=>
int(6)
["avg_run_time"]=>
int(5)
["avg_store_time"]=>
int(3)
}
["metadata"]=>
array(1) {
[0]=>
array(8) {
["name"]=>
string(2) "id"
["orig_name"]=>
string(2) "id"
["table"]=>
string(4) "test"
["orig_table"]=>
string(4) "test"
["db"]=>
string(4) "test"
["max_length"]=>
int(0)
["length"]=>
int(11)
["type"]=>
int(3)
}
}
}
}
}
It is possible to further break down the granularity of
statistics to the level of the normalized statement string. The
normalized statement string is the statements string with all
parameters replaced with question marks. For example, the two
statements SELECT id FROM test WHERE id = 0
and SELECT id FROM test WHERE id = 1 are
normalized into SELECT id FROM test WHERE id =
?. Their both statistics are aggregated into one entry
for SELECT id FROM test WHERE id = ?.
Example 8.14 Example
mysqlnd_qc_get_normalized_query_trace_log
usage
mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_normalized_query_trace=1
<?php
/* connect to MySQL */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* dummy queries to fill the query trace */
for ($i = 1; $i <= 4; $i++) {
$query = sprintf("/*%s*/SELECT id FROM test WHERE id = %d", MYSQLND_QC_ENABLE_SWITCH, $i % 2);
$res = $mysqli->query($query);
$res->free();
}
var_dump(mysqlnd_qc_get_normalized_query_trace_log());
?>
The above examples will output something similar to:
array(4) {
[0]=>
array(9) {
["query"]=>
string(25) "DROP TABLE IF EXISTS test"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[1]=>
array(9) {
["query"]=>
string(27) "CREATE TABLE test (id INT )"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[2]=>
array(9) {
["query"]=>
string(46) "INSERT INTO test (id ) VALUES (? ), (? ), (? )"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[3]=>
array(9) {
["query"]=>
string(31) "SELECT id FROM test WHERE id =?"
["occurences"]=>
int(4)
["eligible_for_caching"]=>
bool(true)
["avg_run_time"]=>
int(179)
["min_run_time"]=>
int(11)
["max_run_time"]=>
int(393)
["avg_store_time"]=>
int(12)
["min_store_time"]=>
int(7)
["max_store_time"]=>
int(25)
}
}
The source distribution of PECL/mysqlnd_qc contains a directory
web/ in which web based monitoring scripts
can be found which give an example how to write a cache monitor.
Please, follow the instructions given in the source.
Since PECL/mysqlnd_qc 1.1.0 it is possible to write statistics
into a log file. Please, see
mysqlnd_qc.collect_statistics_log_file.
Copyright 1997-2014 the PHP Documentation Group.
The query cache plugin supports the use of user-defined storage handler. User-defined storage handler can use arbitrarily complex invalidation algorithms and support arbitrary storage media.
All user-defined storage handlers have to provide a certain interface. The functions of the user-defined storage handler will be called by the core of the cache plugin. The necessary interface consists of seven public functions. Both procedural and object oriented user-defined storage handler must implement the same set of functions.
Example 8.15 Using a user-defined storage handler
<?php
/* Enable default caching of all statements */
ini_set("mysqlnd_qc.cache_by_default", 1);
/* Procedural user defined storage handler functions */
$__cache = array();
function get_hash($host_info, $port, $user, $db, $query) {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
return md5(sprintf("%s%s%s%s%s", $host_info, $port, $user, $db, $query));
}
function find_query_in_cache($key) {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
if (isset($__cache[$key])) {
$tmp = $__cache[$key];
if ($tmp["valid_until"] < time()) {
unset($__cache[$key]);
$ret = NULL;
} else {
$ret = $__cache[$key]["data"];
}
} else {
$ret = NULL;
}
return $ret;
}
function return_to_cache($key) {
/*
Called on cache hit after cached data has been processed,
may be used for reference counting
*/
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
}
function add_query_to_cache_if_not_exists($key, $data, $ttl, $run_time, $store_time, $row_count) {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
$__cache[$key] = array(
"data" => $data,
"row_count" => $row_count,
"valid_until" => time() + $ttl,
"hits" => 0,
"run_time" => $run_time,
"store_time" => $store_time,
"cached_run_times" => array(),
"cached_store_times" => array(),
);
return TRUE;
}
function query_is_select($query) {
printf("\t%s('%s'): ", __FUNCTION__, $query);
$ret = FALSE;
if (stristr($query, "SELECT") !== FALSE) {
/* cache for 5 seconds */
$ret = 5;
}
printf("%s\n", (FALSE === $ret) ? "FALSE" : $ret);
return $ret;
}
function update_query_run_time_stats($key, $run_time, $store_time) {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
if (isset($__cache[$key])) {
$__cache[$key]['hits']++;
$__cache[$key]["cached_run_times"][] = $run_time;
$__cache[$key]["cached_store_times"][] = $store_time;
}
}
function get_stats($key = NULL) {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
if ($key && isset($__cache[$key])) {
$stats = $__cache[$key];
} else {
$stats = array();
foreach ($__cache as $key => $details) {
$stats[$key] = array(
'hits' => $details['hits'],
'bytes' => strlen($details['data']),
'uncached_run_time' => $details['run_time'],
'cached_run_time' => (count($details['cached_run_times']))
? array_sum($details['cached_run_times']) / count($details['cached_run_times'])
: 0,
);
}
}
return $stats;
}
function clear_cache() {
global $__cache;
printf("\t%s(%d)\n", __FUNCTION__, func_num_args());
$__cache = array();
return TRUE;
}
/* Install procedural user-defined storage handler */
if (!mysqlnd_qc_set_user_handlers("get_hash", "find_query_in_cache",
"return_to_cache", "add_query_to_cache_if_not_exists",
"query_is_select", "update_query_run_time_stats", "get_stats", "clear_cache")) {
printf("Failed to install user-defined storage handler\n");
}
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
printf("\nCache put/cache miss\n");
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
/* Delete record to verify we get our data from the cache */
$mysqli->query("DELETE FROM test WHERE id = 1");
printf("\nCache hit\n");
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
printf("\nDisplay cache statistics\n");
var_dump(mysqlnd_qc_get_cache_info());
printf("\nFlushing cache, cache put/cache miss");
var_dump(mysqlnd_qc_clear_cache());
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
?>
The above examples will output something similar to:
query_is_select('DROP TABLE IF EXISTS test'): FALSE
query_is_select('CREATE TABLE test(id INT)'): FALSE
query_is_select('INSERT INTO test(id) VALUES (1), (2)'): FALSE
Cache put/cache miss
query_is_select('SELECT id FROM test WHERE id = 1'): 5
get_hash(5)
find_query_in_cache(1)
add_query_to_cache_if_not_exists(6)
array(1) {
["id"]=>
string(1) "1"
}
query_is_select('DELETE FROM test WHERE id = 1'): FALSE
Cache hit
query_is_select('SELECT id FROM test WHERE id = 1'): 5
get_hash(5)
find_query_in_cache(1)
return_to_cache(1)
update_query_run_time_stats(3)
array(1) {
["id"]=>
string(1) "1"
}
Display cache statistics
get_stats(0)
array(4) {
["num_entries"]=>
int(1)
["handler"]=>
string(4) "user"
["handler_version"]=>
string(5) "1.0.0"
["data"]=>
array(1) {
["18683c177dc89bb352b29965d112fdaa"]=>
array(4) {
["hits"]=>
int(1)
["bytes"]=>
int(71)
["uncached_run_time"]=>
int(398)
["cached_run_time"]=>
int(4)
}
}
}
Flushing cache, cache put/cache miss clear_cache(0)
bool(true)
query_is_select('SELECT id FROM test WHERE id = 1'): 5
get_hash(5)
find_query_in_cache(1)
add_query_to_cache_if_not_exists(6)
NULL
Copyright 1997-2014 the PHP Documentation Group.
Copyright 1997-2014 the PHP Documentation Group.
PHP 5.3.3 or a newer version of
PHP.
PECL/mysqlnd_qc is a mysqlnd plugin. It plugs into the mysqlnd library. To use you this plugin with a PHP MySQL extension, the extension (mysqli, mysql, or PDO_MYSQL) must enable the mysqlnd library.
For using the
APC storage
handler with PECL/mysqlnd_qc 1.0 APC
3.1.3p1-beta or newer. PECL/mysqlnd_qc 1.2 has been
tested with APC 3.1.13-beta. The APC storage
handler cannot be used with a shared build. You cannot use the
PHP configuration directive extension to load
the APC and PECL/mysqlnd_qc extensions if PECL/mysqlnd_qc will
use APC as a storage handler. For using the APC storage handler,
you have to statically compile PHP with APC and PECL/mysqlnd_qc
support into PHP.
For using MEMCACHE storage handler: Use
libmemcache 0.38 or newer. PECL/mysqlnd_qc
1.2 has been tested with libmemcache 1.4.0.
For using sqlite storage handler: Use the
sqlite3
extension that bundled with PHP.
Copyright 1997-2014 the PHP Documentation Group.
This PECL extension is not bundled with PHP.
Information for installing this PECL extension may be found in the manual chapter titled Installation of PECL extensions. Additional information such as new releases, downloads, source files, maintainer information, and a CHANGELOG, can be located here: http://pecl.php.net/package/mysqlnd_qc
A DLL for this PECL extension is currently unavailable. See also the building on Windows section.
Copyright 1997-2014 the PHP Documentation Group.
The behaviour of these functions is affected by settings in php.ini.
Table 8.1 mysqlnd_qc Configure Options
| Name | Default | Changeable | Changelog |
|---|---|---|---|
| mysqlnd_qc.enable_qc | 1 | PHP_INI_SYSTEM | |
| mysqlnd_qc.ttl | 30 | PHP_INI_ALL | |
| mysqlnd_qc.cache_by_default | 0 | PHP_INI_ALL | |
| mysqlnd_qc.cache_no_table | 0 | PHP_INI_ALL | |
| mysqlnd_qc.use_request_time | 0 | PHP_INI_ALL | |
| mysqlnd_qc.time_statistics | 1 | PHP_INI_ALL | |
| mysqlnd_qc.collect_statistics | 0 | PHP_INI_ALL | |
| mysqlnd_qc.collect_statistics_log_file | /tmp/mysqlnd_qc.stats | PHP_INI_SYSTEM | |
| mysqlnd_qc.collect_query_trace | 0 | PHP_INI_SYSTEM | |
| mysqlnd_qc.query_trace_bt_depth | 3 | PHP_INI_SYSTEM | |
| mysqlnd_qc.collect_normalized_query_trace | 0 | PHP_INI_SYSTEM | |
| mysqlnd_qc.ignore_sql_comments | 1 | PHP_INI_ALL | |
| mysqlnd_qc.slam_defense | 0 | PHP_INI_SYSTEM | |
| mysqlnd_qc.slam_defense_ttl | 30 | PHP_INI_SYSTEM | |
| mysqlnd_qc.std_data_copy | 0 | PHP_INI_SYSTEM | |
| mysqlnd_qc.apc_prefix | qc_ | PHP_INI_ALL | |
| mysqlnd_qc.memc_server | 127.0.0.1 | PHP_INI_ALL | |
| mysqlnd_qc.memc_port | 11211 | PHP_INI_ALL | |
| mysqlnd_qc.sqlite_data_file | :memory: | PHP_INI_ALL |
Here's a short explanation of the configuration directives.
mysqlnd_qc.enable_qc
integer
Enables or disables the plugin. If disabled the extension will not plug into mysqlnd to proxy internal mysqlnd C API calls.
mysqlnd_qc.ttl integer
Default Time-to-Live (TTL) for cache entries in seconds.
mysqlnd_qc.cache_by_default
integer
Cache all queries regardless if they begin with the SQL hint that enables caching of a query or not. Storage handler cannot overrule the setting. It is evaluated by the core of the plugin.
mysqlnd_qc.cache_no_table
integer
Whether to cache queries with no table name in any of
columns meta data of their result set, for example,
SELECT SLEEP(1), SELECT
NOW(), SELECT SUBSTRING().
mysqlnd_qc.use_request_time
integer
Use PHP global request time to avoid
gettimeofday() system calls? If using
APC
storage handler it should be set to the value
of
apc.use_request_time
, if not warnings will be generated.
mysqlnd_qc.time_statistics
integer
Collect run time and store time statistics using
gettimeofday() system call? Data will
be collected only if you also set
mysqlnd_qc.collect_statistics = 1,
mysqlnd_qc.collect_statistics
integer
Collect statistics for
mysqlnd_qc_get_core_stats?
Does not influence storage handler statistics! Handler
statistics can be an integral part of the handler
internal storage format. Therefore, collection of some
handler statistics cannot be disabled.
mysqlnd_qc.collect_statistics-log-file
integer
If mysqlnd_qc.collect_statistics and
mysqlnd_qc.collect_statistics_log_file
are set, the plugin will dump statistics into the
specified log file at every 10th web request during PHP
request shutdown. The log file needs to be writable by
the web server user.
Since 1.1.0.
mysqlnd_qc.collect_query_trace
integer
Collect query back traces?
mysqlnd_qc.query_trace_bt_depth
integer
Maximum depth/level of a query code backtrace.
mysqlnd_qc.ignore_sql_comments
integer
Whether to remove SQL comments from a query string
before hashing it to generate a cache key. Disable if
you do not want two statemts such as SELECT
/*my_source_ip=123*/ id FROM test and
SELECT /*my_source_ip=456*/ id FROM
test to refer to the same cache entry.
Since 1.1.0.
mysqlnd_qc.slam_defense
integer
Activates handler based slam defense (cache stampeding
protection) if available. Supported by
Default and
APC
storage handler
mysqlnd_qc.slam_defense_ttl
integer
TTL for stale cache entries which are
served while another client updates the entries.
Supported by
APC
storage handler.
mysqlnd_qc.collect_normalized_query_trace
integer
Collect aggregated normalized query traces? The setting
has no effect by default. You compile the extension
using the define NORM_QUERY_TRACE_LOG
to make use of the setting.
mysqlnd_qc.std_data_copy
integer
Default storage handler: copy cached wire data? EXPERIMENTAL – use default setting!
mysqlnd_qc.apc_prefix
string
The
APC
storage handler stores data in the
APC user cache. The setting sets a
prefix to be used for cache entries.
mysqlnd_qc.memc_server
string
MEMCACHE storage handler: memcache
server host.
mysqlnd_qc.memc_port
integer
MEMCACHE storage handler: memcached
server port.
mysqlnd_qc.sqlite_data_file
string
sqlite storage handler: data file.
Any setting but :memory: may be of
little practical value.
Copyright 1997-2014 the PHP Documentation Group.
The constants below are defined by this extension, and will only be available when the extension has either been compiled into PHP or dynamically loaded at runtime.
SQL hint related
Example 8.16 Using SQL hint constants
The query cache is controlled by SQL hints. SQL hints are used
to enable and disable caching. SQL hints can be used to set
the TTL of a query.
The SQL hints recognized by the query cache can be manually
changed at compile time. This makes it possible to use
mysqlnd_qc in environments in which the
default SQL hints are already taken and interpreted by other
systems. Therefore it is recommended to use the SQL hint
string constants instead of manually adding the default SQL
hints to the query string.
<?php
/* Use constants for maximum portability */
$query = "/*" . MYSQLND_QC_ENABLE_SWITCH . "*/SELECT id FROM test";
/* Valid but less portable: default TTL */
$query = "/*qc=on*/SELECT id FROM test";
/* Valid but less portable: per statement TTL */
$query = "/*qc=on*//*qc_ttl=5*/SELECT id FROM test";
printf("MYSQLND_QC_ENABLE_SWITCH: %s\n", MYSQLND_QC_ENABLE_SWITCH);
printf("MYSQLND_QC_DISABLE_SWITCH: %s\n", MYSQLND_QC_DISABLE_SWITCH);
printf("MYSQLND_QC_TTL_SWITCH: %s\n", MYSQLND_QC_TTL_SWITCH);
?>
The above examples will output:
MYSQLND_QC_ENABLE_SWITCH: qc=on MYSQLND_QC_DISABLE_SWITCH: qc=off MYSQLND_QC_TTL_SWITCH: qc_ttl=
MYSQLND_QC_ENABLE_SWITCH
(string)
MYSQLND_QC_DISABLE_SWITCH
(string)
mysqlnd_qc.cache_by_default = 1.
MYSQLND_QC_TTL_SWITCH
(string)
MYSQLND_QC_SERVER_ID_SWITCH
(string)
This SQL hint should not be used in general.
It is needed by PECL/mysqlnd_ms to group cache entries for one statement but originating from different physical connections. If the hint is used connection settings such as user, hostname and charset are not considered for generating a cache key of a query. Instead the given value and the query string are used as input to the hashing function that generates the key.
PECL/mysqlnd_ms may, if instructed, cache results from MySQL Replication slaves. Because it can hold many connections to the slave the cache key shall not be formed from the user, hostname or other settings that may vary for the various slave connections. Instead, PECL/mysqlnd_ms provides an identifier which refers to the group of slave connections that shall be enabled to share cache entries no matter which physical slave connection was to generate the cache entry.
Use of this feature outside of PECL/mysqlnd_ms is not recommended.
mysqlnd_qc_set_cache_condition
related
Example 8.17 Example
mysqlnd_qc_set_cache_condition
usage
The function
mysqlnd_qc_set_cache_condition
allows setting conditions for automatic caching of statements
which don't begin with the SQL hints necessary to
manually enable caching.
<?php
/* Cache all accesses to tables with the name "new%" in schema/database "db_example" for 1 second */
if (!mysqlnd_qc_set_cache_condition(MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN, "db_example.new%", 1)) {
die("Failed to set cache condition!");
}
$mysqli = new mysqli("host", "user", "password", "db_example", "port");
/* cached although no SQL hint given */
$mysqli->query("SELECT id, title FROM news");
$pdo_mysql = new PDO("mysql:host=host;dbname=db_example;port=port", "user", "password");
/* not cached: no SQL hint, no pattern match */
$pdo_mysql->query("SELECT id, title FROM latest_news");
/* cached: TTL 1 second, pattern match */
$pdo_mysql->query("SELECT id, title FROM news");
?>
MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN
(int)
mysqlnd_qc_set_cache_condition
to set conditions for schema based automatic caching.
Other
The plugin version number can be obtained using either
MYSQLND_QC_VERSION, which is the string
representation of the numerical version number, or
MYSQLND_QC_VERSION_ID, which is an integer
such as 10000. Developers can calculate the version number as
follows.
| Version (part) | Example |
|---|---|
| Major*10000 | 1*10000 = 10000 |
| Minor*100 | 0*100 = 0 |
| Patch | 0 = 0 |
| MYSQLND_QC_VERSION_ID | 10000 |
mysqlnd_qc_clear_cachemysqlnd_qc_get_available_handlersmysqlnd_qc_get_cache_infomysqlnd_qc_get_core_statsmysqlnd_qc_get_normalized_query_trace_logmysqlnd_qc_get_query_trace_logmysqlnd_qc_set_cache_conditionmysqlnd_qc_set_is_selectmysqlnd_qc_set_storage_handlermysqlnd_qc_set_user_handlersCopyright 1997-2014 the PHP Documentation Group.
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_clear_cache
Flush all cache contents
Description
bool mysqlnd_qc_clear_cache();Flush all cache contents.
Flushing the cache is a storage handler responsibility. All
built-in storage handler but the memcache
storage handler support flushing the cache. The
memcache storage handler cannot flush its
cache contents.
User-defined storage handler may or may not support the operation.
Parameters
This function has no parameters.
Return Values
Returns TRUE on success or
FALSE on failure.
A return value of FALSE indicates that
flushing all cache contents has failed or the operation is not
supported by the active storage handler. Applications must not
expect that calling the function will always flush the cache.
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_get_available_handlers
Returns a list of available storage handler
Description
array mysqlnd_qc_get_available_handlers();
Which storage are available depends on the compile time
configuration of the query cache plugin. The
default storage handler is always available.
All other storage handler must be enabled explicitly when
building the extension.
Parameters
This function has no parameters.
Return Values
Returns an array of available built-in storage handler. For each storage handler the version number and version string is given.
Examples
Example 8.18 mysqlnd_qc_get_available_handlers
example
<?php var_dump(mysqlnd_qc_get_available_handlers()); ?>
The above examples will output:
array(5) {
["default"]=>
array(2) {
["version"]=>
string(5) "1.0.0"
["version_number"]=>
int(100000)
}
["user"]=>
array(2) {
["version"]=>
string(5) "1.0.0"
["version_number"]=>
int(100000)
}
["APC"]=>
array(2) {
["version"]=>
string(5) "1.0.0"
["version_number"]=>
int(100000)
}
["MEMCACHE"]=>
array(2) {
["version"]=>
string(5) "1.0.0"
["version_number"]=>
int(100000)
}
["sqlite"]=>
array(2) {
["version"]=>
string(5) "1.0.0"
["version_number"]=>
int(100000)
}
}
See Also
| Installation |
mysqlnd_qc_set_storage_handler
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_get_cache_info
Returns information on the current handler, the number of cache entries and cache entries, if available
Description
array mysqlnd_qc_get_cache_info();Parameters
This function has no parameters.
Return Values
Returns information on the current handler, the number of cache entries and cache entries, if available. If and what data will be returned for the cache entries is subject to the active storage handler. Storage handler are free to return any data. Storage handler are recommended to return at least the data provided by the default handler, if technically possible.
The scope of the information is the PHP process. Depending on the PHP deployment model a process may serve one or more web requests.
Values are aggregated for all cache activities on a per storage
handler basis. It is not possible to tell how much queries
originating from mysqli,
PDO_MySQL or mysql.API
calls have contributed to the aggregated data values. Use
mysqlnd_qc_get_core_stats
to get timing data aggregated for all storage handlers.
Array of cache information
handler string
The active storage handler.
All storage handler. Since 1.0.0.
handler_version string
The version of the active storage handler.
All storage handler. Since 1.0.0.
num_entries int
The number of cache entries. The value depends on the storage handler in use.
The default, APC and SQLite storage handler provide the actual number of cache entries.
The MEMCACHE storage handler always returns
0. MEMCACHE does not support counting
the number of cache entries.
If a user defined handler is used, the number of entries
of the data property is reported.
Since 1.0.0.
data array
The version of the active storage handler.
Additional storage handler dependent data on the cache entries. Storage handler are requested to provide similar and comparable information. A user defined storage handler is free to return any data.
Since 1.0.0.
The following information is provided by the default
storage handler for the data
property.
The data property holds a hash. The
hash is indexed by the internal cache entry identifier
of the storage handler. The cache entry identifier is
human-readable and contains the query string leading to
the cache entry. Please, see also the example below. The
following data is given for every cache entry.
statistics
array
Statistics of the cache entry.
Since 1.0.0.
| Property | Description | Version |
|---|---|---|
rows | Number of rows of the cached result set. | Since 1.0.0. |
stored_size | The size of the cached result set in bytes. This is the size of the payload. The value is not suited for calculating the total memory consumption of all cache entries including the administrative overhead of the cache entries. | Since 1.0.0. |
cache_hits | How often the cached entry has been returned. | Since 1.0.0. |
run_time | Run time of the statement to which the cache entry belongs. This is the
run time of the uncached statement. It
is the time between sending the
statement to MySQL receiving a reply
from MySQL. Run time saved by using the
query cache plugin can be calculated
like this: cache_hits *
((run_time - avg_run_time) + (store_time
- avg_store_time)). | Since 1.0.0. |
store_time | Store time of the statements result set to which the cache entry belongs. This is the time it took to fetch and store the results of the uncached statement. | Since 1.0.0. |
min_run_time | Minimum run time of the cached statement. How long it took to find the statement in the cache. | Since 1.0.0. |
min_store_time | Minimum store time of the cached statement. The time taken for fetching the cached result set from the storage medium and decoding | Since 1.0.0. |
avg_run_time | Average run time of the cached statement. | Since 1.0.0. |
avg_store_time | Average store time of the cached statement. | Since 1.0.0. |
max_run_time | Average run time of the cached statement. | Since 1.0.0. |
max_store_time | Average store time of the cached statement. | Since 1.0.0. |
valid_until | Timestamp when the cache entry expires. | Since 1.1.0. |
metadata array
Metadata of the cache entry. This is the metadata provided by MySQL together with the result set of the statement in question. Different versions of the MySQL server may return different metadata. Unlike with some of the PHP MySQL extensions no attempt is made to hide MySQL server version dependencies and version details from the caller. Please, refer to the MySQL C API documentation that belongs to the MySQL server in use for further details.
The metadata list contains one entry for every column.
Since 1.0.0.
| Property | Description | Version |
|---|---|---|
name | The field name. Depending on the MySQL version this may be the fields alias name. | Since 1.0.0. |
org_name | The field name. | Since 1.0.0. |
table | The table name. If an alias name was used for the table, this usually holds the alias name. | Since 1.0.0. |
org_table | The table name. | Since 1.0.0. |
db | The database/schema name. | Since 1.0.0. |
max_length | The maximum width of the field. Details may vary by MySQL server version. | Since 1.0.0. |
length | The width of the field. Details may vary by MySQL server version. | Since 1.0.0. |
type | The data type of the field. Details may vary by the MySQL server in use.
This is the MySQL C API type constants
value. It is recommended to use type
constants provided by the
mysqli extension to
test for its meaning. You should not
test for certain type values by
comparing with certain numbers. | Since 1.0.0. |
The APC storage handler returns the same information for
the data property but no
metadata. The
metadata of a cache entry is set to
NULL.
The MEMCACHE storage handler does not fill the
data property. Statistics are not
available on a per cache entry basis with the MEMCACHE
storage handler.
A user defined storage handler is free to provide any data.
Examples
Example 8.19 mysqlnd_qc_get_cache_info
example
The example shows the output from the built-in default storage handler. Other storage handler may report different data.
<?php
/* Populate the cache, e.g. using mysqli */
$mysqli = new mysqli("host", "user", "password", "schema");
$mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/SELECT id FROM test");
/* Display cache information */
var_dump(mysqlnd_qc_get_cache_info());
?>
The above examples will output:
array(4) {
["num_entries"]=>
int(1)
["handler"]=>
string(7) "default"
["handler_version"]=>
string(5) "1.0.0"
["data"]=>
array(1) {
["Localhost via UNIX socket 3306 user schema|/*qc=on*/SELECT id FROM test"]=>
array(2) {
["statistics"]=>
array(11) {
["rows"]=>
int(6)
["stored_size"]=>
int(101)
["cache_hits"]=>
int(0)
["run_time"]=>
int(471)
["store_time"]=>
int(27)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
["avg_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
}
["metadata"]=>
array(1) {
[0]=>
array(8) {
["name"]=>
string(2) "id"
["orig_name"]=>
string(2) "id"
["table"]=>
string(4) "test"
["orig_table"]=>
string(4) "test"
["db"]=>
string(4) "schema"
["max_length"]=>
int(1)
["length"]=>
int(11)
["type"]=>
int(3)
}
}
}
}
}
See Also
mysqlnd_qc_get_core_stats
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_get_core_stats
Statistics collected by the core of the query cache
Description
array mysqlnd_qc_get_core_stats();Returns an array of statistics collected by the core of the cache plugin. The same data fields will be reported for any storage handler because the data is collected by the core.
The PHP configuration setting
mysqlnd_qc.collect_statistics controls the
collection of statistics. The collection of statistics is
disabled by default for performance reasons. Disabling the
collection of statistics will also disable the collection of
time related statistics.
The PHP configuration setting
mysqlnd_qc.collect_time_statistics controls
the collection of time related statistics.
The scope of the core statistics is the PHP
process. Depending on your deployment model a
PHP process may handle one or multiple
requests.
Statistics are aggregated for all cache entries and all storage
handler. It is not possible to tell how much queries originating
from mysqli, PDO_MySQL or
mysql API calls have contributed to the
aggregated data values.
Parameters
This function has no parameters.
Return Values
Array of core statistics
| Statistic | Description | Version |
|---|---|---|
cache_hit | Statement is considered cacheable and cached data has been reused. Statement is considered cacheable and a cache miss happened but the statement got cached by someone else while we process it and thus we can fetch the result from the refreshed cache. | Since 1.0.0. |
cache_miss | Statement is considered cacheable...
| Since 1.0.0. |
cache_put | Statement is considered cacheable and has been added to the cache. Take
care when calculating derived statistics. Storage
handler with a storage life time beyond process scope
may report cache_put = 0 together
with cache_hit > 0, if another
process has filled the cache. You may want to use
num_entries from
mysqlnd_qc_get_cache_info
if the handler supports it ( default,
APC). | Since 1.0.0. |
query_should_cache | Statement is considered cacheable based on query string analysis. The
statement may or may not be added to the cache. See also
cache_put. | Since 1.0.0. |
query_should_not_cache | Statement is considered not cacheable based on query string analysis. | Since 1.0.0. |
query_not_cached | Statement is considered not cacheable or it is considered cachable but the storage handler has not returned a hash key for it. | Since 1.0.0. |
query_could_cache | Statement is considered cacheable...
| Since 1.0.0. |
query_found_in_cache | Statement is considered cacheable and we have found it in the cache but we have not replayed the cached data yet and we have not send the result set to the client yet. This is not considered a cache hit because the client might not fetch the result or the cached data may be faulty. | Since 1.0.0. |
query_uncached_other | Statement is considered cacheable and it may or may not be in the cache already but either replaying cached data has failed, no result set is available or some other error has happened. | |
query_uncached_no_table | Statement has not been cached because the result set has at least one
column which has no table name in its meta data. An
example of such a query is SELECT
SLEEP(1). To cache those statements you have
to change default value of the PHP configuration
directive mysqlnd_qc.cache_no_table
and set mysqlnd_qc.cache_no_table =
1. Often, it is not desired to cache such
statements. | Since 1.0.0. |
query_uncached_use_result | Statement would have been cached if a buffered result set had been used.
The situation is also considered as a cache miss and
cache_miss will be incremented as
well. | Since 1.0.0. |
query_aggr_run_time_cache_hit | Aggregated run time (ms) of all cached queries. Cached queries are those
which have incremented cache_hit. | Since 1.0.0. |
query_aggr_run_time_cache_put | Aggregated run time (ms) of all uncached queries that have been put into
the cache. See also cache_put. | Since 1.0.0. |
query_aggr_run_time_total | Aggregated run time (ms) of all uncached and cached queries that have been inspected and executed by the query cache. | Since 1.0.0. |
query_aggr_store_time_cache_hit | Aggregated store time (ms) of all cached queries. Cached queries are
those which have incremented
cache_hit. | Since 1.0.0. |
query_aggr_store_time_cache_put | Aggregated store time ( ms) of all uncached queries
that have been put into the cache. See also
cache_put. | Since 1.0.0. |
query_aggr_store_time_total | Aggregated store time (ms) of all uncached and cached queries that have been inspected and executed by the query cache. | Since 1.0.0. |
receive_bytes_recorded | Recorded incoming network traffic ( bytes) send from
MySQL to PHP. The traffic may or may not have been added
to the cache. The traffic is the total for all queries
regardless if cached or not. | Since 1.0.0. |
receive_bytes_replayed | Network traffic replayed during cache. This is the total amount of incoming traffic saved because of the usage of the query cache plugin. | Since 1.0.0. |
send_bytes_recorded | Recorded outgoing network traffic ( bytes) send from
MySQL to PHP. The traffic may or may not have been added
to the cache. The traffic is the total for all queries
regardless if cached or not. | Since 1.0.0. |
send_bytes_replayed | Network traffic replayed during cache. This is the total amount of outgoing traffic saved because of the usage of the query cache plugin. | Since 1.0.0. |
slam_stale_refresh | Number of cache misses which triggered serving stale data until the client causing the cache miss has refreshed the cache entry. | Since 1.0.0. |
slam_stale_hit | Number of cache hits while a stale cache entry gets refreshed. | Since 1.0.0. |
Examples
Example 8.20 mysqlnd_qc_get_core_stats
example
<?php
/* Enable collection of statistics - default: disabled */
ini_set("mysqlnd_qc.collect_statistics", 1);
/* Enable collection of all timing related statistics -
default: enabled but overruled by mysqlnd_qc.collect_statistics = 0 */
ini_set("mysqlnd_qc.collect_time_statistics", 1);
/* Populate the cache, e.g. using mysqli */
$mysqli = new mysqli('host', 'user', 'password', 'schema');
/* Cache miss and cache put */
$mysqli->query("/*qc=on*/SELECT id FROM test");
/* Cache hit */
$mysqli->query("/*qc=on*/SELECT id FROM test");
/* Display core statistics */
var_dump(mysqlnd_qc_get_core_stats());
?>
The above examples will output:
array(26) {
["cache_hit"]=>
string(1) "1"
["cache_miss"]=>
string(1) "1"
["cache_put"]=>
string(1) "1"
["query_should_cache"]=>
string(1) "2"
["query_should_not_cache"]=>
string(1) "0"
["query_not_cached"]=>
string(1) "0"
["query_could_cache"]=>
string(1) "2"
["query_found_in_cache"]=>
string(1) "1"
["query_uncached_other"]=>
string(1) "0"
["query_uncached_no_table"]=>
string(1) "0"
["query_uncached_no_result"]=>
string(1) "0"
["query_uncached_use_result"]=>
string(1) "0"
["query_aggr_run_time_cache_hit"]=>
string(1) "4"
["query_aggr_run_time_cache_put"]=>
string(3) "395"
["query_aggr_run_time_total"]=>
string(3) "399"
["query_aggr_store_time_cache_hit"]=>
string(1) "2"
["query_aggr_store_time_cache_put"]=>
string(1) "8"
["query_aggr_store_time_total"]=>
string(2) "10"
["receive_bytes_recorded"]=>
string(2) "65"
["receive_bytes_replayed"]=>
string(2) "65"
["send_bytes_recorded"]=>
string(2) "29"
["send_bytes_replayed"]=>
string(2) "29"
["slam_stale_refresh"]=>
string(1) "0"
["slam_stale_hit"]=>
string(1) "0"
["request_counter"]=>
int(1)
["process_hash"]=>
int(3547549858)
}
See Also
| Runtime configuration |
| mysqlnd_qc.collect_statistics |
| mysqlnd_qc.time_statistics |
mysqlnd_qc_get_cache_info
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_get_normalized_query_trace_log
Returns a normalized query trace log for each query inspected by the query cache
Description
array mysqlnd_qc_get_normalized_query_trace_log();
Returns a normalized query trace log for each query inspected by
the query cache. The collection of the trace log is disabled by
default. To collect the trace log you have to set the PHP
configuration directive
mysqlnd_qc.collect_normalized_query_trace to
1
Entries in the trace log are grouped by the normalized query
statement. The normalized query statement is the query statement
with all statement parameter values being replaced with a
question mark. For example, the two statements SELECT
id FROM test WHERE id = 1 and SELECT id FROM
test WHERE id = 2 are normalized as SELECT id
FROM test WHERE id = ?. Whenever a statement is
inspected by the query cache which matches the normalized
statement pattern, its statistics are grouped by the normalized
statement string.
Parameters
This function has no parameters.
Return Values
An array of query log. Every list entry contains the normalized query stringand further detail information.
| Key | Description |
|---|---|
query | Normalized statement string. |
occurences | How many statements have matched the normalized statement string in addition to the one which has created the log entry. The value is zero if a statement has been normalized, its normalized representation has been added to the log but no further queries inspected by PECL/mysqlnd_qc have the same normalized statement string. |
eligible_for_caching | Whether the statement could be cached. An statement eligible for caching has not necessarily been cached. It not possible to tell for sure if or how many cached statement have contributed to the aggregated normalized statement log entry. However, comparing the minimum and average run time one can make an educated guess. |
avg_run_time | The average run time of all queries contributing to the query log entry. The run time is the time between sending the query statement to MySQL and receiving an answer from MySQL. |
avg_store_time | The average store time of all queries contributing to the query log entry. The store time is the time needed to fetch a statements result set from the server to the client and, storing it on the client. |
min_run_time | The minimum run time of all queries contributing to the query log entry. |
min_store_time | The minimum store time of all queries contributing to the query log entry. |
max_run_time | The maximum run time of all queries contributing to the query log entry. |
max_store_time | The maximum store time of all queries contributing to the query log entry. |
Examples
Example 8.21 mysqlnd_qc_get_normalized_query_trace_log
example
mysqlnd_qc.collect_normalized_query_trace=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
/* not cached */
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
/* cache put */
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 2");
var_dump($res->fetch_assoc());
$res->free();
/* cache hit */
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 2");
var_dump($res->fetch_assoc());
$res->free();
var_dump(mysqlnd_qc_get_normalized_query_trace_log());
?>
The above examples will output:
array(1) {
["id"]=>
string(1) "1"
}
array(1) {
["id"]=>
string(1) "2"
}
array(1) {
["id"]=>
string(1) "2"
}
array(4) {
[0]=>
array(9) {
["query"]=>
string(25) "DROP TABLE IF EXISTS test"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[1]=>
array(9) {
["query"]=>
string(27) "CREATE TABLE test (id INT )"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[2]=>
array(9) {
["query"]=>
string(40) "INSERT INTO test (id ) VALUES (? ), (? )"
["occurences"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["avg_run_time"]=>
int(0)
["min_run_time"]=>
int(0)
["max_run_time"]=>
int(0)
["avg_store_time"]=>
int(0)
["min_store_time"]=>
int(0)
["max_store_time"]=>
int(0)
}
[3]=>
array(9) {
["query"]=>
string(31) "SELECT id FROM test WHERE id =?"
["occurences"]=>
int(2)
["eligible_for_caching"]=>
bool(true)
["avg_run_time"]=>
int(159)
["min_run_time"]=>
int(12)
["max_run_time"]=>
int(307)
["avg_store_time"]=>
int(10)
["min_store_time"]=>
int(8)
["max_store_time"]=>
int(13)
}
}
See Also
| Runtime configuration |
| mysqlnd_qc.collect_normalized_query_trace |
| mysqlnd_qc.time_statistics |
mysqlnd_qc_get_query_trace_log
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_get_query_trace_log
Returns a backtrace for each query inspected by the query cache
Description
array mysqlnd_qc_get_query_trace_log();
Returns a backtrace for each query inspected by the query cache.
The collection of the backtrace is disabled by default. To
collect the backtrace you have to set the PHP configuration
directive mysqlnd_qc.collect_query_trace to
1
The maximum depth of the backtrace is limited to the depth set
with the PHP configuration directive
mysqlnd_qc.query_trace_bt_depth.
Parameters
This function has no parameters.
Return Values
An array of query backtrace. Every list entry contains the query string, a backtrace and further detail information.
| Key | Description |
|---|---|
query | Query string. |
origin | Code backtrace. |
run_time | Query run time in milliseconds. The collection of all times and the
necessary gettimeofday system calls
can be disabled by setting the PHP configuration
directive mysqlnd_qc.time_statistics
to 0 |
store_time | Query result set store time in milliseconds. The collection of all times
and the necessary gettimeofday system
calls can be disabled by setting the PHP configuration
directive mysqlnd_qc.time_statistics
to 0 |
eligible_for_caching | TRUE if query is cacheable otherwise
FALSE. |
no_table | TRUE if the query has generated a result set and at
least one column from the result set has no table name
set in its metadata. This is usually the case with
queries which one probably do not want to cache such as
SELECT SLEEP(1). By default any such
query will not be added to the cache. See also PHP
configuration directive
mysqlnd_qc.cache_no_table. |
was_added | TRUE if the query result has been put into the
cache, otherwise FALSE. |
was_already_in_cache | TRUE if the query result would have been added to
the cache if it was not already in the cache (cache
hit). Otherwise FALSE. |
Examples
Example 8.22 mysqlnd_qc_get_query_trace_log
example
mysqlnd_qc.collect_query_trace=1
<?php
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema", "port", "socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");
/* not cached */
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
/* cache put */
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 2");
var_dump($res->fetch_assoc());
$res->free();
/* cache hit */
$res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 2");
var_dump($res->fetch_assoc());
$res->free();
var_dump(mysqlnd_qc_get_query_trace_log());
?>
The above examples will output:
array(1) {
["id"]=>
string(1) "1"
}
array(1) {
["id"]=>
string(1) "2"
}
array(1) {
["id"]=>
string(1) "2"
}
array(6) {
[0]=>
array(8) {
["query"]=>
string(25) "DROP TABLE IF EXISTS test"
["origin"]=>
string(102) "#0 qc.php(4): mysqli->query('DROP TABLE IF E...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
[1]=>
array(8) {
["query"]=>
string(25) "CREATE TABLE test(id INT)"
["origin"]=>
string(102) "#0 qc.php(5): mysqli->query('CREATE TABLE te...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
[2]=>
array(8) {
["query"]=>
string(36) "INSERT INTO test(id) VALUES (1), (2)"
["origin"]=>
string(102) "#0 qc.php(6): mysqli->query('INSERT INTO tes...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(0)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
[3]=>
array(8) {
["query"]=>
string(32) "SELECT id FROM test WHERE id = 1"
["origin"]=>
string(102) "#0 qc.php(9): mysqli->query('SELECT id FROM ...')
#1 {main}"
["run_time"]=>
int(0)
["store_time"]=>
int(25)
["eligible_for_caching"]=>
bool(false)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(false)
}
[4]=>
array(8) {
["query"]=>
string(41) "/*qc=on*/SELECT id FROM test WHERE id = 2"
["origin"]=>
string(103) "#0 qc.php(14): mysqli->query('/*qc=on*/SELECT...')
#1 {main}"
["run_time"]=>
int(311)
["store_time"]=>
int(13)
["eligible_for_caching"]=>
bool(true)
["no_table"]=>
bool(false)
["was_added"]=>
bool(true)
["was_already_in_cache"]=>
bool(false)
}
[5]=>
array(8) {
["query"]=>
string(41) "/*qc=on*/SELECT id FROM test WHERE id = 2"
["origin"]=>
string(103) "#0 qc.php(19): mysqli->query('/*qc=on*/SELECT...')
#1 {main}"
["run_time"]=>
int(13)
["store_time"]=>
int(8)
["eligible_for_caching"]=>
bool(true)
["no_table"]=>
bool(false)
["was_added"]=>
bool(false)
["was_already_in_cache"]=>
bool(true)
}
}
See Also
| Runtime configuration |
| mysqlnd_qc.collect_query_trace |
| mysqlnd_qc.query_trace_bt_depth |
| mysqlnd_qc.time_statistics |
| mysqlnd_qc.cache_no_table |
mysqlnd_qc_get_normalized_query_trace_log
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_set_cache_condition
Set conditions for automatic caching
Description
bool mysqlnd_qc_set_cache_condition(int condition_type,
mixed condition,
mixed condition_option);Sets a condition for automatic caching of statements which do not contain the necessary SQL hints to enable caching of them.
Parameters
condition_type
Type of the condition. The only allowed value is
MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN.
condition
Parameter for the condition set with
condition_type. Parameter type and
structure depend on condition_type
If condition_type equals
MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN
condition must be a string. The string
sets a pattern. Statements are cached if table and
database meta data entry of their result sets match the
pattern. The pattern is checked for a match with the
db and org_table
meta data entries provided by the underlying MySQL client
server library. Please, check the MySQL Reference manual
for details about the two entries. The
db and org_table
values are concatenated with a dot (.)
before matched against condition.
Pattern matching supports the wildcards
% and _. The
wildcard % will match one or many
arbitrary characters. _ will match one
arbitrary character. The escape symbol is backslash.
condition_option
Option for condition. Type and
structure depend on condition_type.
If condition_type equals
MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN
condition_options is the TTL to be
used.
Examples
Example 8.23 mysqlnd_qc_set_cache_condition
example
<?php
/* Cache all accesses to tables with the name "new%" in schema/database "db_example" for 1 second */
if (!mysqlnd_qc_set_cache_condition(MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN, "db_example.new%", 1)) {
die("Failed to set cache condition!");
}
$mysqli = new mysqli("host", "user", "password", "db_example", "port");
/* cached although no SQL hint given */
$mysqli->query("SELECT id, title FROM news");
$pdo_mysql = new PDO("mysql:host=host;dbname=db_example;port=port", "user", "password");
/* not cached: no SQL hint, no pattern match */
$pdo_mysql->query("SELECT id, title FROM latest_news");
/* cached: TTL 1 second, pattern match */
$pdo_mysql->query("SELECT id, title FROM news");
?>
Return Values
Returns TRUE on success or FALSE on FAILURE.
See Also
| Quickstart: pattern based caching |
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_set_is_select
Installs a callback which decides whether a statement is cached
Description
mixed mysqlnd_qc_set_is_select(string callback);Installs a callback which decides whether a statement is cached.
There are several ways of hinting PELC/mysqlnd_qc to cache a
query. By default, PECL/mysqlnd_qc attempts to cache a if
caching of all statements is enabled or the query string begins
with a certain SQL hint. The plugin internally calls a function
named is_select() to find out. This internal
function can be replaced with a user-defined callback. Then, the
user-defined callback is responsible to decide whether the
plugin attempts to cache a statement. Because the internal
function is replaced with the callback, the callback gains full
control. The callback is free to ignore the configuration
setting mysqlnd_qc.cache_by_default and SQL
hints.
The callback is invoked for every statement inspected by the
plugin. It is given the statements string as a parameter. The
callback returns FALSE if the statement
shall not be cached. It returns TRUE to
make the plugin attempt to cache the statements result set, if
any. A so-created cache entry is given the default TTL set with
the PHP configuration directive
mysqlnd_qc.ttl. If a different TTL shall be
used, the callback returns a numeric value to be used as the
TTL.
The internal is_select function is part of
the internal cache storage handler interface. Thus, a
user-defined storage handler offers the same capabilities.
Parameters
This function has no parameters.
Return Values
Returns TRUE on success or
FALSE on failure.
Examples
Example 8.24 mysqlnd_qc_set_is_select
example
<?php
/* callback which decides if query is cached */
function is_select($query) {
static $patterns = array(
/* true - use default from mysqlnd_qc.ttl */
"@SELECT\s+.*\s+FROM\s+test@ismU" => true,
/* 3 - use TTL = 3 seconds */
"@SELECT\s+.*\s+FROM\s+news@ismU" => 3
);
/* check if query does match pattern */
foreach ($patterns as $pattern => $ttl) {
if (preg_match($pattern, $query)) {
printf("is_select(%45s): cache\n", $query);
return $ttl;
}
}
printf("is_select(%45s): do not cache\n", $query);
return false;
}
mysqlnd_qc_set_is_select("is_select");
/* Connect, create and populate test table */
$mysqli = new mysqli("host", "user", "password", "schema");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");
/* cache put */
$mysqli->query("SELECT id FROM test WHERE id = 1");
/* cache hit */
$mysqli->query("SELECT id FROM test WHERE id = 1");
/* cache put */
$mysqli->query("SELECT * FROM test");
?>
The above examples will output:
is_select( DROP TABLE IF EXISTS test): do not cache is_select( CREATE TABLE test(id INT)): do not cache is_select( INSERT INTO test(id) VALUES (1), (2), (3)): do not cache is_select( SELECT id FROM test WHERE id = 1): cache is_select( SELECT id FROM test WHERE id = 1): cache is_select( SELECT * FROM test): cache
See Also
| Runtime configuration |
| mysqlnd_qc.ttl |
| mysqlnd_qc.cache_by_default |
mysqlnd_qc_set_user_handlers
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_set_storage_handler
Change current storage handler
Description
bool mysqlnd_qc_set_storage_handler(string handler);
Sets the storage handler used by the query cache. A list of
available storage handler can be obtained from
mysqlnd_qc_get_available_handlers.
Which storage are available depends on the compile time
configuration of the query cache plugin. The
default storage handler is always available.
All other storage handler must be enabled explicitly when
building the extension.
Parameters
handler
Handler can be of type string representing the name of a
built-in storage handler or an object of type
mysqlnd_qc_handler_default. The names
of the built-in storage handler are
default, APC,
MEMCACHE, sqlite.
Return Values
Returns TRUE on success or
FALSE on failure.
If changing the storage handler fails a catchable fatal error will be thrown. The query cache cannot operate if the previous storage handler has been shutdown but no new storage handler has been installed.
Examples
Example 8.25 mysqlnd_qc_set_storage_handler
example
The example shows the output from the built-in default storage handler. Other storage handler may report different data.
<?php
var_dump(mysqlnd_qc_set_storage_handler("memcache"));
if (true === mysqlnd_qc_set_storage_handler("default"))
printf("Default storage handler activated");
/* Catchable fatal error */
var_dump(mysqlnd_qc_set_storage_handler("unknown"));
?>
The above examples will output:
bool(true) Default storage handler activated Catchable fatal error: mysqlnd_qc_set_storage_handler(): Unknown handler 'unknown' in (file) on line (line)
See Also
| Installation |
mysqlnd_qc_get_available_handlers
|
Copyright 1997-2014 the PHP Documentation Group.
mysqlnd_qc_set_user_handlers
Sets the callback functions for a user-defined procedural storage handler
Description
bool mysqlnd_qc_set_user_handlers(string get_hash,
string find_query_in_cache,
string return_to_cache,
string add_query_to_cache_if_not_exists,
string query_is_select,
string update_query_run_time_stats,
string get_stats,
string clear_cache);Sets the callback functions for a user-defined procedural storage handler.
Parameters
get_hash
Name of the user function implementing the storage handler
get_hash functionality.
find_query_in_cache
Name of the user function implementing the storage handler
find_in_cache functionality.
return_to_cache
Name of the user function implementing the storage handler
return_to_cache functionality.
add_query_to_cache_if_not_exists
Name of the user function implementing the storage handler
add_query_to_cache_if_not_exists
functionality.
query_is_select
Name of the user function implementing the storage handler
query_is_select functionality.
update_query_run_time_stats
Name of the user function implementing the storage handler
update_query_run_time_stats
functionality.
get_stats
Name of the user function implementing the storage handler
get_stats functionality.
clear_cache
Name of the user function implementing the storage handler
clear_cache functionality.
Return Values
Returns TRUE on success or FALSE on FAILURE.
See Also
| Procedural user-defined storage handler example |
Copyright 1997-2014 the PHP Documentation Group.
This change history is a high level summary of selected changes that may impact applications and/or break backwards compatibility.
See also the CHANGES file in the source
distribution for a complete list of changes.
Copyright 1997-2014 the PHP Documentation Group.
1.2.0 - alpha
Feature changes
Update build for PHP 5.5 (Credits: Remi Collet)
APC storage handler update
Introduced
MYSQLND_QC_VERSION
and
MYSQLND_QC_VERSION_ID.
Copyright 1997-2014 the PHP Documentation Group.
1.1.0 - stable
1.1.0 - beta
1.1.0 - alpha
Feature changes
APC storage handler update
New PHP configuration directives
mysqlnd_qc.collect_statistics_log_file. Aggregated
cache statistics log file written after every 10th request served by the
PHP process.
mysqlnd_qc.ignore_sql_comments.
Control whether SQL comments are ignored for cache key hash generation.
New constants and SQL hints
MYSQLND_QC_SERVER_ID_SWITCH allows grouping of cache entries from
different physical connections. This is needed by PECL/mysqlnd_ms.
MYSQLND_QC_CONDITION_META_SCHEMA_PATTERN
to be used with mysqlnd_qc_set_cache_condition.
New function
mysqlnd_qc_set_cache_condition
for built-in schema pattern based caching. Likely to
support a wider range of conditions in the future.
Report valid_until timestamp for cache
entries of the default handler through
mysqlnd_qc_get_cache_info.
Include charset number for cache entry hashing. This should prevent serving result sets which have the wrong charset.
API change: get_hash_key expects new "charsetnr" (int) parameter after "port".
API change: changing is_select() signature from bool is_select() to mixed is_select(). Mixed can be either boolean or array(long ttl, string server_id). This is needed by PECL/mysqlnd_ms.
Other
Support acting as a cache backend for PECL/mysqlnd_ms 1.3.0-beta or later to transparently replace MySQL Replication slave reads with cache accesses, if the user explicitly allows.
Bug fixes
Copyright 1997-2014 the PHP Documentation Group.
1.0.1-stable
Added support for Prepared statements and unbuffered queries.
1.0.0-beta
Initial public release of the transparent TTL-based query result cache. Flexible storage of cached results. Various storage media supported.