Utiliser les optimisations basées sur l'historique

Ce guide explique comment fonctionnent les optimisations basées sur l'historique et comment estimer leur impact sur les requêtes.

À propos des optimisations basées sur l'historique

Les optimisations basées sur l'historique utilisent automatiquement les informations provenant d'exécutions déjà terminées de requêtes identiques ou similaires, visant à appliquer des optimisations supplémentaires et à encore améliorer les performances des requêtes, telles que la durée d'utilisation des emplacements et la latence des requêtes. Par exemple, la première exécution d'une requête peut prendre 60 secondes, mais la deuxième exécution peut ne prendre que 30 secondes si une optimisation basée sur l'historique a été identifiée. Ce processus se poursuit jusqu'à ce qu'il n'y ait plus d'optimisations à ajouter.

Voici un exemple de fonctionnement des optimisations basées sur l'historique avec BigQuery :

Nombre d'exécutions Durée d'utilisation des emplacements consommée pour la requête Remarques
1 60 Exécution d'origine.
2 30 Application de la première optimisation basée sur l'historique.
3 20 Application de la deuxième optimisation basée sur l'historique.
4 21 Aucune autre optimisation basée sur l'historique à appliquer.
5 19 Aucune autre optimisation basée sur l'historique à appliquer.
6 20 Aucune autre optimisation basée sur l'historique à appliquer.

Les optimisations basées sur l'historique ne sont appliquées que lorsqu'il est fort probable que cela ait un impact positif sur les performances des requêtes. En outre, lorsqu'une optimisation n'améliore pas de manière significative les performances des requêtes ou peut entraîner une baisse des performances, elle est révoquée et n'est pas utilisée dans les exécutions futures de cette requête.

Examiner les optimisations basées sur l'historique associées à un job

Pour examiner les optimisations basées sur l'historique associées à un job, vous pouvez utiliser une requête SQL ou un appel de méthode d'API REST.

SQL

Vous pouvez utiliser une requête pour obtenir les optimisations basées sur l'historique associées à un job. La requête doit inclure l'instruction INFORMATION_SCHEMA.JOBS_BY_PROJECT et le nom de colonne query_info.optimization_details.

L'exemple suivant renvoie les détails d'optimisation associés à un job nommé sample_job. Si aucune optimisation basée sur l'historique n'a été appliquée, la valeur NULL est générée pour optimization_details :

SELECT
  job_id,
  query_info.optimization_details
FROM `PROJECT_NAME.region-LOCATION`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE job_id = 'sample_job'
LIMIT 1;

Les résultats ressemblent à ce qui suit :

-- The JSON in optimization_details has been formatted for readability.
/*------------+-----------------------------------------------------------------*
 | job_id     | optimization_details                                            |
 +------------+-----------------------------------------------------------------+
 | sample_job | {                                                               |
 |            |   "optimizations": [                                            |
 |            |     {                                                           |
 |            |       "semi_join_reduction": "web_sales.web_date,RIGHT"         |
 |            |     },                                                          |
 |            |     {                                                           |
 |            |       "semi_join_reduction": "catalog_sales.catalog_date,RIGHT" |
 |            |     },                                                          |
 |            |     {                                                           |
 |            |       "semi_join_reduction": "store_sales.store_date,RIGHT"     |
 |            |     },
 |            |     {                                                           |
 |            |       "join_commutation": "web_returns.web_item"                |
 |            |     },
 |            |     {                                                           |
 |            |       "parallelism_adjustment": "applied"                       |
 |            |     },
 |            |   ]                                                             |
 |            | }                                                               |
 *------------+-----------------------------------------------------------------*/

API

Pour obtenir les détails d'optimisation associés à un job, vous pouvez appeler la méthode jobs.get.

Dans l'exemple suivant, la méthode jobs.get renvoie les détails d'optimisation (optimizationDetails) dans la réponse complète :

{
  "jobReference": {
    "projectId": "myProject",
    "jobId": "sample_job"
  }
}

Les résultats ressemblent à ce qui suit :

-- The unrelated parts in the full response have been removed.
{
  "jobReference": {
    "projectId": "myProject",
    "jobId": "sample_job",
    "location": "US"
  },
  "statistics": {
    "query": {
      "queryInfo": {
        "optimizationDetails": {
          "optimizations": [
            {
              "semi_join_reduction": "web_sales.web_date,RIGHT"
            },
            {
              "semi_join_reduction": "catalog_sales.catalog_date,RIGHT"
            },
            {
              "semi_join_reduction": "store_sales.store_date,RIGHT"
            },
            {
              "join_commutation": "web_returns.web_item"
            },
            {
              "parallelism_adjustment": "applied"
            }
          ]
        }
      }
    }
  }
}

Estimer l'impact des optimisations basées sur l'historique

Pour estimer l'impact des optimisations basées sur l'historique, vous pouvez utiliser l'exemple de requête SQL suivant afin d'identifier les requêtes de projet qui présentent l'amélioration de temps d'exécution estimée la plus grande.

  WITH
    jobs AS (
      SELECT
        *,
        query_info.query_hashes.normalized_literals AS query_hash,
        TIMESTAMP_DIFF(end_time, start_time, MILLISECOND) AS elapsed_ms,
        IFNULL(
          ARRAY_LENGTH(JSON_QUERY_ARRAY(query_info.optimization_details.optimizations)) > 0,
          FALSE)
          AS has_history_based_optimization,
      FROM region-LOCATION.INFORMATION_SCHEMA.JOBS_BY_PROJECT
      WHERE EXTRACT(DATE FROM creation_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
    ),
    most_recent_jobs_without_history_based_optimizations AS (
      SELECT *
      FROM jobs
      WHERE NOT has_history_based_optimization
      QUALIFY ROW_NUMBER() OVER (PARTITION BY query_hash ORDER BY end_time DESC) = 1
    )
  SELECT
    job.job_id,
    100 * SAFE_DIVIDE(
      original_job.elapsed_ms - job.elapsed_ms,
      original_job.elapsed_ms) AS percent_execution_time_saved,
    job.elapsed_ms AS new_elapsed_ms,
    original_job.elapsed_ms AS original_elapsed_ms,
  FROM jobs AS job
  INNER JOIN most_recent_jobs_without_history_based_optimizations AS original_job
    USING (query_hash)
  WHERE
    job.has_history_based_optimization
    AND original_job.end_time < job.start_time
  ORDER BY percent_execution_time_saved DESC
  LIMIT 10;

Le résultat de la requête précédente se présente comme suit si des optimisations basées sur l'historique ont été appliquées :

  /*--------------+------------------------------+------------------+-----------------------*
   |    job_id    | percent_execution_time_saved | new_execution_ms | original_execution_ms |
   +--------------+------------------------------+------------------+-----------------------+
   | sample_job1  |           67.806850186245114 |             7087 |                 22014 |
   | sample_job2  |           66.485800412501987 |            10562 |                 31515 |
   | sample_job3  |           63.285605271764254 |            97668 |                266021 |
   | sample_job4  |           61.134141726887904 |           923384 |               2375823 |
   | sample_job5  |           55.381272089713754 |          1060062 |               2375823 |
   | sample_job6  |           45.396943168036479 |          2324071 |               4256302 |
   | sample_job7  |           38.227031526376024 |            17811 |                 28833 |
   | sample_job8  |           33.826608962725111 |            66360 |                100282 |
   | sample_job9  |           32.087813758311604 |            44020 |                 64819 |
   | sample_job10 |           28.356416319483539 |            19088 |                 26643 |
   *--------------+------------------------------+------------------+-----------------------*/

Les résultats de cette requête ne sont qu'une estimation de l'impact des optimisations basées sur l'historique. De nombreux facteurs peuvent influencer les performances des requêtes, y compris, mais sans s'y limiter, la disponibilité des emplacements, l'évolution des données au fil du temps, les définitions de vues ou de UDF, et les différences dans les valeurs des paramètres de requête.

Si le résultat de cet exemple de requête est vide, cela signifie qu'aucun job n'a utilisé d'optimisations basées sur l'historique ou que toutes les requêtes ont été optimisées il y a plus de 30 jours.

Cette requête peut être appliquée à d'autres métriques de performances des requêtes, telles que total_slot_ms et total_bytes_billed. Pour en savoir plus, consultez le schéma INFORMATION_SCHEMA.JOBS.