Guia de latência de ponta a ponta do Cloud Spanner

Esta página:

  • Descreve os componentes de alto nível envolvidos em uma solicitação da API Cloud Spanner.
  • Explica como extrair, capturar e visualizar latências associadas a esses componentes para conhecer a origem das latências.

Visão geral

Diagrama da arquitetura do Cloud Spanner

Os componentes de alto nível usados para fazer uma solicitação da API Cloud Spanner incluem o seguinte:

  1. Bibliotecas de cliente do Cloud Spanner. Essas bibliotecas fornecem uma camada de abstração sobre o gRPC e gerenciam os detalhes do gerenciamento da sessão, a execução de transações, novas tentativas e muito mais.

  2. Google Front End Esse serviço de infraestrutura é comum a todos os serviços do Google Cloud, incluindo o Cloud Spanner. O Google Front End garante que todas as conexões TLS sejam encerradas e aplica proteções contra ataques de negação de serviço. Para saber mais sobre o Google Front End, consulte Google Front End Service.

  3. Front-end da API Cloud Spanner, que executa várias verificações na solicitação da API, incluindo verificações de autenticação, autorização e cota, e mantém sessões e estados de transação.

  4. Banco de dados do Cloud Spanner, que executa a execução de leituras e gravações no banco de dados.

Ao fazer uma chamada de procedimento remoto para o Cloud Spanner, a solicitação da API é preparada pelas bibliotecas de cliente do Cloud Spanner e passa pelo Google Front End e pelo front-end da API Cloud Spanner antes de chegar ao banco de dados do Cloud Spanner.

Ao medir e comparar as latências de solicitação de componentes diferentes no banco de dados, você saberá qual deles está causando o problema.

A latência de cada componente é explicada nas seções a seguir.

Latência de ida e volta do cliente

Diagrama da arquitetura do Cloud Spanner com latência de ida e volta do cliente

Essa latência é o período (em milissegundos) entre o primeiro byte da solicitação da API Cloud Spanner que é enviado pelo cliente para o banco de dados (por meio do Google Front End e do Cloud Spanner API Front End) e o último byte do resposta que o cliente recebe do banco de dados.

As bibliotecas de cliente do Cloud Spanner fornecem estatísticas e traces com o uso do framework de instrumentação OpenCensus. Essa estrutura oferece insights sobre os componentes internos do cliente e ajuda na solução de problemas de latência de ponta a ponta (ida e volta).

Por padrão, o framework está desativado. Para saber como ativar essa estrutura, consulte Capturar latência de ida e volta do cliente.

A métrica grpc.io/client/roundtrip_latency fornece o tempo entre o primeiro byte da solicitação de API enviada ao último byte da resposta recebida.

Capturar a latência de ida e volta do cliente

É possível capturar a latência de ida e volta do cliente para os seguintes idiomas:

Java

static void captureGrpcMetric(DatabaseClient dbClient) {
  // Register basic gRPC views.
  RpcViews.registerClientGrpcBasicViews();

  // Enable OpenCensus exporters to export metrics to Stackdriver Monitoring.
  // Exporters use Application Default Credentials to authenticate.
  // See https://developers.google.com/identity/protocols/application-default-credentials
  // for more details.
  try {
    StackdriverStatsExporter.createAndRegister();
  } catch (IOException | IllegalStateException e) {
    System.out.println("Error during StackdriverStatsExporter");
  }

  try (ResultSet resultSet =
      dbClient
          .singleUse() // Execute a single read or query against Cloud Spanner.
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Go


import (
	"context"
	"fmt"
	"io"
	"regexp"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/plugin/ocgrpc"
	"go.opencensus.io/stats/view"
)

var validDatabasePattern = regexp.MustCompile("^projects/(?P<project>[^/]+)/instances/(?P<instance>[^/]+)/databases/(?P<database>[^/]+)$")

func queryWithGRPCMetric(w io.Writer, db string) error {
	projectID, _, _, err := parseDatabaseName(db)
	if err != nil {
		return err
	}

	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	// Register OpenCensus views.
	if err := view.Register(ocgrpc.DefaultClientViews...); err != nil {
		return err
	}

	// Create OpenCensus Stackdriver exporter.
	sd, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: projectID,
	})
	if err != nil {
		return err
	}
	// It is imperative to invoke flush before your main function exits
	defer sd.Flush()

	// Start the metrics exporter
	sd.StartMetricsExporter()
	defer sd.StopMetricsExporter()

	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	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 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)
	}
}

func parseDatabaseName(databaseUri string) (project, instance, database string, err error) {
	matches := validDatabasePattern.FindStringSubmatch(databaseUri)
	if len(matches) == 0 {
		return "", "", "", fmt.Errorf("failed to parse database name from %q according to pattern %q",
			databaseUri, validDatabasePattern.String())
	}
	return matches[1], matches[2], matches[3], nil
}

Visualizar a latência de ida e volta do cliente

Depois de recuperar as métricas, é possível visualizar a latência de ida e volta do cliente no Cloud Monitoring.

Veja um exemplo de um gráfico que ilustra a métrica de latência de ida e volta do cliente. O programa cria uma visualização do OpenCensus chamada roundtrip_latency. Essa string se torna parte do nome da métrica quando é exportada para o Cloud Monitoring.

Latência de ida e volta do cliente do Cloud Monitoring

Figura 1. Gráfico de latência de ida e volta do cliente no Cloud Monitoring

Latência do Google Front End

Diagrama da arquitetura do Cloud Spanner para latência do Google Front End

Essa latência é o período (em milissegundos) entre o momento em que a rede do Google recebe uma chamada de procedimento remoto do cliente e quando o primeiro byte da resposta é recebido pelo Google Front End. Essa latência não inclui handshakes de TCP/SSL.

Cada resposta do Cloud Spanner, seja REST ou gRPC, inclui um cabeçalho que contém o tempo total entre o Google Front End e o back-end (o serviço Cloud Spanner) para a solicitação e a resposta. Isso ajuda a distinguir melhor a origem da latência entre o cliente e a rede do Google.

A métrica cloud.google.com/[language]/spanner/gfe_latency captura e expõe a latência do Google Front End para as solicitações do Cloud Spanner.

Capturar a latência do Google Front End

É possível capturar a latência do Google Front End para os seguintes idiomas:

Java

private static final String MILLISECOND = "ms";
private static final TagKey key = TagKey.create("grpc_client_method");

// GFE t4t7 latency extracted from server-timing header.
public static final MeasureLong SPANNER_GFE_LATENCY =
    MeasureLong.create(
        "cloud.google.com/java/spanner/gfe_latency",
        "Latency between Google's network receives an RPC and reads back the first byte of the"
            + " response",
        MILLISECOND);

static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM =
    Distribution.create(BucketBoundaries.create(Arrays.asList(
        0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
        16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
        300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
        100000.0)));
static final View GFE_LATENCY_VIEW = View
    .create(Name.create("cloud.google.com/java/spanner/gfe_latency"),
        "Latency between Google's network receives an RPC and reads back the first byte of the"
            + " response",
        SPANNER_GFE_LATENCY,
        AGGREGATION_WITH_MILLIS_HISTOGRAM,
        Collections.singletonList(key));

static ViewManager manager = Stats.getViewManager();

private static final Tagger tagger = Tags.getTagger();
private static final StatsRecorder STATS_RECORDER = Stats.getStatsRecorder();

static void captureGfeMetric(DatabaseClient dbClient) {
  // Register GFE view.
  manager.registerView(GFE_LATENCY_VIEW);

  // Enable OpenCensus exporters to export metrics to Stackdriver Monitoring.
  // Exporters use Application Default Credentials to authenticate.
  // See https://developers.google.com/identity/protocols/application-default-credentials
  // for more details.
  try {
    StackdriverStatsExporter.createAndRegister();
  } catch (IOException | IllegalStateException e) {
    System.out.println("Error during StackdriverStatsExporter");
  }

  try (ResultSet resultSet =
      dbClient
          .singleUse() // Execute a single read or query against Cloud Spanner.
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

private static final HeaderClientInterceptor interceptor = new HeaderClientInterceptor();
private static final Metadata.Key<String> SERVER_TIMING_HEADER_KEY =
    Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER);
// Every response from Cloud Spanner, there will be an additional header that contains the total
// elapsed time on GFE. The format is "server-timing: gfet4t7; dur=[GFE latency in ms]".
private static final Pattern SERVER_TIMING_HEADER_PATTERN = Pattern.compile(".*dur=(?<dur>\\d+)");

// ClientInterceptor to intercept the outgoing RPCs in order to retrieve the GFE header.
private static class HeaderClientInterceptor implements ClientInterceptor {

  @Override
  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
      CallOptions callOptions, Channel next) {
    return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {

      @Override
      public void start(Listener<RespT> responseListener, Metadata headers) {
        super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
          @Override
          public void onHeaders(Metadata metadata) {
            processHeader(metadata, method.getFullMethodName());
            super.onHeaders(metadata);
          }
        }, headers);
      }
    };
  }

  // Process header, extract duration value and record it using OpenCensus.
  private static void processHeader(Metadata metadata, String method) {
    if (metadata.get(SERVER_TIMING_HEADER_KEY) != null) {
      String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY);
      Matcher matcher = SERVER_TIMING_HEADER_PATTERN.matcher(serverTiming);
      if (matcher.find()) {
        long latency = Long.parseLong(matcher.group("dur"));

        TagContext tctx = tagger.emptyBuilder().put(key, TagValue.create(method)).build();
        try (Scope ss = tagger.withTagContext(tctx)) {
          STATS_RECORDER.newMeasureMap()
              .put(SPANNER_GFE_LATENCY, latency)
              .record();
        }
      }
    }
  }

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"

	spanner "cloud.google.com/go/spanner/apiv1"
	gax "github.com/googleapis/gax-go/v2"
	sppb "google.golang.org/genproto/googleapis/spanner/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"
)

// OpenCensus Tag, Measure and View.
var (
	KeyMethod    = tag.MustNewKey("grpc_client_method")
	GFELatencyMs = stats.Int64("cloud.google.com/go/spanner/gfe_latency",
		"Latency between Google's network receives an RPC and reads back the first byte of the response", "ms")
	GFELatencyView = view.View{
		Name:        "cloud.google.com/go/spanner/gfe_latency",
		Measure:     GFELatencyMs,
		Description: "Latency between Google's network receives an RPC and reads back the first byte of the response",
		Aggregation: view.Distribution(0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
			16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
			300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
			100000.0),
		TagKeys: []tag.Key{KeyMethod}}
)

func queryWithGFELatency(w io.Writer, db string) error {
	projectID, _, _, err := parseDatabaseName(db)
	if err != nil {
		return err
	}

	ctx := context.Background()
	client, err := spanner.NewClient(ctx)
	if err != nil {
		return err
	}
	defer client.Close()

	// Register OpenCensus views.
	err = view.Register(&GFELatencyView)
	if err != nil {
		return err
	}

	// Create OpenCensus Stackdriver exporter.
	sd, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: projectID,
	})
	if err != nil {
		return err
	}
	// It is imperative to invoke flush before your main function exits
	defer sd.Flush()

	// Start the metrics exporter
	sd.StartMetricsExporter()
	defer sd.StopMetricsExporter()

	// Create a session.
	req := &sppb.CreateSessionRequest{Database: db}
	session, err := client.CreateSession(ctx, req)
	if err != nil {
		return err
	}

	// Execute a SQL query and retrieve the GFE server-timing header in gRPC metadata.
	req2 := &sppb.ExecuteSqlRequest{
		Session: session.Name,
		Sql:     `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`,
	}
	var md metadata.MD
	resultSet, err := client.ExecuteSql(ctx, req2, gax.WithGRPCOptions(grpc.Header(&md)))
	if err != nil {
		return err
	}
	for _, row := range resultSet.GetRows() {
		for _, value := range row.GetValues() {
			fmt.Fprintf(w, "%s ", value.GetStringValue())
		}
		fmt.Fprintf(w, "\n")
	}

	// The format is: "server-timing: gfet4t7; dur=[GFE latency in ms]"
	srvTiming := md.Get("server-timing")[0]
	gfeLtcy, err := strconv.Atoi(strings.TrimPrefix(srvTiming, "gfet4t7; dur="))
	if err != nil {
		return err
	}
	// Record GFE t4t7 latency with OpenCensus.
	ctx, err = tag.New(ctx, tag.Insert(KeyMethod, "ExecuteSql"))
	if err != nil {
		return err
	}
	stats.Record(ctx, GFELatencyMs.M(int64(gfeLtcy)))

	return nil
}

Visualizar a latência do Google Front End

Depois de recuperar as métricas, é possível visualizar a latência do Google Front End no Cloud Monitoring.

Veja um exemplo de um gráfico que ilustra a métrica de latência do Google Front End. O programa cria uma gfe_latencyvisualização do OpenCensus chamada . Essa string se torna parte do nome da métrica quando é exportada para o Cloud Monitoring.

Latência do Google Monitoring no Google Front End

Figura 2. Gráfico de latência do Google Front End no Cloud Monitoring

Latência de solicitação da API Cloud Spanner

Diagrama da arquitetura do Cloud Spanner para latência da solicitação da API Cloud Spanner

Essa latência é o período (em segundos) entre o primeiro byte da solicitação que o front-end da API Cloud Spanner recebe e o último byte de resposta que o front-end da API Cloud Spanner envia. A latência inclui o tempo necessário para o processamento das solicitações de API no back-end do Cloud Spanner e na camada de API. No entanto, ele não inclui sobrecarga de rede ou proxy reverso entre clientes e servidores do Cloud Spanner.

A métrica spanner.googleapis.com/api/request_latencies captura e expõe a latência do front-end da API Cloud Spanner para solicitações do Cloud Spanner.

Capturar a latência da solicitação da API Cloud Spanner

Por padrão, essa latência está disponível como parte das métricas do Cloud Monitoring. Você não precisa fazer nada para capturar e exportar essas informações.

Visualizar a latência da solicitação da API Cloud Spanner

É possível usar a ferramenta de gráficos do Metrics Explorer para visualizar o gráfico da métrica spanner.googleapis.com/api/request_latencies no Cloud Monitoring.

Veja um exemplo de um gráfico que ilustra a métrica de latência da solicitação da API Cloud Spanner.

Latência de solicitação da API Cloud Monitoring

Figura 3. Gráfico de latência da solicitação da API Cloud Spanner no Cloud Monitoring

Latência da consulta

Diagrama da arquitetura do Cloud Spanner com a latência da consulta

Essa latência é o tempo (em milissegundos) necessário para executar consultas SQL no banco de dados do Cloud Spanner.

A latência da consulta está disponível para as APIs executeSql e executeStreamingSql (links em inglês).

Se o parâmetro QueryMode estiver definido como PROFILE, o ResultSetStats do Cloud Spanner estará disponível nas respostas.

Ao definir QueryMode como PROFILE, são retornados o plano de consulta e as estatísticas de execução com os resultados. Além disso, ResultSetStats inclui o tempo decorrido para executar consultas no banco de dados do Cloud Spanner.

Capturar latência da consulta

É possível capturar a latência da consulta nos seguintes idiomas:

Java

private static final String MILLISECOND = "ms";
static final List<Double> RPC_MILLIS_BUCKET_BOUNDARIES =
    Collections.unmodifiableList(
        Arrays.asList(
            0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
            16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
            300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
            100000.0));
static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM =
    Distribution.create(BucketBoundaries.create(RPC_MILLIS_BUCKET_BOUNDARIES));

static MeasureDouble QUERY_STATS_ELAPSED =
    MeasureDouble.create(
        "cloud.google.com/java/spanner/query_stats_elapsed",
        "The execution of the query",
        MILLISECOND);

// Register the view. It is imperative that this step exists,
// otherwise recorded metrics will be dropped and never exported.
static View QUERY_STATS_LATENCY_VIEW = View
    .create(Name.create("cloud.google.com/java/spanner/query_stats_elapsed"),
        "The execution of the query",
        QUERY_STATS_ELAPSED,
        AGGREGATION_WITH_MILLIS_HISTOGRAM,
        Collections.emptyList());

static ViewManager manager = Stats.getViewManager();
private static final StatsRecorder STATS_RECORDER = Stats.getStatsRecorder();

static void captureQueryStatsMetric(DatabaseClient dbClient) {
  manager.registerView(QUERY_STATS_LATENCY_VIEW);

  // Enable OpenCensus exporters to export metrics to Cloud Monitoring.
  // Exporters use Application Default Credentials to authenticate.
  // See https://developers.google.com/identity/protocols/application-default-credentials
  // for more details.
  try {
    StackdriverStatsExporter.createAndRegister();
  } catch (IOException | IllegalStateException e) {
    System.out.println("Error during StackdriverStatsExporter");
  }

  try (ResultSet resultSet = dbClient.singleUse()
      .analyzeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),
          QueryAnalyzeMode.PROFILE)) {

    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
    Value value = resultSet.getStats().getQueryStats()
        .getFieldsOrDefault("elapsed_time", Value.newBuilder().setStringValue("0 msecs").build());
    double elapasedTime = Double.parseDouble(value.getStringValue().replaceAll(" msecs", ""));
    STATS_RECORDER.newMeasureMap()
        .put(QUERY_STATS_ELAPSED, elapasedTime)
        .record();
  }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"
)

// OpenCensus Tag, Measure and View.
var (
	QueryStatsElapsed = stats.Float64("cloud.google.com/go/spanner/query_stats_elapsed",
		"The execution of the query", "ms")
	QueryStatsLatencyView = view.View{
		Name:        "cloud.google.com/go/spanner/query_stats_elapsed",
		Measure:     QueryStatsElapsed,
		Description: "The execution of the query",
		Aggregation: view.Distribution(0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
			16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
			300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
			100000.0),
		TagKeys: []tag.Key{}}
)

func queryWithQueryStats(w io.Writer, db string) error {
	projectID, _, _, err := parseDatabaseName(db)
	if err != nil {
		return err
	}

	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	// Register OpenCensus views.
	err = view.Register(&QueryStatsLatencyView)
	if err != nil {
		return err
	}

	// Create OpenCensus Stackdriver exporter.
	sd, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: projectID,
	})
	if err != nil {
		return err
	}
	// It is imperative to invoke flush before your main function exits
	defer sd.Flush()

	// Start the metrics exporter
	sd.StartMetricsExporter()
	defer sd.StopMetricsExporter()

	// Execute a SQL query and get the query stats.
	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().QueryWithStats(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			// Record query execution time with OpenCensus.
			elapasedTime := iter.QueryStats["elapsed_time"].(string)
			elapasedTimeMs, err := strconv.ParseFloat(strings.TrimSuffix(elapasedTime, " msecs"), 64)
			if err != nil {
				return err
			}
			stats.Record(ctx, QueryStatsElapsed.M(elapasedTimeMs))
			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)
	}
}

Visualizar a latência da consulta

Depois de recuperar as métricas, é possível visualizar a latência da consulta no Cloud Monitoring.

Veja um exemplo de um gráfico que ilustra a métrica de latência da consulta. O programa cria uma visualização do OpenCensus chamada query_stats_elapsed. Essa string se torna parte do nome da métrica quando é exportada para o Cloud Monitoring.

Latência da consulta do Cloud Monitoring

Figura 4. Gráfico de latência da consulta no Cloud Monitoring

Resolver problemas de latência

Ao usar o Cloud Monitoring para capturar e visualizar viagens de ida e volta do cliente, Google Front End, solicitação da API Cloud Spanner e latências de consulta, você pode compará-las lado a lado. Isso ajudará você a identificar a origem da latência.

Na tabela a seguir, veja alguns exemplos de problemas de latência e por que eles estão ocorrendo:

Para este problema de latência... O problema pode ser...
Você tem uma alta latência de ida e volta ao cliente, mas uma baixa latência do Google Front End e uma baixa latência de solicitação da API Cloud Spanner. Pode haver um problema no código do aplicativo ou um problema de rede entre o cliente e o Google Front End regional. Se o aplicativo tiver um problema de desempenho que causa lentidão em alguns caminhos do código, o tempo de retorno do cliente para cada solicitação de API poderá aumentar. Essa latência também pode ser causada por problemas na infraestrutura de computação no lado do cliente (por exemplo, utilização de VM, CPU ou memória, conexões, descritores de arquivo etc.).
Você tem uma alta latência de ida e volta ao cliente e uma alta latência de solicitação da API Cloud Spanner.
  1. Algumas das suas consultas estão causando latências mais altas porque elas buscam e buscam uma grande quantidade de dados. Use este guia para analisar o perfil e otimizar suas consultas.
  2. Se a taxa de consulta por segundo for baixa, a latência poderá vir de outliers. Ajuste as configurações de prazo e repetição para reduzir o impacto dos outliers.
Você tem uma alta latência de Google Front End, mas uma baixa latência de solicitação da API Cloud Spanner.
  1. O acesso ao banco de dados de outra região pode aumentar a latência do Google Front End e a diminuir a latência da solicitação da API Cloud Spanner. Por exemplo, o tráfego de um cliente na região us-east1 que tem uma instância na região us-central1 terá uma latência alta do Google Front End, mas uma latência da solicitação da API Cloud Spanner menor.
  2. Verifique no Painel de status do Google Cloud se há algum problema de rede na sua região. Se não houver nenhum problema, abra um caso de suporte e inclua essas informações para que os engenheiros de suporte possam ajudar na solução de problemas do Google Front End.
Você tem uma alta latência de solicitação na API Cloud Spanner, mas uma baixa latência de consulta. Verifique no Painel de status do Google Cloud se há algum problema de rede na sua região. Se não houver nenhum problema, abra um caso de suporte e inclua essas informações para que os engenheiros de suporte possam ajudar na solução de problemas do front-end da API Cloud Spanner.
Você tem uma alta latência de consulta. Para ver as métricas da consulta e ajudar a resolver o problema de latência dela, consulte Estatísticas de consulta. Para melhorar o desempenho, otimize o esquema, a indexação e a consulta. Para mais informações, consulte Práticas recomendadas de SQL e Solução de problemas de regressões de desempenho.

A seguir