Medir la eficiencia de la caché

PECL/mysqlnd_qc ofrece tres maneras de medir la eficiencia de la caché. La función mysqlnd_qc_get_normalized_query_trace_log() devuelve estadísticas acumuladas por la cadena de consulta normalizada, mysqlnd_qc_get_cache_info() proporciona información específica del gestor de almacenamiento que incluye una lista de todos los elementos almacenados en la caché, dependiendo de dicho gestor. Adicionalmente, el núcleo de PECL/mysqlnd_qc recopila estadísticas de resumen de alto nivel acumuladas por cada proceso de PHP. Las estadísticas de alto nivel son devueltas por mysqlnd_qc_get_core_stats().

Las funciones mysqlnd_qc_get_normalized_query_trace_log() y mysqlnd_qc_get_core_stats() no recopilarán datos a menos que dicha recopilación haya sido habilitada a través de sus directivas de configuración de PHP correspondientes. La recopilación de datos está deshabilitada de forma predeterminada por consideraciones de rendimiento. Se puede configurar con la opción mysqlnd_qc.time_statistics, la cual determina si se debería recopilar la información de los tiempos. La recopilación de las estadísticas de tiempo está habiltada de forma predeterminada, pero únicamente se lleva cabo si está habilitada la recopilación de datos en sí. El registro de estadísticas de tiempo ocasiona llamadas extra al sistema. En la mayoría de los casos, el beneficio de la monitorización es mayor que cualquier penalización de rendimiento potencial causado por las llamadas adicionales al sistema.

Ejemplo #1 Recopilar datos de estadísticas con el ajuste ini mysqlnd_qc.time_statistics

mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_statistics=1
<?php
/* conectarse a MySQL */
$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), (3)");

/* consultas varias */
for ($i 1$i <= 4$i++) {
    
$consulta sprintf("/*%s*/SELECT id FROM test WHERE id = %d"MYSQLND_QC_ENABLE_SWITCH$i 2);
    
$res   $mysqli->query($consulta);
    
    
$res->free();
}

var_dump(mysqlnd_qc_get_core_stats());
?>

El resultado de los ejemplos sería algo similar a:

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)
}

Para una visión general se llama a mysqlnd_qc_get_core_stats(), que emite estadísticas relacionadas con el uso de la caché, los tiempos de caché y el tráfico. Los valores se acumulan en función de cada proceso para todas las consultas emitidas por cualquier llamada a la API de MySQL para PHP.

Algunos gestores de almacenamiento, como el gestor predetermindo, pueden informar de las entradas de la caché, de las estadísticas relacionadas con las entradas y de los metadatos de las consultas subyacentes a través de la función mysqlnd_qc_get_cache_info(). Por favor, observe que la información devuelta depende del gestor de almacenmiento. Los valores se acumulan en función de cada proceso.

Ejemplo #2 Ejemplo del uso de mysqlnd_qc_get_cache_info()

mysqlnd_qc.enable_qc=1
<?php
/* conectarse a MySQL */
$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), (3)");

/* consultas varias para rellenar el rastreo de consultas */
for ($i 1$i <= 4$i++) {
    
$consulta sprintf("/*%s*/SELECT id FROM test WHERE id = %d"MYSQLND_QC_ENABLE_SWITCH$i 2);
    
$res   $mysqli->query($consulta);
  
    
$res->free();
}

var_dump(mysqlnd_qc_get_cache_info());
?>

El resultado de los ejemplos sería algo similar a:

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)
        }
      }
    }
  }
}

Es posible disminuir más la granularidad de las estadísticas al nivel de la cadena de sentencia normalizada. Ésta es la cadena de sentencia con todos los parámetros reemplazados con signos de interrogación. Por ejemplo, las dos sentencias SELECT id FROM test WHERE id = 0 y SELECT id FROM test WHERE id = 1 se normalizan a SELECT id FROM test WHERE id = ?. Sus estadísticas se acumulan en una entrada para SELECT id FROM test WHERE id = ?.

Ejemplo #3 Ejemplo del uso de mysqlnd_qc_get_normalized_query_trace_log()

mysqlnd_qc.enable_qc=1
mysqlnd_qc.collect_normalized_query_trace=1
<?php
/* conectarse a MySQL */
$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), (3)");

/* consultas varias para rellenar el rastreo de consultas */
for ($i 1$i <= 4$i++) {
    
$consulta sprintf("/*%s*/SELECT id FROM test WHERE id = %d"MYSQLND_QC_ENABLE_SWITCH$i 2);
    
$res   $mysqli->query($consulta);
  
    
$res->free();
}

var_dump(mysqlnd_qc_get_normalized_query_trace_log());
?>

El resultado de los ejemplos sería algo similar a:

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)
  }
}

La distribución del código fuente de PECL/mysqlnd_qc contiene el directorio web/ en el que se pueden encontrar scripts basados en monitorización que proporcionan un ejemplo de cómo escribir un monitor de caché. Por favor, siga las instrucciones proporcionadas en el código fuente.

Desde PECL/mysqlnd_qc 1.1.0 es posible escribir estadísticas en un fichero de registro. Véase mysqlnd_qc.collect_statistics_log_file.