Gestire lo strumento di ottimizzazione delle query

Questa pagina descrive come gestire l'ottimizzatore delle query in Spanner per i database con dialetto GoogleSQL e PostgreSQL.

Lo strumento di ottimizzazione delle query Spanner determina il modo più efficiente per eseguire una query SQL. Tuttavia, il piano di query determinato dallo strumento di ottimizzazione potrebbe cambiare leggermente quando lo strumento di ottimizzazione delle query stesso si evolve o quando le statistiche del database vengono aggiornate. Per ridurre al minimo il potenziale di regressione delle prestazioni quando l'ottimizzatore delle query o le statistiche cambiano, Spanner fornisce le seguenti opzioni di query.

  • optimizer_version: le modifiche allo strumento di ottimizzazione delle query vengono raggruppate e rilasciate come versioni dello strumento di ottimizzazione. Spanner inizia a utilizzare l'ultima versione dell'ottimizzatore come predefinita almeno 30 giorni dopo il rilascio di questa versione. Puoi utilizzare l'opzione della versione dello strumento di ottimizzazione delle query per eseguire query su una versione precedente dello strumento di ottimizzazione.

  • optimizer_statistics_package: Spanner aggiorna regolarmente le statistiche dell'ottimizzatore. Le nuove statistiche vengono rese disponibili come pacchetto. Questa opzione di query specifica un pacchetto di statistiche da utilizzare per l'ottimizzazione delle query durante la compilazione di una query SQL. Il pacchetto specificato deve avere la garbage collection disattivata:

GoogleSQL

 ALTER STATISTICS <package_name> SET OPTIONS (allow_gc=false)

PostgreSQL

ALTER STATISTICS spanner."<package_name>" SET OPTIONS (allow_gc = true)

Questa guida mostra come impostare queste singole opzioni in ambiti diversi in Spanner.

Elenca le opzioni dello strumento di ottimizzazione delle query

Spanner archivia informazioni sulle versioni dell'ottimizzatore e sui pacchetti di statistiche disponibili che puoi selezionare.

Versioni dello strumento di ottimizzazione

La versione dello strumento di ottimizzazione delle query è un valore intero, incrementato di 1 a ogni aggiornamento. L'ultima versione dello strumento di ottimizzazione delle query è 8.

Esegui la seguente istruzione SQL per restituire un elenco di tutte le versioni dell'ottimizzatore supportate, insieme alle date di rilascio corrispondenti e a un'indicazione che specifichi se la versione è quella predefinita. Il numero di versione più grande restituito è l'ultima versione supportata dell'ottimizzatore.

SELECT * FROM SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS;

Versione predefinita

Per impostazione predefinita, Spanner inizia a utilizzare l'ultima versione dell'ottimizzatore almeno 30 giorni dopo il rilascio. Durante il periodo superiore a 30 giorni tra una nuova release e quella che diventa predefinita, ti consigliamo di testare le query sulla nuova versione per rilevare eventuali regressioni.

Per trovare la versione predefinita, esegui la seguente istruzione SQL:

SELECT * FROM SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS;

La query restituisce un elenco di tutte le versioni dello strumento di ottimizzazione supportate. La colonna IS_DEFAULT specifica la versione predefinita corrente.

Per informazioni dettagliate su ciascuna versione, vedi Cronologia delle versioni dell'ottimizzatore di query.

Pacchetti di statistiche dello strumento di ottimizzazione

A ogni nuovo pacchetto di statistiche dell'ottimizzatore creato da Spanner viene assegnato un nome di pacchetto che è garantito essere univoco all'interno del database specificato.

Il formato del nome del pacchetto è auto_{PACKAGE_TIMESTAMP}UTC. In GoogleSQL, l'istruzione ANALYZE attiva la creazione del nome del pacchetto di statistiche. In PostgreSQL, l'istruzione ANALYZE esegue questa attività. Il formato del nome del pacchetto di statistiche è analyze_{PACKAGE_TIMESTAMP}UTC, dove {PACKAGE_TIMESTAMP} è il timestamp, nel fuso orario UTC, dell'inizio della creazione delle statistiche. Esegui la seguente istruzione SQL per restituire un elenco di tutti i pacchetti di statistiche per l'ottimizzazione disponibili.

SELECT * FROM INFORMATION_SCHEMA.SPANNER_STATISTICS;

Per impostazione predefinita, Spanner utilizza il pacchetto di statistiche dello strumento di ottimizzazione più recente, a meno che il database o la query non siano bloccati su un pacchetto precedente utilizzando uno dei metodi descritti in questa pagina.

Precedenza dell'override delle opzioni

Se utilizzi un database con dialetto GoogleSQL, Spanner offre diversi modi per modificare le opzioni dell'ottimizzatore. Ad esempio, puoi impostare le opzioni per una query specifica o configurarle nella libreria client a livello di processo o query. Quando un'opzione viene impostata in più modi, si applica il seguente ordine di precedenza. Seleziona un link per passare alla sezione corrispondente di questo documento.

Spanner default ← database optionclient appenvironment variableclient querystatement hint

Ad esempio, ecco come interpretare l'ordine di precedenza quando si imposta la versione dell'ottimizzatore delle query:

Quando crei un database, viene utilizzata la versione predefinita dell'ottimizzatore di Spanner. L'impostazione della versione dell'ottimizzatore utilizzando uno dei metodi elencati in precedenza ha la precedenza su qualsiasi altro elemento a sinistra. Ad esempio, l'impostazione dell'ottimizzatore per un'app che utilizza una variabile di ambiente ha la precedenza su qualsiasi valore impostato per il database utilizzando l'opzione database. L'impostazione della versione dello strumento di ottimizzazione tramite un suggerimento di istruzione ha la precedenza più alta per la query specificata, che ha la precedenza sul valore impostato utilizzando qualsiasi altro metodo.

Le sezioni seguenti forniscono maggiori dettagli su ciascun metodo.

Imposta le opzioni dello strumento di ottimizzazione a livello di database

Puoi impostare la versione predefinita dello strumento di ottimizzazione su un database utilizzando il seguente comando DDL ALTER DATABASE.

GoogleSQL

ALTER DATABASE MyDatabase
SET OPTIONS (optimizer_version =  8);

PostgreSQL

ALTER DATABASE MyDatabase SET spanner.optimizer_version = 5;

Puoi impostare il pacchetto di statistiche in modo simile, come mostrato nell'esempio seguente.

GoogleSQL

ALTER DATABASE MyDatabase
SET OPTIONS (optimizer_statistics_package = "auto_20191128_14_47_22UTC");

PostgreSQL

ALTER DATABASE MyDatabase
SET spanner.optimizer_statistics_package = "auto_20191128_14_47_22UTC";

Puoi anche impostare più di un'opzione contemporaneamente, come mostrato nel seguente comando DDL.

GoogleSQL

ALTER DATABASE MyDatabase
SET OPTIONS (optimizer_version = 8,
            optimizer_statistics_package = "auto_20191128_14_47_22UTC");

Puoi eseguire ALTER DATABASE in gcloud CLI con il comando gcloud CLI databases ddl update come segue.

GoogleSQL

gcloud spanner databases ddl update MyDatabase --instance=test-instance \
    --ddl='ALTER DATABASE MyDatabase SET OPTIONS ( optimizer_version = 8 )'

PostgreSQL

gcloud spanner databases ddl update MyDatabase --instance=test-instance \
  --ddl='ALTER DATABASE MyDatabase SET spanner.optimizer_version = 8'

Se imposti un'opzione del database su NULL (in GoogleSQL) o DEFAULT (in PostgreSQL), questa viene cancellata in modo che venga utilizzato il valore predefinito.

Per visualizzare il valore corrente di queste opzioni per un database, esegui una query sulla vista INFORMATION_SCHEMA.DATABASE_OPTIONS per GoogleSQL o sulla tabella information_schema database_options per PostgreSQL, come segue.

GoogleSQL

SELECT
  s.OPTION_NAME,
  s.OPTION_VALUE
FROM
  INFORMATION_SCHEMA.DATABASE_OPTIONS s
WHERE
  s.SCHEMA_NAME=""
  AND s.OPTION_NAME IN ('optimizer_version', 'optimizer_statistics_package')

PostgreSQL

  SELECT
    s.option_name,
    s.option_value
  FROM
    information_schema.database_options s
  WHERE
    s.schema_name='public'
    AND s.option_name IN ('optimizer_version',
      'optimizer_statistics_package')

Impostare le opzioni dell'ottimizzatore con le librerie client

Quando interagisci in modo programmatico con Spanner tramite le librerie client, esistono diversi modi per modificare le opzioni di query per l'applicazione client.

Per impostare le opzioni dell'ottimizzatore, devi utilizzare le versioni più recenti delle librerie client.

Impostare le opzioni dello strumento di ottimizzazione per un client di database

Un'applicazione può impostare le opzioni dell'ottimizzatore a livello globale nella libreria client configurando la proprietà delle opzioni di query come mostrato negli snippet di codice seguenti. Le impostazioni dell'ottimizzatore vengono memorizzate nell'istanza client e vengono applicate a tutte le query eseguite durante il ciclo di vita del client. Anche se le opzioni vengono applicate a livello di database nel backend, quando vengono impostate a livello di client, si applicano a tutti i database connessi a quel client.

C++

namespace spanner = ::google::cloud::spanner;
spanner::Client client(
    spanner::MakeConnection(db),
    google::cloud::Options{}
        .set<spanner::QueryOptimizerVersionOption>("1")
        .set<spanner::QueryOptimizerStatisticsPackageOption>(
            "auto_20191128_14_47_22UTC"));

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class CreateConnectionWithQueryOptionsAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public int SingerId { get; set; }
        public string AlbumTitle { get; set; }
    }

    public async Task<List<Album>> CreateConnectionWithQueryOptionsAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString)
        {
            // Set query options on the connection.
            QueryOptions = QueryOptions.Empty
                .WithOptimizerVersion("1")
                // The list of available statistics packages for the database can
                // be found by querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS"
                // table.
                .WithOptimizerStatisticsPackage("latest")
        };

        var albums = new List<Album>();
        var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                SingerId = reader.GetFieldValue<int>("SingerId"),
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/spanner"
	sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
	"google.golang.org/api/iterator"
)

func createClientWithQueryOptions(w io.Writer, database string) error {
	ctx := context.Background()
	queryOptions := spanner.QueryOptions{
		Options: &sppb.ExecuteSqlRequest_QueryOptions{
			OptimizerVersion: "1",
			// The list of available statistics packages can be found by
			// querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
			OptimizerStatisticsPackage: "latest",
		},
	}
	client, err := spanner.NewClientWithConfig(
		ctx, database, spanner.ClientConfig{QueryOptions: queryOptions},
	)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: `SELECT VenueId, VenueName, LastUpdateTime FROM Venues`}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var venueID int64
		var venueName string
		var lastUpdateTime time.Time
		if err := row.Columns(&venueID, &venueName, &lastUpdateTime); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %s %s\n", venueID, venueName, lastUpdateTime)
	}
}

Java

static void clientWithQueryOptions(DatabaseId db) {
  SpannerOptions options =
      SpannerOptions.newBuilder()
          .setDefaultQueryOptions(
              db, QueryOptions
                  .newBuilder()
                  .setOptimizerVersion("1")
                  // The list of available statistics packages can be found by querying the
                  // "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
                  .setOptimizerStatisticsPackage("latest")
                  .build())
          .build();
  Spanner spanner = options.getService();
  DatabaseClient dbClient = spanner.getDatabaseClient(db);
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(
  databaseId,
  {},
  {
    optimizerVersion: '1',
    // The list of available statistics packages can be found by querying the
    // "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
    optimizerStatisticsPackage: 'latest',
  },
);

const query = {
  sql: `SELECT AlbumId, AlbumTitle, MarketingBudget
        FROM Albums
        ORDER BY AlbumTitle`,
};

// Queries rows from the Albums table
try {
  const [rows] = await database.run(query);

  rows.forEach(row => {
    const json = row.toJSON();
    const marketingBudget = json.MarketingBudget
      ? json.MarketingBudget
      : null; // This value is nullable
    console.log(
      `AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${marketingBudget}`,
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Database;

/**
 * Create a client with query options.
 * Example:
 * ```
 * create_client_with_query_options($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function create_client_with_query_options(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient([
        'queryOptions' => [
            'optimizerVersion' => '1',
            // Pin the statistics package used for this client instance to the
            // latest version. The list of available statistics packages can be
            // found by querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS"
            // table.
            'optimizerStatisticsPackage' => 'latest'
        ]
    ]);
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $results = $database->execute(
        'SELECT VenueId, VenueName, LastUpdateTime FROM Venues'
    );

    foreach ($results as $row) {
        printf('VenueId: %s, VenueName: %s, LastUpdateTime: %s' . PHP_EOL,
            $row['VenueId'], $row['VenueName'], $row['LastUpdateTime']);
    }
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client(
    query_options={
        "optimizer_version": "1",
        "optimizer_statistics_package": "latest",
    }
)
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

with database.snapshot() as snapshot:
    results = snapshot.execute_sql(
        "SELECT VenueId, VenueName, LastUpdateTime FROM Venues"
    )

    for row in results:
        print("VenueId: {}, VenueName: {}, LastUpdateTime: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

query_options = {
  optimizer_version: "1",
  # The list of available statistics packages can be
  # found by querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS"
  # table.
  optimizer_statistics_package: "latest"
}

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id, query_options: query_options

sql_query = "SELECT VenueId, VenueName, LastUpdateTime FROM Venues"

client.execute(sql_query).rows.each do |row|
  puts "#{row[:VenueId]} #{row[:VenueName]} #{row[:LastUpdateTime]}"
end

Impostare le opzioni dell'ottimizzatore con le variabili di ambiente

Per semplificare la prova di diverse impostazioni dell'ottimizzatore senza dover ricompilare l'app, puoi impostare le variabili di ambiente SPANNER_OPTIMIZER_VERSION e SPANNER_OPTIMIZER_STATISTICS_PACKAGE ed eseguire l'app, come mostrato nel seguente snippet.

Linux / macOS

export SPANNER_OPTIMIZER_VERSION="8"
export SPANNER_OPTIMIZER_STATISTICS_PACKAGE="auto_20191128_14_47_22UTC"

Windows

set SPANNER_OPTIMIZER_VERSION="8"
  set SPANNER_OPTIMIZER_STATISTICS_PACKAGE="auto_20191128_14_47_22UTC"

I valori delle opzioni dell'ottimizzatore di query specificate vengono letti e archiviati nell'istanza del client al momento dell'inizializzazione del client e si applicano a tutte le query eseguite per tutta la durata del client.

Impostare le opzioni di ottimizzazione per una query client

Puoi specificare un valore per la versione dell'ottimizzatore o per la versione del pacchetto di statistiche a livello di query nell'applicazione client specificando una proprietà delle opzioni di query durante la creazione della query.

C++

void QueryWithQueryOptions(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto sql = spanner::SqlStatement("SELECT SingerId, FirstName FROM Singers");
  auto opts =
      google::cloud::Options{}
          .set<spanner::QueryOptimizerVersionOption>("1")
          .set<spanner::QueryOptimizerStatisticsPackageOption>("latest");
  auto rows = client.ExecuteQuery(std::move(sql), std::move(opts));

  using RowType = std::tuple<std::int64_t, std::string>;
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "FirstName: " << std::get<1>(*row) << "\n";
  }
  std::cout << "Read completed for [spanner_query_with_query_options]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class RunCommandWithQueryOptionsAsyncSample
{
    public class Album
    {
        public int SingerId { get; set; }
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
    }

    public async Task<List<Album>> RunCommandWithQueryOptionsAsync(string projectId, string instanceId, string databaseId)
    {
        var connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");

        cmd.QueryOptions = QueryOptions.Empty
            .WithOptimizerVersion("1")
            // The list of available statistics packages for the database can
            // be found by querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS"
            // table.
            .WithOptimizerStatisticsPackage("latest");
        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album()
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                SingerId = reader.GetFieldValue<int>("SingerId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/spanner"
	sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
	"google.golang.org/api/iterator"
)

func queryWithQueryOptions(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: `SELECT VenueId, VenueName, LastUpdateTime FROM Venues`}
	queryOptions := spanner.QueryOptions{
		Options: &sppb.ExecuteSqlRequest_QueryOptions{
			OptimizerVersion: "1",
			// The list of available statistics packages can be found by
			// querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
			OptimizerStatisticsPackage: "latest",
		},
	}
	iter := client.Single().QueryWithOptions(ctx, stmt, queryOptions)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var venueID int64
		var venueName string
		var lastUpdateTime time.Time
		if err := row.Columns(&venueID, &venueName, &lastUpdateTime); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %s %s\n", venueID, venueName, lastUpdateTime)
	}
}

Java

static void queryWithQueryOptions(DatabaseClient dbClient) {
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .executeQuery(
              Statement
                  .newBuilder("SELECT SingerId, AlbumId, AlbumTitle FROM Albums")
                  .withQueryOptions(QueryOptions
                      .newBuilder()
                      .setOptimizerVersion("1")
                      // The list of available statistics packages can be found by querying the
                      // "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
                      .setOptimizerStatisticsPackage("latest")
                      .build())
                  .build())) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const query = {
  sql: `SELECT AlbumId, AlbumTitle, MarketingBudget
        FROM Albums
        ORDER BY AlbumTitle`,
  queryOptions: {
    optimizerVersion: 'latest',
    // The list of available statistics packages can be found by querying the
    // "INFORMATION_SCHEMA.SPANNER_STATISTICS" table.
    optimizerStatisticsPackage: 'latest',
  },
};

// Queries rows from the Albums table
try {
  const [rows] = await database.run(query);

  rows.forEach(row => {
    const json = row.toJSON();
    const marketingBudget = json.MarketingBudget
      ? json.MarketingBudget
      : null; // This value is nullable
    console.log(
      `AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${marketingBudget}`,
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Database;

/**
 * Queries sample data using SQL with query options.
 * Example:
 * ```
 * query_data_with_query_options($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function query_data_with_query_options(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $results = $database->execute(
        'SELECT VenueId, VenueName, LastUpdateTime FROM Venues',
        [
            'queryOptions' => [
                'optimizerVersion' => '1',
                // Pin the statistics package to the latest version just for
                // this query.
                'optimizerStatisticsPackage' => 'latest'
            ]
        ]
    );

    foreach ($results as $row) {
        printf('VenueId: %s, VenueName: %s, LastUpdateTime: %s' . PHP_EOL,
            $row['VenueId'], $row['VenueName'], $row['LastUpdateTime']);
    }
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

with database.snapshot() as snapshot:
    results = snapshot.execute_sql(
        "SELECT VenueId, VenueName, LastUpdateTime FROM Venues",
        query_options={
            "optimizer_version": "1",
            "optimizer_statistics_package": "latest",
        },
    )

    for row in results:
        print("VenueId: {}, VenueName: {}, LastUpdateTime: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

sql_query = "SELECT VenueId, VenueName, LastUpdateTime FROM Venues"
query_options = {
  optimizer_version: "1",
  # The list of available statistics packagebs can be
  # found by querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS"
  # table.
  optimizer_statistics_package: "latest"
}

client.execute(sql_query, query_options: query_options).rows.each do |row|
  puts "#{row[:VenueId]} #{row[:VenueName]} #{row[:LastUpdateTime]}"
end

Impostare le opzioni dell'ottimizzatore per una query utilizzando un suggerimento per l'istruzione

Un suggerimento per l'istruzione è un suggerimento su un'istruzione di query che modifica l'esecuzione della query rispetto al comportamento predefinito. L'impostazione del suggerimento OPTIMIZER_VERSION in un'istruzione forza l'esecuzione della query utilizzando la versione specificata dell'ottimizzatore di query.

Il suggerimento OPTIMIZER_VERSION ha la precedenza della versione dell'ottimizzatore più elevata. Se il suggerimento dell'istruzione è specificato, viene utilizzato indipendentemente da tutte le altre impostazioni della versione dell'ottimizzatore.

GoogleSQL

@{OPTIMIZER_VERSION=8} SELECT * FROM MyTable;

PostgreSQL

/*@OPTIMIZER_VERSION=8*/ SELECT * FROM MyTable;

Puoi anche utilizzare il valore letterale latest_version per impostare la versione dello strumento di ottimizzazione per una query sull'ultima versione, come mostrato qui.

GoogleSQL

@{OPTIMIZER_VERSION=latest_version} SELECT * FROM MyTable;

PostgreSQL

/*@OPTIMIZER_VERSION=latest_version*/ SELECT * FROM MyTable;

L'impostazione del suggerimento OPTIMIZER_STATISTICS_PACKAGE su un'istruzione forza l'esecuzione della query utilizzando la versione specificata del pacchetto di statistiche dello strumento di ottimizzazione delle query. Il pacchetto specificato deve avere la garbage collection disabilitata:

GoogleSQL

ALTER STATISTICS <package_name> SET OPTIONS (allow_gc=false)

PostgreSQL

ALTER STATISTICS spanner."package_name" SET OPTIONS (allow_gc=false)

Il suggerimento OPTIMIZER_STATISTICS_PACKAGE ha la priorità più alta per l'impostazione del pacchetto di ottimizzazione. Se viene specificato il suggerimento per l'istruzione, questo viene utilizzato indipendentemente da tutte le altre impostazioni della versione del pacchetto dell'ottimizzatore.

@{OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC} SELECT * FROM MyTable;

Puoi anche utilizzare il valore letterale latest per utilizzare il pacchetto di statistiche più recente.

@{OPTIMIZER_STATISTICS_PACKAGE=latest} SELECT * FROM MyTable;

Entrambi gli hint possono essere impostati in un'unica istruzione, come mostrato nell'esempio seguente.

Il valore letterale default_version imposta la versione dello strumento di ottimizzazione per una query sulla versione predefinita, che potrebbe essere diversa dall'ultima versione. Per ulteriori dettagli, consulta Versione predefinita.

GoogleSQL

@{OPTIMIZER_VERSION=default_version, OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC} SELECT * FROM MyTable;

PostgreSQL

/*@OPTIMIZER_VERSION=default_version, OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC*/ SELECT * FROM KeyValue;

Impostare le opzioni dell'ottimizzatore quando si utilizza il driver JDBC di Spanner

Puoi eseguire l'override del valore predefinito della versione dell'ottimizzatore e del pacchetto di statistiche specificando le opzioni nella stringa di connessione JDBC come mostrato nell'esempio seguente.

Queste opzioni sono supportate solo nelle versioni più recenti del driver JDBC di Spanner.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

class ConnectionWithQueryOptionsExample {

  static void connectionWithQueryOptions() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    connectionWithQueryOptions(projectId, instanceId, databaseId);
  }

  static void connectionWithQueryOptions(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String optimizerVersion = "1";
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s?optimizerVersion=%s",
            projectId, instanceId, databaseId, optimizerVersion);
    try (Connection connection = DriverManager.getConnection(connectionUrl);
        Statement statement = connection.createStatement()) {
      // Execute a query using the optimizer version '1'.
      try (ResultSet rs =
          statement.executeQuery(
              "SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName")) {
        while (rs.next()) {
          System.out.printf("%d %s %s%n", rs.getLong(1), rs.getString(2), rs.getString(3));
        }
      }
      try (ResultSet rs = statement.executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
        while (rs.next()) {
          System.out.printf("Optimizer version: %s%n", rs.getString(1));
        }
      }
    }
  }
}

Puoi anche impostare la versione dello strumento di ottimizzazione delle query utilizzando l'istruzione SET OPTIMIZER_VERSION come mostrato nell'esempio seguente.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

class SetQueryOptionsExample {

  static void setQueryOptions() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    setQueryOptions(projectId, instanceId, databaseId);
  }

  static void setQueryOptions(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl);
        Statement statement = connection.createStatement()) {
      // Instruct the JDBC connection to use version '1' of the query optimizer.
      // NOTE: Use `SET SPANNER.OPTIMIZER_VERSION='1`` when connected to a PostgreSQL database.
      statement.execute("SET OPTIMIZER_VERSION='1'");
      // Execute a query using the latest optimizer version.
      try (ResultSet rs =
          statement.executeQuery(
              "SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName")) {
        while (rs.next()) {
          System.out.printf("%d %s %s%n", rs.getLong(1), rs.getString(2), rs.getString(3));
        }
      }
      // NOTE: Use `SHOW SPANNER.OPTIMIZER_VERSION` when connected to a PostgreSQL database.
      try (ResultSet rs = statement.executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
        while (rs.next()) {
          System.out.printf("Optimizer version: %s%n", rs.getString(1));
        }
      }
    }
  }
}

Per ulteriori dettagli sull'utilizzo del driver open source, vedi Utilizzo del driver JDBC open source.

Gestione delle versioni non valide dello strumento di ottimizzazione

Spanner supporta una gamma di versioni dell'ottimizzatore. Questo intervallo cambia nel tempo quando viene aggiornato lo strumento di ottimizzazione delle query. Se la versione che specifichi non rientra nell'intervallo, la query non riesce. Ad esempio, se tenti di eseguire una query con il suggerimento dell'istruzione @{OPTIMIZER_VERSION=9}, ma il numero di versione più recente dello strumento di ottimizzazione è solo 8, Spanner risponde con questo messaggio di errore:

Query optimizer version: 9 is not supported

Gestire un'impostazione non valida del pacchetto di statistiche dello strumento di ottimizzazione

Puoi bloccare il database o la query in qualsiasi pacchetto di statistiche disponibile utilizzando uno dei metodi descritti in precedenza in questa pagina. Una query non va a buon fine se viene fornito un nome pacchetto statistiche non valido. Un pacchetto di statistiche specificato da una query deve essere:

Determinare la versione dello strumento di ottimizzazione delle query utilizzata per eseguire una query

La versione dell'ottimizzatore utilizzata per una query è visibile tramite la console Google Cloud e in Google Cloud CLI.

Google Cloud console

Per visualizzare la versione dell'ottimizzatore utilizzata per una query, esegui la query nella pagina Spanner Studio della console Google Cloud , quindi seleziona la scheda Spiegazione. Dovresti vedere un messaggio simile al seguente:

Versione dello strumento di ottimizzazione delle query: 8

Interfaccia a riga di comando gcloud

Per visualizzare la versione utilizzata durante l'esecuzione di una query in gcloud CLI, imposta il flag --query-mode su PROFILE come mostrato nello snippet seguente.

gcloud spanner databases execute-sql MyDatabase --instance=test-instance \
    --query-mode=PROFILE --sql='SELECT * FROM MyTable'

Visualizzare la versione dello strumento di ottimizzazione delle query in Metrics Explorer

Cloud Monitoring raccoglie misurazioni per aiutarti a comprendere le prestazioni delle tue applicazioni e dei tuoi servizi di sistema. Una delle metriche raccolte per Spanner è il conteggio delle query, che misura il numero di query in un'istanza, campionate nel tempo. Sebbene questa metrica sia molto utile per visualizzare le query raggruppate per codice di errore, possiamo utilizzarla anche per vedere quale versione dello strumento di ottimizzazione è stata utilizzata per eseguire ogni query.

Puoi utilizzare Esplora metriche nella consoleGoogle Cloud per visualizzare il conteggio delle query per l'istanza del database. La figura 1 mostra il conteggio delle query per tre database. Puoi vedere quale versione dello strumento per ottimizzare viene utilizzata in ogni database.

La tabella sotto il grafico in questa figura mostra che my-db-1 ha tentato di eseguire una query con una versione dell'ottimizzatore non valida, restituendo lo stato Utilizzo errato e un conteggio delle query pari a 0. Gli altri database hanno eseguito query utilizzando rispettivamente le versioni 1 e 2 dello strumento di ottimizzazione.

Conteggio delle query in Metrics Explorer raggruppate per versione dello strumento di ottimizzazione delle query

Figura 1. Conteggio delle query visualizzate in Metrics Explorer con le query raggruppate per versione dell'ottimizzatore.

Per configurare un grafico simile per la tua istanza:

  1. Vai a Metrics Explorer nella console Google Cloud .
  2. Nel campo Tipo di risorsa, seleziona Cloud Spanner Instance.
  3. Nel campo Metrica, seleziona Count of queries.
  4. Nel campo Raggruppa per, seleziona database, optimizer_version e status.

In questo esempio non viene mostrato il caso in cui viene utilizzata una versione diversa dello strumento di ottimizzazione per query diverse nello stesso database. In questo caso, il grafico mostrerebbe un segmento a barre per ogni combinazione di versione del database e dell'ottimizzatore.

Per scoprire come utilizzare Cloud Monitoring per monitorare le istanze Spanner, consulta Monitoraggio con Cloud Monitoring.