Soluciona problemas relacionados con las etiquetas de solicitud y de transacción

Spanner proporciona un conjunto de tablas de estadísticas integradas para ayudarte a obtener estadísticas en tus consultas, lecturas y transacciones. Para correlacionar las estadísticas con código de la aplicación y para mejorar la solución de problemas, puedes agregar una etiqueta (una string) a las operaciones de lectura, consulta y transacción de Spanner en tu el código de la aplicación. Estas etiquetas se propagan en tablas de estadísticas que te ayudan para la correlación y la búsqueda basada en etiquetas.

Spanner admite dos tipos de etiquetas: etiquetas request y transaction. Tal como su nombre lo sugiere, puedes agregar etiquetas de transacción a transacciones y etiquetas de solicitud a consultas individuales y lee APIs. Puedes configurar etiqueta de transacción en el alcance de la transacción y configura etiquetas de solicitud individuales para para cada solicitud a la API aplicable dentro de la transacción. Solicita etiquetas y transacción las etiquetas que se establecen en el código de la aplicación se completan en las columnas de de las siguientes tablas de estadísticas.

Tabla de estadísticas Tipo de etiquetas propagadas en la tabla de estadísticas
Estadísticas de consultas principales Etiquetas de solicitud
Estadísticas de lectura de TopN Etiquetas de solicitud
TopN Transactions Statistics Etiquetas de transacción
Estadísticas de bloqueo de TopN Etiquetas de transacción

Etiquetas de solicitud

Puedes agregar una etiqueta de solicitud opcional a una consulta o una solicitud de lectura. Spanner estadísticas de grupos por etiqueta de solicitud, que se puede ver en el campo REQUEST_TAG de tanto estadísticas de consultas y estadísticas de lectura en diferentes tipos de tablas particionadas.

Cuándo usar etiquetas de solicitud

Las siguientes son algunas de las situaciones que se benefician del uso de etiquetas de solicitud.

  • Encuentra el origen de una consulta o lectura problemática: Spanner que recopila estadísticas para lecturas y consultas en tablas de estadísticas integradas. Cuando encuentres las consultas lentas o las lecturas que consumen mucha CPU en las estadísticas si ya asignaste etiquetas, puedes identificar fuente (aplicación o microservicio) que llama a estas operaciones según la información de la etiqueta.
  • Identifica lecturas o consultas en tablas de estadísticas: asigna solicitudes con las etiquetas ayuda a filtrar las filas en la tabla de estadísticas según las etiquetas les interesan.
  • Descubrir si las consultas de una aplicación o un microservicio Lento: Las etiquetas de solicitud pueden ayudar a identificar si las consultas de un una aplicación o un microservicio tienen latencias más altas.
  • Agrupar estadísticas para un conjunto de lecturas o consultas: Puedes usar solicitudes Etiquetas para realizar un seguimiento, comparar e informar el rendimiento en un conjunto de lecturas similares o consultas. Por ejemplo, si múltiples consultas acceden a una tabla/conjunto de tablas con el mismo patrón de acceso, puedes considerar agregar la misma etiqueta a todas esas consultas para rastrearlas juntas.

Cómo asignar etiquetas de solicitud

En el siguiente ejemplo, se muestra cómo configurar etiquetas de solicitud con la API de bibliotecas cliente.

C++

void SetRequestTag(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  spanner::SqlStatement select(
      "SELECT SingerId, AlbumId, AlbumTitle FROM Albums");
  using RowType = std::tuple<std::int64_t, std::int64_t, std::string>;

  auto opts = google::cloud::Options{}.set<spanner::RequestTagOption>(
      "app=concert,env=dev,action=select");
  auto rows = client.ExecuteQuery(std::move(select), std::move(opts));
  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";
  }
}

C#


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

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

    public async Task<List<Album>> RequestTagAsync(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");
        // Sets the request tag to "app=concert,env=dev,action=select".
        // This request tag will only be set on this request.
        cmd.Tag = "app=concert,env=dev,action=select";

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            var album = new Album
            {
                SingerId = reader.GetFieldValue<int>("SingerId"),
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle")
            };
            albums.Add(album);
            Console.WriteLine($"SingerId: {album.SingerId}, AlbumId: {album.AlbumId}, AlbumTitle: {album.AlbumTitle}");
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

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

// queryWithTag reads from a database with request tag set
func queryWithTag(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().QueryWithOptions(ctx, stmt, spanner.QueryOptions{RequestTag: "app=concert,env=dev,action=select"})
	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

static void setRequestTag(DatabaseClient databaseClient) {
  // Sets the request tag to "app=concert,env=dev,action=select".
  // This request tag will only be set on this request.
  try (ResultSet resultSet = databaseClient
      .singleUse()
      .executeQuery(
          Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),
          Options.tag("app=concert,env=dev,action=select"))) {
    while (resultSet.next()) {
      System.out.printf(
          "SingerId: %d, AlbumId: %d, AlbumTitle: %s\n",
          resultSet.getLong(0),
          resultSet.getLong(1),
          resultSet.getString(2));
    }
  }
}

Node.js

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

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

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

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

  // Execute a query with a request tag.
  const [albums] = await database.run({
    sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
    requestOptions: {requestTag: 'app=concert,env=dev,action=select'},
    json: true,
  });
  albums.forEach(album => {
    console.log(
      `SingerId: ${album.SingerId}, AlbumId: ${album.AlbumId}, AlbumTitle: ${album.AlbumTitle}`
    );
  });
  await database.close();
}
queryTags();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Executes a read with a request tag.
 * Example:
 * ```
 * spanner_set_request_tag($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function set_request_tag(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $snapshot = $database->snapshot();
    $results = $snapshot->execute(
        'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
        [
            'requestOptions' => [
                'requestTag' => 'app=concert,env=dev,action=select'
            ]
        ]
    );
    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"
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 SingerId, AlbumId, AlbumTitle FROM Albums",
        request_options={"request_tag": "app=concert,env=dev,action=select"},
    )

    for row in results:
        print("SingerId: {}, AlbumId: {}, AlbumTitle: {}".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

client.execute(
  "SELECT SingerId, AlbumId, MarketingBudget FROM Albums",
  request_options: { tag: "app=concert,env=dev,action=select" }
).rows.each do |row|
  puts "#{row[:SingerId]} #{row[:AlbumId]} #{row[:MarketingBudget]}"
end

Cómo ver las etiquetas de solicitud en la tabla de estadísticas

La siguiente consulta muestra las estadísticas en intervalos de 10 minutos.

SELECT t.text,
       t.request_tag,
       t.execution_count,
       t.avg_latency_seconds,
       t.avg_rows,
       t.avg_bytes
FROM SPANNER_SYS.QUERY_STATS_TOP_10MINUTE AS t
LIMIT 3;

Tomemos los siguientes datos como ejemplo de los resultados que obtenemos de nuestra para cada búsqueda.

texto request_tag execution_count avg_latency_seconds avg_rows avg_bytes
SELECT SingerId, AlbumId, AlbumTitle FROM Albums app=concert,env=dev,action=select 212 0.025 21 2365
seleccionar * de pedidos; app=catalogsearch,env=dev,action=list 55 0.02 16 33.35
SELECT SingerId, FirstName, LastName FROM Singers; [string vacía] 154 0.048 42 486.33

En esta tabla de resultados, podemos ver que si asignaste un REQUEST_TAG para una consulta, se propaga en la tabla de estadísticas. Si no hay de solicitud de acceso asignada, se muestra como una cadena vacía.

Para las consultas etiquetadas, las estadísticas se agregan por etiqueta (p. ej., etiqueta de solicitud). app=concert,env=dev,action=select tiene una latencia promedio de 0.025. segundos). Si no hay ninguna etiqueta asignada, las estadísticas se agregan por consulta (p.ej., la consulta en la tercera fila tiene una latencia promedio de 0.048) segundos).

Etiquetas de transacción

Se puede agregar una etiqueta de transacción opcional a las transacciones individuales. Spanner agrupa estadísticas por etiqueta de transacción, que se puede ver en el Campo TRANSACTION_TAG de estadísticas de transacciones en diferentes tipos de tablas particionadas.

Cuándo usar etiquetas de transacción

Las siguientes son algunas situaciones en las que se beneficia el uso de las transacciones rótulos nuevos rápidamente.

  • Encuentra el origen de una transacción problemática: Spanner recopila estadísticas de las transacciones de lectura y escritura en la transacción de una tabla de estadísticas. Cuando encuentras transacciones lentas en la transacción de estadísticas, si ya les asignaste etiquetas, puedes identificar la fuente (aplicación o microservicio) que llama a estos en función de la información de la etiqueta.
  • Identifica transacciones en tablas de estadísticas: asigna transacciones con las etiquetas ayuda a filtrar las filas en la tabla de estadísticas de transacciones según la etiquetas que te interesan. Sin etiquetas de transacción, descubrir qué las operaciones están representadas por una estadística puede ser un proceso engorroso. Para ejemplo, para las estadísticas de transacciones, deberías examinar las tablas y las columnas necesarias para identificar la transacción sin etiquetar.
  • Descubrir si las transacciones de una aplicación o un microservicio Lento: Las etiquetas de transacción pueden ayudar a identificar si las transacciones de un determinado una aplicación o un microservicio tienen latencias más altas.
  • Estadísticas de agrupación para un conjunto de transacciones: Puedes usar etiquetas para realizar un seguimiento, comparar e informar el rendimiento de un conjunto de transacciones de contenedores.
  • Encuentra qué transacciones acceden a las columnas involucradas en el bloqueo Conflicto: Las etiquetas de transacción pueden ayudar a identificar transacciones individuales. lo que causa conflictos de bloqueo en las tablas de Estadísticas de bloqueo.
  • Transmisión de datos de cambios del usuario fuera de Spanner mediante flujos de cambios: Los registros de datos de flujos de cambios contienen etiquetas de transacción para las transacciones que modificaban los datos del usuario. Esto permite que el lector de un flujo de cambios asociar los cambios al tipo de transacción según las etiquetas.

Cómo asignar etiquetas de transacción

En el siguiente ejemplo, se muestra cómo configurar etiquetas de transacción con Spanner bibliotecas cliente. Cuando usas una biblioteca cliente, puedes configurar una etiqueta de transacción en al comienzo de la llamada de transacción que se aplica a todas las llamadas operaciones dentro de esa transacción.

C++

void SetTransactionTag(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  using ::google::cloud::StatusOr;

  // Sets the transaction tag to "app=concert,env=dev". This will be
  // applied to all the individual operations inside this transaction.
  auto commit_options =
      google::cloud::Options{}.set<spanner::TransactionTagOption>(
          "app=concert,env=dev");
  auto commit = client.Commit(
      [&client](
          spanner::Transaction const& txn) -> StatusOr<spanner::Mutations> {
        spanner::SqlStatement update_statement(
            "UPDATE Venues SET Capacity = CAST(Capacity/4 AS INT64)"
            "  WHERE OutdoorVenue = false");
        // Sets the request tag to "app=concert,env=dev,action=update".
        // This will only be set on this request.
        auto update = client.ExecuteDml(
            txn, std::move(update_statement),
            google::cloud::Options{}.set<spanner::RequestTagOption>(
                "app=concert,env=dev,action=update"));
        if (!update) return std::move(update).status();

        spanner::SqlStatement insert_statement(
            "INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, "
            "                    LastUpdateTime)"
            " VALUES (@venueId, @venueName, @capacity, @outdoorVenue, "
            "         PENDING_COMMIT_TIMESTAMP())",
            {
                {"venueId", spanner::Value(81)},
                {"venueName", spanner::Value("Venue 81")},
                {"capacity", spanner::Value(1440)},
                {"outdoorVenue", spanner::Value(true)},
            });
        // Sets the request tag to "app=concert,env=dev,action=insert".
        // This will only be set on this request.
        auto insert = client.ExecuteDml(
            txn, std::move(insert_statement),
            google::cloud::Options{}.set<spanner::RequestTagOption>(
                "app=concert,env=dev,action=select"));
        if (!insert) return std::move(insert).status();
        return spanner::Mutations{};
      },
      commit_options);
  if (!commit) throw std::move(commit).status();
}

C#


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

public class TransactionTagAsyncSample
{
    public async Task<int> TransactionTagAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        return await connection.RunWithRetriableTransactionAsync(async transaction =>
        {
            // Sets the transaction tag to "app=concert,env=dev".
            // This transaction tag will be applied to all the individual operations inside
            // the transaction.
            transaction.Tag = "app=concert,env=dev";

            // Sets the request tag to "app=concert,env=dev,action=update".
            // This request tag will only be set on this request.
            var updateCommand =
                connection.CreateDmlCommand("UPDATE Venues SET Capacity = DIV(Capacity, 4) WHERE OutdoorVenue = false");
            updateCommand.Tag = "app=concert,env=dev,action=update";
            updateCommand.Transaction = transaction;
            int rowsModified = await updateCommand.ExecuteNonQueryAsync();

            var insertCommand = connection.CreateDmlCommand(
                @"INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime)
                    VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())",
                new SpannerParameterCollection
                {
                    {"venueId", SpannerDbType.Int64, 81},
                    {"venueName", SpannerDbType.String, "Venue 81"},
                    {"capacity", SpannerDbType.Int64, 1440},
                    {"outdoorVenue", SpannerDbType.Bool, true}
                }
            );
            // Sets the request tag to "app=concert,env=dev,action=insert".
            // This request tag will only be set on this request.
            insertCommand.Tag = "app=concert,env=dev,action=insert";
            insertCommand.Transaction = transaction;
            rowsModified += await insertCommand.ExecuteNonQueryAsync();
            return rowsModified;
        });
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

// readWriteTransactionWithTag executes the update and insert queries on venues table with appropriate transaction and requests tag
func readWriteTransactionWithTag(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	_, err = client.ReadWriteTransactionWithOptions(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		stmt := spanner.Statement{
			SQL: `UPDATE Venues SET Capacity = CAST(Capacity/4 AS INT64) WHERE OutdoorVenue = false`,
		}
		_, err := txn.UpdateWithOptions(ctx, stmt, spanner.QueryOptions{RequestTag: "app=concert,env=dev,action=update"})
		if err != nil {
			return err
		}
		fmt.Fprint(w, "Venue capacities updated.")
		stmt = spanner.Statement{
			SQL: `INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime) 
                   VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())`,
			Params: map[string]interface{}{
				"venueId":      81,
				"venueName":    "Venue 81",
				"capacity":     1440,
				"outdoorVenue": true,
			},
		}
		_, err = txn.UpdateWithOptions(ctx, stmt, spanner.QueryOptions{RequestTag: "app=concert,env=dev,action=insert"})
		if err != nil {
			return err
		}
		fmt.Fprint(w, "New venue inserted.")
		return nil
	}, spanner.TransactionOptions{TransactionTag: "app=concert,env=dev"})
	return err
}

Java

static void setTransactionTag(DatabaseClient databaseClient) {
  // Sets the transaction tag to "app=concert,env=dev".
  // This transaction tag will be applied to all the individual operations inside this
  // transaction.
  databaseClient
      .readWriteTransaction(Options.tag("app=concert,env=dev"))
      .run(transaction -> {
        // Sets the request tag to "app=concert,env=dev,action=update".
        // This request tag will only be set on this request.
        transaction.executeUpdate(
            Statement.of("UPDATE Venues"
                + " SET Capacity = CAST(Capacity/4 AS INT64)"
                + " WHERE OutdoorVenue = false"),
            Options.tag("app=concert,env=dev,action=update"));
        System.out.println("Venue capacities updated.");

        Statement insertStatement = Statement.newBuilder(
            "INSERT INTO Venues"
                + " (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime)"
                + " VALUES ("
                + " @venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP()"
                + " )")
            .bind("venueId")
            .to(81)
            .bind("venueName")
            .to("Venue 81")
            .bind("capacity")
            .to(1440)
            .bind("outdoorVenue")
            .to(true)
            .build();

        // Sets the request tag to "app=concert,env=dev,action=insert".
        // This request tag will only be set on this request.
        transaction.executeUpdate(
            insertStatement,
            Options.tag("app=concert,env=dev,action=insert"));
        System.out.println("New venue inserted.");

        return null;
      });
}

Node.js

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

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

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

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

  // Run a transaction with a transaction tag that will automatically be
  // included with each request in the transaction.
  try {
    await database.runTransactionAsync(
      {requestOptions: {transactionTag: 'app=cart,env=dev'}},
      async tx => {
        // Set the request tag to "app=concert,env=dev,action=update".
        // This request tag will only be set on this request.
        await tx.runUpdate({
          sql: 'UPDATE Venues SET Capacity = DIV(Capacity, 4) WHERE OutdoorVenue = false',
          requestOptions: {requestTag: 'app=concert,env=dev,action=update'},
        });
        console.log('Updated capacity of all indoor venues to 1/4.');

        await tx.runUpdate({
          sql: `INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime)
                VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())`,
          params: {
            venueId: 81,
            venueName: 'Venue 81',
            capacity: 1440,
            outdoorVenue: true,
          },
          types: {
            venueId: {type: 'int64'},
            venueName: {type: 'string'},
            capacity: {type: 'int64'},
            outdoorVenue: {type: 'bool'},
          },
          requestOptions: {requestTag: 'app=concert,env=dev,action=update'},
        });
        console.log('Inserted new outdoor venue');

        await tx.commit();
      }
    );
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    await database.close();
  }
}
transactionTag();

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;

/**
 * Executes a transaction with a transaction tag.
 * Example:
 * ```
 * spanner_set_transaction_tag($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function set_transaction_tag(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $database->runTransaction(function (Transaction $t) {
        $t->executeUpdate(
            'UPDATE Venues SET Capacity = CAST(Capacity/4 AS INT64) WHERE OutdoorVenue = false',
            [
                'requestOptions' => ['requestTag' => 'app=concert,env=dev,action=update']
            ]
        );
        print('Venue capacities updated.' . PHP_EOL);
        $t->executeUpdate(
            'INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime) '
            . 'VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())',
            [
                'parameters' => [
                    'venueId' => 81,
                    'venueName' => 'Venue 81',
                    'capacity' => 1440,
                    'outdoorVenue' => true,
                ],
                'requestOptions' => ['requestTag' => 'app=concert,env=dev,action=insert']
            ]
        );
        print('New venue inserted.' . PHP_EOL);
        $t->commit();
    }, [
        'requestOptions' => ['transactionTag' => 'app=concert,env=dev']
    ]);
}

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)

def update_venues(transaction):
    # Sets the request tag to "app=concert,env=dev,action=update".
    #  This request tag will only be set on this request.
    transaction.execute_update(
        "UPDATE Venues SET Capacity = CAST(Capacity/4 AS INT64) WHERE OutdoorVenue = false",
        request_options={"request_tag": "app=concert,env=dev,action=update"},
    )
    print("Venue capacities updated.")

    # Sets the request tag to "app=concert,env=dev,action=insert".
    # This request tag will only be set on this request.
    transaction.execute_update(
        "INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime) "
        "VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())",
        params={
            "venueId": 81,
            "venueName": "Venue 81",
            "capacity": 1440,
            "outdoorVenue": True,
        },
        param_types={
            "venueId": param_types.INT64,
            "venueName": param_types.STRING,
            "capacity": param_types.INT64,
            "outdoorVenue": param_types.BOOL,
        },
        request_options={"request_tag": "app=concert,env=dev,action=insert"},
    )
    print("New venue inserted.")

database.run_in_transaction(update_venues, transaction_tag="app=concert,env=dev")

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

client.transaction request_options: { tag: "app=cart,env=dev" } do |tx|
  tx.execute_update \
    "UPDATE Venues SET Capacity = CAST(Capacity/4 AS INT64) WHERE OutdoorVenue = false",
    request_options: { tag: "app=concert,env=dev,action=update" }

  puts "Venue capacities updated."

  tx.execute_update \
    "INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue) " \
    "VALUES (@venue_id, @venue_name, @capacity, @outdoor_venue)",
    params: {
      venue_id: 81,
      venue_name: "Venue 81",
      capacity: 1440,
      outdoor_venue: true
    },
    request_options: { tag: "app=concert,env=dev,action=insert" }

  puts "New venue inserted."
end

Cómo ver las etiquetas de transacción en la tabla de estadísticas de transacciones

La siguiente consulta muestra las estadísticas de transacciones en intervalos de 10 minutos.

SELECT t.fprint,
       t.transaction_tag,
       t.read_columns,
       t.commit_attempt_count,
       t.avg_total_latency_seconds
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE AS t
LIMIT 3;

Tomemos los siguientes datos como ejemplo de los resultados que obtenemos de nuestra para cada búsqueda.

fprint transaction_tag read_columns commit_attempt_count avg_total_latency_seconds
40015598317 app=concert,env=dev [Lugares._existe,
Establecimientos.VenueId,
Venues.VenueName,
Venues.Capacity]
278802 0.3508
20524969030 app=product,service=payment [Singers.SingerInfo] 129012 0.0142
77848338483 [string vacía] [Singers.FirstName, Singers.LastName, Singers._exists] 5357 0.048

En esta tabla de resultados, podemos ver que si asignaste una TRANSACTION_TAG a una transacción y, luego, se propaga en la transacción. de una tabla de estadísticas. Si no hay ninguna etiqueta de transacción asignada, se muestra como un una cadena vacía.

Para las transacciones etiquetadas, las estadísticas se agregan por etiqueta de transacción. (p.ej., la etiqueta de transacción app=concert,env=dev a tiene un promedio latencia de 0.3508 segundos). Si no hay ninguna etiqueta asignada, las estadísticas se agregan por FPRINT (p.ej., 77848338483 en la tercera fila tiene un una latencia promedio de 0.048 segundos).

Cómo ver las etiquetas de transacción en la tabla Lock Statistics

La siguiente consulta muestra las estadísticas de bloqueo en intervalos de 10 minutos.

La función CAST() convierte la row_range_start_key de BYTES a una STRING.

SELECT 
   CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
   s.lock_wait_seconds,
   s.sample_lock_requests
FROM SPANNER_SYS.LOCK_STATS_TOP_10MINUTE s
LIMIT 2;

Tomemos los siguientes datos como ejemplo de los resultados que obtenemos de nuestra para cada búsqueda.

row_range_start_key lock_wait_seconds sample_lock_requests
Canciones (2,1,1) 0.61 LOCK_MODE: ReaderShared
COLUMN: Singers.SingerInfo
TRANSACTION_TAG: app=product,service=shipping

LOCK_MODE: WriterShared
COLUMN: Singers.SingerInfo
TRANSACTION_TAG: app=product,service=payment
albums(2,1+) 0.48 LOCK_MODE: ReaderShared
COLUMN: users._exists1
TRANSACTION_TAG: [empty string]

LOCK_MODE: WriterShared
COLUMN: users._exists
TRANSACTION_TAG: [empty string]

En esta tabla de resultados, podemos ver que si asignaste una TRANSACTION_TAG a una transacción y, luego, se propagará en el bloqueo de una tabla de estadísticas. Si no hay ninguna etiqueta de transacción asignada, se muestra como una cadena vacía.

Asignación entre los métodos de la API y la etiqueta de solicitud o transacción

Las etiquetas de solicitud y de transacción se pueden aplicar a métodos específicos de la API basados en según si el modo de transacción es de solo lectura o de lectura y escritura transacción. Por lo general, las etiquetas de transacción se aplican a operaciones de lectura y escritura transacciones, mientras que las etiquetas de solicitud se pueden aplicar a las transacciones de solo lectura. La siguiente tabla muestra la asignación de los métodos de la API a los tipos aplicables de rótulos nuevos rápidamente.

Métodos de la API Modos de transacción Solicitar etiqueta Etiqueta de transacción
Read,
StreamingRead
Transacción de solo lectura No
Transacción de lectura o escritura
ExecuteSql,
ExecuteStreamingSql1
Transacción de solo lectura1 1 No
Transacción de lectura o escritura
ExecuteBatchDml Transacción de lectura o escritura
BeginTransaction Transacción de lectura o escritura No
Confirmar Transacción de lectura o escritura No

1 Para las consultas de flujos de cambios ejecutadas con Apache Beam SpannerIO Conector de Dataflow; REQUEST_TAG contiene un nombre de trabajo de Dataflow.

Limitaciones

Cuando agregues etiquetas a tus lecturas, consultas y transacciones, ten en cuenta lo siguiente: limitaciones:

  • La longitud de una cadena de etiqueta tiene un límite de 50 caracteres. Las cadenas que exceden este límite están truncados.
  • En las etiquetas, solo se permiten caracteres ASCII (32-126). Unicode arbitrario los caracteres se reemplazan por guiones bajos.
  • Todos los caracteres de guion bajo (_) iniciales se quitan de la cadena.
  • Las etiquetas distinguen mayúsculas de minúsculas. Por ejemplo, si agregas la etiqueta de solicitud APP=cart,ENV=dev a un conjunto de consultas y agrega app=cart,env=dev a otro conjunto de consultas, Spanner agrega estadísticas por separado para cada etiqueta.
  • Es posible que falten etiquetas en las tablas de estadísticas de los siguientes elementos: circunstancia:

    • Si Spanner no puede almacenar estadísticas de todas las etiquetas que se ejecutan durante el intervalo en tablas, el sistema prioriza las operaciones con los recursos que consumen más recursos durante durante un intervalo de tiempo determinado.

Nombres de etiquetas

Cuando asignas etiquetas a las operaciones de tu base de datos, es importante considerar qué información deseas transmitir en cada cadena de etiqueta. La convención o y el patrón que elijas hará que las etiquetas sean más eficaces. Por ejemplo, la etiqueta correcta facilita la correlación de las estadísticas con el código de la aplicación.

Puedes elegir cualquier etiqueta que desees dentro de las limitaciones indicadas. Sin embargo, recomendamos que construyas una cadena de etiqueta como un conjunto de pares clave-valor separados con comas.

Por ejemplo, supongamos que usas una base de datos de Spanner para un de comercio electrónico. Puedes incluir información sobre el entorno de desarrollo y la acción que realiza la consulta en la etiqueta de solicitud que asignarás a una consulta específica. Puedes considera asignar la cadena de etiqueta en el formato de par clave-valor, como app=cart,env=dev,action=update. Esto significa que se llama a la consulta desde el carrito. en el entorno de desarrollo y se usa para actualizar el carrito.

Supongamos que tienes otra consulta de una aplicación de búsqueda de catálogos y asignas la cadena de etiqueta como app=catalogsearch,env=dev,action=list. Ahora bien, si alguno de estos en la tabla de estadísticas de consultas como consultas de latencia alta, identificar fácilmente la fuente usando la etiqueta.

Estos son algunos ejemplos de cómo se puede usar un patrón de etiquetado para organizar tus estadísticas de operación. Estos ejemplos no pretenden ser exhaustivos. puedes y, luego, combinarlos en su cadena de etiqueta con un delimitador, como una coma.

Claves de etiquetas Ejemplos de par etiqueta-valor Descripción
Aplicación app=cart
app=frontend
app=catalogsearch
Ayuda a identificar la aplicación que llama a la operación.
Entorno env=prod
env=dev
env=test
env=staging
Ayuda a identificar el entorno que está asociado con la operación.
Framework framework=spring
framework=django
framework=jetty
Ayuda a identificar el framework asociado con la operación.
Acción action=list
action=retrieve
action=update
Ayuda a identificar la acción que realiza la operación.
Servicio service=payment
service=shipping
Ayuda a identificar el microservicio que llama a la operación.

Observe lo siguiente:

  • Cuando asignas un REQUEST_TAG, las estadísticas de varias consultas que tienen la misma cadena de etiqueta se agrupan en una sola fila en estadísticas de consulta desde una tabla de particiones. Solo se muestra el texto de una de esas consultas en el campo TEXT.
  • Cuando asignas un REQUEST_TAG, las estadísticas de múltiples lecturas que tienen la misma cadena de etiqueta se agrupan en una sola fila en estadísticas de lectura desde una tabla de particiones. El conjunto de todas las columnas que se leen se agrega a READ_COLUMNS. .
  • Cuando asignas un TRANSACTION_TAG, las estadísticas de las transacciones que tienen la misma cadena de etiqueta se agrupan en una sola fila en las estadísticas de transacciones desde una tabla de particiones. Se agrega el conjunto de todas las columnas que escriben las transacciones. al campo WRITE_CONSTRUCTIVE_COLUMNS y al conjunto de todas las columnas que se agregan al campo READ_COLUMNS.

Situaciones de solución de problemas con etiquetas

Encuentra el origen de una transacción problemática

La siguiente consulta devuelve los datos sin procesar de las transacciones principales de la el período seleccionado.

SELECT
 fprint,
 transaction_tag,
 ROUND(avg_total_latency_seconds,4) as avg_total_latency_sec,
 ROUND(avg_commit_latency_seconds,4) as avg_commit_latency_sec,
 commit_attempt_count,
 commit_abort_count
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
WHERE interval_end = "2020-05-17T18:40:00"
ORDER BY avg_total_latency_seconds DESC;

En la tabla siguiente, se muestran datos de ejemplo de nuestra consulta, en los que tenemos tres aplicaciones: cart, product y frontend, que pertenecen o consultan la misma base de datos.

Una vez que identifiques las transacciones que experimentan alta latencia, puedes usar el etiquetas asociadas para identificar la parte relevante del código de la aplicación a solucionar más problemas con estadísticas de transacciones.

fprint transaction_tag avg_total_latency_sec avg_commit_latency_sec commit_attempt_count commit_abort_count
7129109266372596045 app=cart,service=order 0.3508 0.0139 278802 142205
9353100217060788102 app=cart,service=redis 0.1633 0.0142 129012 27177
9353100217060788102 app=product,service=payment 0.1423 0.0133 5357 636
898069986622520747 app=product,service=shipping 0.0159 0.0118 4269 1
9521689070912159706 app=frontend,service=ads 0.0093 0.0045 164 0
11079878968512225881 [string vacía] 0.031 0.015 14 0

Del mismo modo, se puede usar la etiqueta de solicitud para encontrar la fuente de una consulta problemática en la tabla estadísticas de consultas y la fuente de lectura problemática en la tabla lectura de estadísticas.

Encuentra la latencia y otras estadísticas de las transacciones de una aplicación o un microservicio en particular

Si usaste el nombre de la aplicación o el nombre del microservicio en la cadena de etiquetas, ayuda a filtrar la tabla de estadísticas de transacciones por etiquetas que contengan ese el nombre de la aplicación o el nombre del microservicio.

Supongamos que agregaste nuevas transacciones a la app de payment y quieres las latencias y otras estadísticas de esas transacciones nuevas. Si tienes utilizado el nombre de la aplicación de pagos en la etiqueta, puede filtrar el la tabla de estadísticas de transacciones solo para las etiquetas que contienen app=payment.

La siguiente consulta devuelve las estadísticas de transacción de la app de pagos a lo largo en intervalos de 10 minutos.

SELECT
  transaction_tag,
  avg_total_latency_sec,
  avg_commit_latency_sec,
  commit_attempt_count,
  commit_abort_count
FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
WHERE STARTS_WITH(transaction_tag, "app=payment")
LIMIT 3;

Este es un resultado de ejemplo:

transaction_tag avg_total_latency_sec avg_commit_latency_sec commit_attempt_count commit_abort_count
app=payment,action=update 0.3508 0.0139 278802 142205
app=payment,action=transfer 0.1633 0.0142 129012 27177
app=payment, action=retrieve 0.1423 0.0133 5357 636

Del mismo modo, puedes encontrar consultas o lecturas de una aplicación específica en estadísticas de consulta o de lectura de estadísticas con .

Cómo descubrir las transacciones involucradas en un conflicto de bloqueo

Para descubrir qué transacciones y claves de fila experimentaron tiempos de espera altos de bloqueo, consultamos la tabla LOCK_STAT_TOP_10MINUTE, que enumera las claves de fila, columnas y las transacciones correspondientes que intervienen en el conflicto.

SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
       t.total_lock_wait_seconds,
       s.lock_wait_seconds,
       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
       s.sample_lock_requests
FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
WHERE
  t.interval_end = "2020-05-17T18:40:00" and s.interval_end = t.interval_end;

Este es un resultado de ejemplo de nuestra consulta:

row_range_start_key total_lock_wait_seconds lock_wait_seconds frac_of_total sample_lock_requests
Singers(32) 2.37 1.76 1 LOCK_MODE: WriterShared
COLUMN: Singers.SingerInfo
TRANSACTION_TAG:
app=cart,service=order

LOCK_MODE: ReaderShared
COLUMN: Singers.SingerInfo
TRANSACTION_TAG:
app=cart,service=redis

En esta tabla de resultados, podemos ver que el conflicto ocurrió en Singers de clave SingerId=32. Singers.SingerInfo es la columna en la que el valor conflicto de bloqueo entre ReaderShared y WriterShared También puedes identifique las transacciones correspondientes (app=cart,service=order y app=cart,service=redis) que están experimentando el conflicto.

Una vez que se identifican las transacciones que causan los conflictos de bloqueo, puedes Enfóquese en estas transacciones con las estadísticas de transacciones. para tener una mejor idea de lo que hacen las transacciones o reducir el tiempo durante el cual se mantienen los bloqueos. Para obtener más información, Consulta Prácticas recomendadas para reducir la contención de bloqueo.

¿Qué sigue?