&reftitle.examples;
Basic usage The Query Cache plugin supports caching of queries issued by the following user API calls: mysqli mysqli_query mysqli_real_query + mysqli_store_result PDO_MYSQL PDO::query if PDO::ATTR_EMULATE_PREPARES = 1 (default setting) mysql mysql_query A 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. uncached: SELECT id FROM test cached: /*qc=on*/SELECT id FROM test Example using the most advanced PHP MySQL API, which is 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: cache put and cache_miss */ $res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 1"); var_dump($res->fetch_assoc()); $res->free(); /* Will NOT be cached and will NOT hit the cache: no SQL hint */ $res = $mysqli->query("SELECT id FROM test WHERE id = 2"); var_dump($res->fetch_assoc()); $res->free(); /* Display cache statistics */ $stats = mysqlnd_qc_get_core_stats(); printf("Cache hit\t: %d\n", $stats['cache_hit']); printf("Cache miss\t: %d\n", $stats['cache_miss']); printf("Cache put\t: %d\n", $stats['cache_put']); ?> ]]> &examples.outputs; string(1) "1" } array(1) { ["id"]=> string(1) "2" } Cache hit : 0 Cache miss : 1 Cache put : 1 ]]> The default invalidation strategy of the cache is Time-to-Live ( TTL). Cache entries are valid for a certain duration. The default duration is set by the PHP configuration directive mysqlnd_qc.tll
Setting the TTL The default invalidation strategy of the query cache plugin is Time-to-Live ( TTL). The built-in storage handler 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. Any TTL based cache can serve stale data. Cache entries are not automatically invalidated, if underlying data changes. User-defined cache storage handler can implement any invalidation strategy to work around this limitation. 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 because of the SQL hint */ $res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "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! */ $res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 1"); var_dump($res->fetch_assoc()); $res->free(); sleep(ini_get("mysqlnd_qc.ttl")); /* Cache miss - cache entry has expired */ $res = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT id FROM test WHERE id = 1"); var_dump($res->fetch_assoc()); $res->free(); ?> ]]> &examples.outputs; string(1) "1" } array(1) { ["id"]=> string(1) "1" } NULL ]]> 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. 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); ?> ]]> &examples.outputs; string(1) "1" } array(1) { ["id"]=> string(1) "1" } NULL Script runtime : 3 seconds ]]>
Cache by default 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. 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 or served from cache 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(); ?> ]]> &examples.outputs; string(1) "1" } array(1) { ["id"]=> string(1) "1" } NULL ]]>
Procedural user-defined storage handler 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. Please check the example for details. $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(); ?> ]]> &examples.outputs; 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 ]]>