Leituras direcionadas

Nesta página, descrevemos as leituras direcionadas do Spanner e como usá-las.

As leituras direcionadas no Spanner oferecem a flexibilidade de rotear transações somente leitura e leituras únicas para um tipo ou região de réplica específica em uma configuração de instância multirregional ou uma configuração regional personalizada com regiões somente leitura opcionais.

Casos de uso comuns

Alguns casos de uso comuns para leituras direcionadas incluem:

  • Faça o balanceamento de carga de cargas de trabalho em várias regiões para alcançar uma utilização mais uniforme da CPU.
  • Isolar a análise no seu aplicativo minimizando o impacto nas cargas de trabalho transacionais.
  • fornecer isolamento parcial para leituras de streams de alterações; Se você usar a API Spanner para fazer streaming de alterações de dados do Spanner, será possível minimizar o impacto nas cargas de trabalho transacionais usando leituras direcionadas.

Operações de consulta compatíveis

Operações de consulta Leituras direcionadas são compatíveis?
Leitura desatualizada Sim
Leitura forte Sim
Transação de leitura/gravação No

As leituras direcionadas não são compatíveis com transações de leitura e gravação e tipos de DML particionada de atualizações em massa. Isso ocorre porque as transações de leitura e gravação precisam ser processadas na região líder. Se leituras direcionadas forem usadas em uma transação de leitura/gravação, a transação falhará com um erro BAD_REQUEST.

Limitações

As leituras direcionadas do Spanner têm as seguintes limitações:

  • Só é possível usar leituras direcionadas em uma instância do Spanner que esteja em uma configuração de instância multirregional ou uma configuração regional personalizada com regiões somente leitura opcionais.
  • Não é possível usar leituras direcionadas com solicitações de leitura/gravação porque elas são sempre atendidas pela região líder.
  • Não é possível usar leituras direcionadas no console do Google Cloud ou na Google Cloud CLI. Ela está disponível usando as APIs REST e RPC e as bibliotecas de cliente do Spanner.
  • É possível especificar no máximo 10 réplicas em uma única leitura direcionada.
  • Se você usar o painel de insights do sistema ou o Metrics Explorer, nos gráficos de Operações por segundo, Operações por segundo por banco de dados e Operações por segundo por método da API, talvez sua solicitação de leituras direcionadas seja exibida em um local diferente do designado. Nesse caso, as informações dos gráficos podem estar incorretas. Confirme se as solicitações de leitura são roteadas para as regiões escolhidas usando as métricas de utilização da CPU. Essas métricas mostram as informações mais precisas sobre para onde o tráfego de leituras direcionadas é roteado.

Antes de começar

Considere o seguinte antes de usar leituras direcionadas:

  • O aplicativo poderá gerar mais latência se você estiver roteando leituras para uma réplica ou região diferente da mais próxima do aplicativo.
  • É possível rotear o tráfego com base em:
    • Nome da região (por exemplo: us-central1).
    • Tipo de réplica (valores possíveis: READ_ONLY e READ_WRITE).
  • A opção de failover automático em leituras direcionadas é ativada por padrão. Quando a opção de failover automático está ativada e todas as réplicas especificadas estão indisponíveis ou não íntegras, o Spanner encaminha solicitações para uma réplica fora da lista includeReplicas. Se você desativar a opção de failover automático e todas as réplicas especificadas estiverem indisponíveis ou não íntegras, a solicitação de leituras direcionadas falhará.

Parâmetros de leitura direta

Se você estiver usando a API REST ou RPC para executar leituras direcionadas, defina esses campos no parâmetro directedReadOptions. Só é possível incluir um de includeReplicas ou excludeReplicas, não ambos.

  • includeReplicas: contém um conjunto repetido de replicaSelections. Essa lista indica a ordem em que as leituras direcionadas a regiões ou tipos de réplica específicos precisam ser consideradas. É possível especificar no máximo 10 includeReplicas.
    • replicaSelections: consiste em location ou réplica type que atende à solicitação de leituras direcionadas. Se você usar includeReplicas, precisará fornecer pelo menos um dos seguintes campos:
      • location: o local que atende à solicitação de leituras direcionadas. O local precisa ser uma das regiões na configuração multirregional do seu banco de dados. Se o local não for uma das regiões na configuração multirregional do seu banco de dados, as solicitações não serão roteadas como esperado. Em vez disso, eles são atendidos pela região mais próxima. Por exemplo, é possível direcionar leituras para o local us-central1 em um banco de dados na configuração da instância multirregional nam6.
      • type: o tipo de réplica que atende à solicitação de leituras direcionadas. Os tipos possíveis incluem READ_WRITE e READ_ONLY.
    • autoFailoverDisabled: por padrão, é definido como False, o que significa que o failover automático está ativado. Quando a opção de failover automático está ativada e todas as réplicas especificadas estão indisponíveis ou não íntegras, o Spanner encaminha solicitações para uma réplica fora da lista includeReplicas. Se você desativar a opção de failover automático e todas as réplicas especificadas estiverem indisponíveis ou não íntegras, a solicitação de leituras direcionadas falhará. Os valores possíveis incluem TRUE para desativado e FALSE para ativado.
  • excludeReplicas: contém um conjunto repetido de replicaSelections que é excluído das solicitações de veiculação. O Spanner não encaminha solicitações para as réplicas dessa lista.
    • replicaSelections: o local ou o tipo de réplica que foi excluído de atender à solicitação de leituras direcionadas. Se você usa excludeReplicas, é necessário fornecer pelo menos um dos seguintes campos:
      • location: o local que foi excluído da veiculação da solicitação de leituras direcionadas.
      • type: o tipo de réplica que não pode atender à solicitação de leituras direcionadas. Os tipos possíveis incluem READ_WRITE e READ_ONLY.

Para ver um exemplo de corpo de solicitação REST, clique na guia REST na seção Usar leituras direcionadas.

Usar leituras direcionadas

É possível usar as bibliotecas de cliente do Spanner e as APIs REST e RPC para executar leituras direcionadas.

Bibliotecas de cliente

C++

void DirectedRead(std::string const& project_id, std::string const& instance_id,
                  std::string const& database_id) {
  namespace spanner = ::google::cloud::spanner;

  // Create a client with a DirectedReadOption.
  auto client = spanner::Client(
      spanner::MakeConnection(
          spanner::Database(project_id, instance_id, database_id)),
      google::cloud::Options{}.set<spanner::DirectedReadOption>(
          spanner::ExcludeReplicas({spanner::ReplicaSelection("us-east4")})));

  spanner::SqlStatement select(
      "SELECT SingerId, AlbumId, AlbumTitle FROM Albums");
  using RowType = std::tuple<std::int64_t, std::int64_t, std::string>;

  // A DirectedReadOption on the operation will override the option set
  // at the client level.
  auto rows = client.ExecuteQuery(
      std::move(select),
      google::cloud::Options{}.set<spanner::DirectedReadOption>(
          spanner::IncludeReplicas(
              {spanner::ReplicaSelection(spanner::ReplicaType::kReadWrite)},
              /*auto_failover_disabled=*/true)));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row)
              << " AlbumId: " << std::get<1>(*row)
              << " AlbumTitle: " << std::get<2>(*row) << "\n";
  }
  std::cout << "Read completed for [spanner_directed_read]\n";
}

C#


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

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

    public async Task<List<Album>> DirectedReadsAsync(string projectId, string instanceId, string databaseId)
    {
        string 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");
        // Set directed read options on a query or read command.
        cmd.DirectedReadOptions = new DirectedReadOptions
        {
            IncludeReplicas = new DirectedReadOptions.Types.IncludeReplicas
            {
                AutoFailoverDisabled = true,
                ReplicaSelections =
                {
                    new DirectedReadOptions.Types.ReplicaSelection
                    {
                        Location = "us-central1",
                        Type = DirectedReadOptions.Types.ReplicaSelection.Types.Type.ReadOnly
                    }
                }
            }
        };

        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"

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

//	Shows how to run a query with directed read options.
//	Only one of ExcludeReplicas or IncludeReplicas can be set
//	Each accepts a list of ReplicaSelections which contains Location and Type
//	* `location` - The location must be one of the regions within the
//	multi-region configuration of your database.
//	* `type` - The type of the replica
//	Some examples of using replica_selectors are:
//	* `location:us-east1` --> The "us-east1" replica(s) of any available type
//		will be used to process the request.
//	* `type:READ_ONLY`    --> The "READ_ONLY" type replica(s) in nearest
//	available location will be used to process the
//	request.
//	* `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)
//	in location "us-east1" will be used to process the request.
//		IncludeReplicas also contains an option for AutoFailoverDisabled which when set
//	Spanner will not route requests to a replica outside the
//	IncludeReplicas list when all the specified replicas are unavailable
//	or unhealthy. The default value is `false`

func directedReadOptions(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()
	directedReadOptionsForClient := &sppb.DirectedReadOptions{
		Replicas: &sppb.DirectedReadOptions_ExcludeReplicas_{
			ExcludeReplicas: &sppb.DirectedReadOptions_ExcludeReplicas{
				ReplicaSelections: []*sppb.DirectedReadOptions_ReplicaSelection{
					{
						Location: "us-east4",
					},
				},
			},
		},
	}
	// DirectedReadOptions can be set at client level and will be used in all read-only transaction requests
	client, err := spanner.NewClientWithConfig(ctx, db, spanner.ClientConfig{DirectedReadOptions: directedReadOptionsForClient})
	if err != nil {
		return err
	}
	defer client.Close()

	// DirectedReadOptions set at Request level will override the options set at Client level.
	directedReadOptionsForRequest := &sppb.DirectedReadOptions{
		Replicas: &sppb.DirectedReadOptions_IncludeReplicas_{
			IncludeReplicas: &sppb.DirectedReadOptions_IncludeReplicas{
				ReplicaSelections: []*sppb.DirectedReadOptions_ReplicaSelection{
					{
						Type: sppb.DirectedReadOptions_ReplicaSelection_READ_ONLY,
					},
				},
				AutoFailoverDisabled: true,
			},
		},
	}

	statement := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	// // Read rows while passing directedReadOptions directly to the query.
	iter := client.Single().QueryWithOptions(ctx, statement, spanner.QueryOptions{DirectedReadOptions: directedReadOptionsForRequest})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %d %s\n", singerID, albumID, albumTitle)
	}
}

Java

import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Options;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.spanner.v1.DirectedReadOptions;
import com.google.spanner.v1.DirectedReadOptions.ExcludeReplicas;
import com.google.spanner.v1.DirectedReadOptions.IncludeReplicas;
import com.google.spanner.v1.DirectedReadOptions.ReplicaSelection;

public class DirectedReadSample {
  static void directedRead() {
    // TODO(developer): Replace these variables before running the sample.
    final String projectId = "my-project";
    final String instanceId = "my-instance";
    final String databaseId = "my-database";
    directedRead(projectId, instanceId, databaseId);
  }

  static void directedRead(String projectId, String instanceId, String databaseId) {
    // Only one of excludeReplicas or includeReplicas can be set
    // Each accepts a list of replicaSelections which contains location and type
    //   * `location` - The location must be one of the regions within the
    //      multi-region configuration of your database.
    //   * `type` - The type of the replica
    // Some examples of using replicaSelectors are:
    //   * `location:us-east1` --> The "us-east1" replica(s) of any available type
    //                             will be used to process the request.
    //   * `type:READ_ONLY`    --> The "READ_ONLY" type replica(s) in nearest
    // .                            available location will be used to process the
    //                             request.
    //   * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)
    //                          in location "us-east1" will be used to process
    //                          the request.
    //  includeReplicas also contains an option called autoFailoverDisabled, which when set to true
    //  will instruct Spanner to not route requests to a replica outside the
    //  includeReplicas list when all the specified replicas are unavailable
    //  or unhealthy. Default value is `false`.
    final DirectedReadOptions directedReadOptionsForClient =
        DirectedReadOptions.newBuilder()
            .setExcludeReplicas(
                ExcludeReplicas.newBuilder()
                    .addReplicaSelections(
                        ReplicaSelection.newBuilder().setLocation("us-east4").build())
                    .build())
            .build();

    // You can set default `DirectedReadOptions` for a Spanner client. These options will be applied
    // to all read-only transactions that are executed by this client, unless specific
    // DirectedReadOptions are set for a query.
    // Directed read can only be used for read-only transactions. The default options will be
    // ignored for any read/write transaction that the client executes.
    try (Spanner spanner =
        SpannerOptions.newBuilder()
            .setProjectId(projectId)
            .setDirectedReadOptions(directedReadOptionsForClient)
            .build()
            .getService()) {
      final DatabaseClient dbClient =
          spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId));

      // DirectedReadOptions at request level will override the options set at
      // client level (through SpannerOptions).
      final DirectedReadOptions directedReadOptionsForRequest =
          DirectedReadOptions.newBuilder()
              .setIncludeReplicas(
                  IncludeReplicas.newBuilder()
                      .addReplicaSelections(
                          ReplicaSelection.newBuilder()
                              .setType(ReplicaSelection.Type.READ_WRITE)
                              .build())
                      .setAutoFailoverDisabled(true)
                      .build())
              .build();

      // Read rows while passing DirectedReadOptions directly to the query.
      try (ResultSet rs =
          dbClient
              .singleUse()
              .executeQuery(
                  Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),
                  Options.directedRead(directedReadOptionsForRequest))) {
        while (rs.next()) {
          System.out.printf(
              "SingerId: %d, AlbumId: %d, AlbumTitle: %s\n",
              rs.getLong(0), rs.getLong(1), rs.getString(2));
        }
        System.out.println("Successfully executed read-only transaction with directedReadOptions");
      }
    }
  }
}

Node.js

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

// Only one of excludeReplicas or includeReplicas can be set
// Each accepts a list of replicaSelections which contains location and type
//   * `location` - The location must be one of the regions within the
//      multi-region configuration of your database.
//   * `type` - The type of the replica
// Some examples of using replicaSelectors are:
//   * `location:us-east1` --> The "us-east1" replica(s) of any available type
//                             will be used to process the request.
//   * `type:READ_ONLY`    --> The "READ_ONLY" type replica(s) in nearest
//.                            available location will be used to process the
//                             request.
//   * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)
//                          in location "us-east1" will be used to process
//                          the request.
//  includeReplicas also contains an option for autoFailover which when set
//  Spanner will not route requests to a replica outside the
//  includeReplicas list when all the specified replicas are unavailable
//  or unhealthy. The default value is `false`
const directedReadOptionsForClient = {
  excludeReplicas: {
    replicaSelections: [
      {
        location: 'us-east4',
      },
    ],
  },
};

// Instantiates a client with directedReadOptions
const spanner = new Spanner({
  projectId: projectId,
  directedReadOptions: directedReadOptionsForClient,
});

async function spannerDirectedReads() {
  // Gets a reference to a Cloud Spanner instance and backup
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);
  const directedReadOptionsForRequest = {
    includeReplicas: {
      replicaSelections: [
        {
          type: protos.google.spanner.v1.DirectedReadOptions.ReplicaSelection
            .Type.READ_ONLY,
        },
      ],
      autoFailoverDisabled: true,
    },
  };

  await database.getSnapshot(async (err, transaction) => {
    if (err) {
      console.error(err);
      return;
    }
    try {
      // Read rows while passing directedReadOptions directly to the query.
      // These will override the options passed at Client level.
      const [rows] = await transaction.run({
        sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
        directedReadOptions: directedReadOptionsForRequest,
      });
      rows.forEach(row => {
        const json = row.toJSON();
        console.log(
          `SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`
        );
      });
      console.log(
        'Successfully executed read-only transaction with directedReadOptions'
      );
    } catch (err) {
      console.error('ERROR:', err);
    } finally {
      transaction.end();
      // Close the database when finished.
      await database.close();
    }
  });
}
spannerDirectedReads();

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType;

/**
 * Queries sample data from the database with directed read options.
 * Example:
 * ```
 * directed_read($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function directed_read(string $instanceId, string $databaseId): void
{
    $directedReadOptionsForClient = [
        'directedReadOptions' => [
            'excludeReplicas' => [
                'replicaSelections' => [
                    [
                        'location' => 'us-east4'
                    ]
                ]
            ]
        ]
    ];

    $directedReadOptionsForRequest = [
        'directedReadOptions' => [
            'includeReplicas' => [
                'replicaSelections' => [
                    [
                        'type' => ReplicaType::READ_WRITE
                    ]
                ],
                'autoFailoverDisabled' => true
            ]
        ]
    ];

    $spanner = new SpannerClient($directedReadOptionsForClient);
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);
    $snapshot = $database->snapshot();

    // directedReadOptions at Request level will override the options set at
    // Client level
    $results = $snapshot->execute(
        'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
        $directedReadOptionsForRequest
    );

    foreach ($results as $row) {
        printf('SingerId: %s, AlbumId: %s, AlbumTitle: %s' . PHP_EOL,
            $row['SingerId'], $row['AlbumId'], $row['AlbumTitle']);
    }
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"

directed_read_options_for_client = {
    "exclude_replicas": {
        "replica_selections": [
            {
                "location": "us-east4",
            },
        ],
    },
}

# directed_read_options can be set at client level and will be used in all
# read-only transaction requests
spanner_client = spanner.Client(
    directed_read_options=directed_read_options_for_client
)
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

directed_read_options_for_request = {
    "include_replicas": {
        "replica_selections": [
            {
                "type_": DirectedReadOptions.ReplicaSelection.Type.READ_ONLY,
            },
        ],
        "auto_failover_disabled": True,
    },
}

with database.snapshot() as snapshot:
    # Read rows while passing directed_read_options directly to the query.
    # These will override the options passed at Client level.
    results = snapshot.execute_sql(
        "SELECT SingerId, AlbumId, AlbumTitle FROM Albums",
        directed_read_options=directed_read_options_for_request,
    )

    for row in results:
        print("SingerId: {}, AlbumId: {}, AlbumTitle: {}".format(*row))

Ruby

require "google/cloud/spanner"

##
# This is a snippet for showcasing how to pass in directed read options.
#
# @param project_id  [String] The ID of the Google Cloud project.
# @param instance_id [String] The ID of the spanner instance.
# @param database_id [String] The ID of the database.
#
def spanner_directed_read project_id:, instance_id:, database_id:
  # Only one of exclude_replicas or include_replicas can be set.
  # Each accepts a list of replica_selections which contains location and type
  #   * `location` - The location must be one of the regions within the
  #      multi-region configuration of your database.
  #   * `type` - The type of the replica
  # Some examples of using replicaSelectors are:
  #   * `location:us-east1` --> The "us-east1" replica(s) of any available type
  #                             will be used to process the request.
  #   * `type:READ_ONLY`    --> The "READ_ONLY" type replica(s) in the nearest
  # .                            available location will be used to process the
  #                             request.
  #   * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)
  #                          in location "us-east1" will be used to process
  #                          the request.
  #  include_replicas also contains an option for auto_failover_disabled. If set
  #  Spanner will not route requests to a replica outside the
  #  include_replicas list even if all the specified replicas are
  #  unavailable or unhealthy. The default value is `false`.
  directed_read_options_for_client = {
    include_replicas: {
      replica_selections: [{ location: "us-east4" }]
    }
  }

  # Instantiates a client with directedReadOptions
  spanner = Google::Cloud::Spanner.new project: project_id
  client  = spanner.client instance_id, database_id, directed_read_options: directed_read_options_for_client

  directed_read_options = {
    include_replicas: {
      replica_selections: [{ type: "READ_WRITE" }],
      auto_failover_disabled: true
    }
  }

  result = client.execute_sql "SELECT SingerId, AlbumId, AlbumTitle FROM Albums", directed_read_options: directed_read_options
  result.rows.each do |row|
    puts "SingerId: #{row[:SingerId]}"
    puts "AlbumId: #{row[:AlbumId]}"
    puts "AlbumTitle: #{row[:AlbumTitle]}"
  end
  puts "Successfully executed read-only transaction with directed_read_options"
end

REST

É possível usar as seguintes APIs REST para executar leituras direcionadas:

Por exemplo, para executar leituras direcionadas em us-central1 usando executeSQL:

  1. Clique em projects.instances.databases.sessions.executeSql.

  2. Para a sessão, digite:

    projects/<VAR>PROJECT-ID</VAR>/instances/<VAR>INSTANCE-ID</VAR>/databases/<VAR>DATABASE-ID</VAR>/sessions/<VAR>SESSION-ID</VAR>
    

    Substitua:

    • PROJECT-ID: o ID do projeto;
    • INSTANCE-ID: o ID da instância
    • DATABASE-ID: o ID do banco de dados.
    • SESSION-ID: o ID da sessão. Você recebe o valor SESSION-ID ao criar uma sessão.
  3. Para o Corpo de solicitação, use o seguinte:

    {
      "directedReadOptions": {
        "includeReplicas": {
          "replicaSelections": [
            {
              "location": "us-central1",
            }
          ]
        }
      },
      "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums"
    }
    
  4. Clique em Executar. A resposta exibe os resultados da consulta.

RPC

É possível usar as seguintes APIs RPC para executar leituras direcionadas:

A seguir