管理查询优化器

本页面介绍了如何在 Spanner 中为 GoogleSQL 方言数据库和 PostgreSQL 方言数据库管理查询优化器。

Spanner 查询优化器会确定执行 SQL 查询的最有效方式。不过,当查询优化器本身发生变化或数据库统计信息更新时,优化器确定的查询计划可能会略有不同。为了最大限度地降低查询优化器或统计信息发生变化时性能下降的可能性,Spanner 提供了以下查询选项。

  • optimizer_version:对查询优化器所做的更改会打包并作为优化器版本发布。Spanner 会在最新版本的优化器发布后至少 30 天开始将该版本用作默认优化器。您可以使用查询优化器版本选项,以旧版优化器运行查询。

  • optimizer_statistics_package:Spanner 会定期更新优化器统计信息。新的统计信息以软件包的形式提供。此查询选项用于指定查询优化器在编译 SQL 查询时要使用的统计信息软件包。指定的软件包必须停用垃圾回收:

GoogleSQL

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

PostgreSQL

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

本指南介绍了如何在 Spanner 的不同范围内设置这些单独的选项。

列出查询优化器选项

Spanner 会存储您可以选择的可用优化器版本和统计信息软件包的相关信息。

优化器版本

查询优化器版本是一个整数值,每次更新时递增 1。查询优化器的最新版本为 8

执行以下 SQL 语句以返回所有受支持的优化器版本的列表及其相应的发布日期,以及该版本是否为默认版本。返回的最大版本号是支持的优化器最新版本。

SELECT * FROM SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS; 

默认版本

默认情况下,Spanner 会在最新版本的优化器发布后至少 30 天开始使用该版本。从新版本到默认版本为期 30 天的宽限期内,建议您根据新版本测试查询,以检测是否有任何回归问题。

如需查找默认版本,请执行以下 SQL 语句:

SELECT * FROM SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS; 

查询将返回所有受支持的优化器版本的列表。IS_DEFAULT 列指定的是当前默认版本。

如需详细了解每个版本,请参阅查询优化器版本版本记录

优化器统计信息软件包

Spanner 创建的每个新的优化器统计信息软件包都分配有一个软件包名称,该名称在给定数据库中必须是唯一的。

软件包名称的格式为 auto_{PACKAGE_TIMESTAMP}UTC。在 GoogleSQL 中,ANALYZE 语句会触发创建统计信息软件包名称。在 PostgreSQL 中,ANALYZE 语句会执行此任务。统计信息软件包名称的格式为 analyze_{PACKAGE_TIMESTAMP}UTC,其中 {PACKAGE_TIMESTAMP} 是统计信息构建开始时的 UTC 时区时间戳。执行以下 SQL 语句以返回所有可用的优化器统计信息软件包的列表。

SELECT * FROM INFORMATION_SCHEMA.SPANNER_STATISTICS; 

默认情况下,除非数据库或查询使用本页面上所述的某个方法固定到旧版软件包,否则 Spanner 默认使用最新的优化器统计信息软件包。

选项替换优先级

如果您使用的是 GoogleSQL 方言数据库,Spanner 提供了多种更改优化器选项的方法。例如,您可以为特定查询设置选项,或在客户端库的进程级层或查询级层配置选项。如果以多种方式设置选项,则遵循以下优先顺序。(选择链接以转到本文档中的相应部分)。

Spanner 默认 ← 数据库选项客户端应用环境变量客户端查询语句提示

例如,下方展示了如何在设置查询优化器版本时解读优先级顺序:

创建数据库时,数据库将使用 Spanner 默认优化器版本。使用上面列出的方法之一设置优化器版本的优先级高于其左侧的任何内容。例如,使用环境变量为应用设置优化器的优先级高于您使用数据库选项为数据库设置的任何值。对于一个给定的查询,通过语句提示设置优化器版本的优先级最高,优先于使用任何其他方法设置的值。

以下部分详细介绍了每种方法。

在数据库级层设置优化器选项

如需在数据库上设置默认优化器版本,请使用以下 ALTER DATABASE DDL 命令。设置此选项并不要求所有查询都运行该版本。而是为查询所用的 QO 版本设置上限。其预期用途是缓解在发布新版优化器后出现的回归问题。

GoogleSQL

ALTER DATABASE MyDatabase SET OPTIONS (optimizer_version =  8); 

PostgreSQL

ALTER DATABASE MyDatabase SET spanner.optimizer_version = 5; 

您可以以类似方式设置统计信息软件包,如以下示例所示。

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"; 

您还可以同时设置多个选项,如以下 DDL 命令所示。

GoogleSQL

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

您可以使用 gcloud CLI databases ddl update 命令在 gcloud CLI 中运行 ALTER DATABASE,如下所示。

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'

将数据库选项设置为 NULL(在 GoogleSQL 中)或 DEFAULT(在 PostgreSQL 中)可清除该选项,以便使用默认值。

如需查看数据库这些选项的当前值,请查询 GoogleSQL 的 INFORMATION_SCHEMA.DATABASE_OPTIONS 视图或 PostgreSQL 的 information_schema database_options 表,如下所示。

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

使用客户端库设置优化器选项

通过客户端库以编程方式与 Spanner 进行交互时,可以通过多种方式更改客户端应用的查询选项。

您必须使用最新版本的客户端库来设置优化器选项。

为数据库客户端设置优化器选项

应用可以通过配置查询选项属性,在客户端库上全局设置优化器选项,如以下代码段所示。优化器设置存储在客户端实例中,并应用于在客户端生命周期内运行的所有查询。即使选项在后端的数据库级层应用,但当选项在客户端级层设置时,它们会应用于连接到该客户端的所有数据库。

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

使用环境变量设置优化器选项

如需更轻松地尝试不同的优化器设置,而无需重新编译应用,您可以设置 SPANNER_OPTIMIZER_VERSIONSPANNER_OPTIMIZER_STATISTICS_PACKAGE 环境变量并运行应用,如以下代码段所示。

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"

在客户端初始化时,系统会读取指定的查询优化器选项值并将其存储在客户端实例中,还会将其应用于客户端生命周期内运行的所有查询。

为客户端查询设置优化器选项

通过在构建查询时指定查询选项属性,您可以在客户端应用的查询级层为优化器版本或统计信息软件包版本指定值。

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

使用语句提示为查询设置优化器选项

语句提示是对查询语句的提示,会更改查询执行的默认行为。在语句上设置 OPTIMIZER_VERSION 提示会强制使用指定的查询优化器版本运行该查询。

OPTIMIZER_VERSION 提示具有最高的优化器版本优先级。如果指定了语句提示,则将使用该提示,而不考虑其他所有优化器版本设置。

GoogleSQL

@{OPTIMIZER_VERSION=8} SELECT * FROM MyTable; 

PostgreSQL

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

您还可以使用 latest_version 字面量将查询的优化器版本设置为最新版本,如此处所示。

GoogleSQL

@{OPTIMIZER_VERSION=latest_version} SELECT * FROM MyTable; 

PostgreSQL

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

在语句上设置 OPTIMIZER_STATISTICS_PACKAGE 提示会强制使用指定的查询优化器统计信息软件包版本运行该查询。指定的软件包必须停用垃圾回收

GoogleSQL

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

PostgreSQL

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

OPTIMIZER_STATISTICS_PACKAGE 提示具有最高的优化器软件包设置优先级。如果指定了语句提示,则将使用该提示,而不考虑其他所有优化器软件包版本设置。

@{OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC} SELECT * FROM MyTable; 

您还可以通过 latest 字面量来使用最新的统计信息软件包。

@{OPTIMIZER_STATISTICS_PACKAGE=latest} SELECT * FROM MyTable; 

这两个提示都可以在单个语句中设置,如以下示例所示。

default_version 字面量会将查询的优化器版本设置为默认版本,此版本可能与最新版本不同。如需了解详情,请参阅默认版本

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; 

使用 Spanner JDBC 驱动程序时设置优化器选项

如以下示例所示,您可以通过在 JDBC 连接字符串中指定选项来替换优化器版本和统计信息软件包的默认值。

这些选项仅在最新版本的 Spanner JDBC 驱动程序中受支持。

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

您还可以使用 SET OPTIMIZER_VERSION 语句设置查询优化器版本,如以下示例所示。

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

如需详细了解如何使用开源驱动程序,请参阅使用开源 JDBC 驱动程序

无效优化器版本的处理方式

Spanner 支持一系列范围的优化器版本。当查询优化器更新时,此范围会随之发生变化。如果您指定的版本超出范围,查询将失败。例如,如果您尝试运行包含语句提示 @{OPTIMIZER_VERSION=9} 的查询,但最新的优化器版本号仅为 8,Spanner 会返回以下错误消息:

Query optimizer version: 9 is not supported

处理无效的优化器统计信息软件包设置

您可以使用本页面上前面介绍过的任一方法,将数据库或查询固定到任何可用的统计信息软件包。如果提供的统计信息软件包名称无效,查询会失败。查询指定的统计信息软件包必须:

确定用于运行查询的查询优化器版本

您可以通过 Google Cloud 控制台和 Google Cloud CLI 查看用于查询的优化器版本。

Google Cloud 控制台

如需查看用于查询的优化器版本,请在 Google Cloud 控制台的 Spanner Studio 页面中运行查询,然后选择说明标签页。您应该会看到如下所示的消息:

查询优化器版本:8

gcloud CLI

要查看在 gcloud CLI 中运行查询时使用的版本,请按照以下代码段所示将 --query-mode 标志设置为 PROFILE

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

在 Metrics Explorer 中直观呈现查询优化器版本

Cloud Monitoring 会收计量结果,以帮助您了解应用和系统服务的性能。为 Spanner 收集的指标之一是查询计数,用于衡量实例中的查询数量(按时间进行采样)。此指标可帮助我们查看按错误代码分组的查询,此外,我们还可以使用它来查看运行每个查询的优化器版本。

您可以使用Google Cloud 控制台中的 Metrics Explorer 来直观呈现数据库实例的查询计数。图 1 显示了三个数据库的查询计数。您可以查看每个数据库中使用的优化器版本。

此图中图表下方的表格显示 my-db-1 尝试使用无效的优化器版本运行查询,返回 Bad usage 状态,并导致查询计数为 0。其他数据库分别使用优化器版本 1 和版本 2 运行查询。

Metrics Explorer 中按查询优化器版本分组的查询计数

图 1:Metrics Explorer 中显示的按优化器版本分组的查询的查询计数

要为您的实例设置类似的图表,请执行以下操作:

  1. 在 Google Cloud 控制台中前往 Metrics Explorer
  2. 资源类型字段中,选择 Cloud Spanner Instance
  3. 指标字段中,选择 Count of queries
  4. 分组依据字段中,选择 databaseoptimizer_versionstatus

本示例中未显示同一数据库的不同查询使用不同优化器版本的情况。在这种情况下,图表会针对数据库和优化器的每个组合显示条形图。

如需了解如何使用 Cloud Monitoring 监控 Spanner 实例,请参阅使用 Cloud Monitoring 进行监控