El complemento de caché de consultas admite el uso de gestores de almacenamiento definidos por el usuario. Éstos pueden usar algoritmos de invalidacion complejos de forma arbitraria y admitir medios de almacenamiento arbitrarios.
Todos los gestores de almacenamiento definidos por el usuario deben proporcionar una cierta interfaz. Las funciones del gestor de almacenamiento definido por el usuario serán llamadas por el núcleo del complemento de caché. La interfaz necesaria consiste en siete funciones públicas. El gestor de almacenamiento definido por el usuario, tanto procedimental como el orientado a objetos, debe implementar el mismo conjunto de funciones.
Ejemplo #1 Utilizar un gestor de almacenamiento definido por el usuario
<?php
/* Habilitar el almacenamiento en la caché predeterminado para todas las sentencas */
ini_set("mysqlnd_qc.cache_by_default", 1);
/* Funciones del gestor de almacenamiento procedimental definido por el usuario */
$__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) {
/*
Invocada en una búsqueda coincidente en la caché después de haber procesado el
almacenamiento de los datos, podría usarse como cuenta de referencia
*/
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) {
/* almacenar en caché durante 5 segundos */
$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;
}
/* Instalar el gestor de almacenamiento procedimental definido por el usuario */
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("Fallo al instalar el gestor de almacenmiento definido por el usuario\n");
}
/* Conectar, crear y rellenar la tabla test */
$mysqli = new mysqli("host", "usuario", "contraseña", "esquema", "puerto", "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("\nColocada en caché/sin coincidencia en la caché\n");
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
/* Borrar el registro para verificar que obtenemos los datos desde la caché */
$mysqli->query("DELETE FROM test WHERE id = 1");
printf("\nCoincidencia con la caché\n");
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
printf("\nMostrar las estadísticas de la caché\n");
var_dump(mysqlnd_qc_get_cache_info());
printf("\nVolcado de la caché, colocada en la caché/sin coincidencia en la caché");
var_dump(mysqlnd_qc_clear_cache());
$res = $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
?>
El resultado de los ejemplos serían algo similar a:
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 Colocada en caché/sin coincidencia en la caché 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 Coincidencia con la caché 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" } Mostrar las estadísticas de la caché 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) } } } Volcado de la caché, colocada en la caché/sin coincidencia en la caché 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