Fehlerbehebung mit Anfrage- und Transaktions-Tags

Spanner bietet eine Reihe von integrierten Statistiktabellen, mit denen Sie Einblicke Abfragen, Lesevorgängen und Transaktionen analysieren. Um Statistiken mit Ihren und zur Verbesserung der Fehlerbehebung können Sie ein Tag (ein Tag im freien Format String) an Spanner-Lese-, Abfrage- und Transaktionsvorgänge in Ihrem Anwendungscode. Diese Tags werden in Statistiktabellen eingetragen, sodass Sie basierend auf Tags korrelieren und suchen können.

Spanner unterstützt zwei Arten von Tags: request-Tags und transaction-Tags. Wie die Namen vermuten lassen, können Sie Transaktions-Tags einer Transaktion hinzufügen und Anfrage-Tags an einzelne Abfragen und Lese-APIs. Sie können ein Transaktions-Tag auf Transaktionsebene festlegen und einzelne Anfrage-Tags für jede anwendbare API-Anfrage in der Transaktion festlegen. Anfrage- und Transaktions-Tags, die im Anwendungscode festgelegt sind, werden in die Spalten der folgenden Statistiktabellen eingetragen.

Statistiktabelle In der Statistiktabelle eingetragene Tag-Arten
Statistiken zu TopN-Abfragen Anfrage-Tags
Statistiken zu TopN-Lesevorgängen Anfrage-Tags
Statistiken zur TopN-Transaktionen Transaktions-Tags
Statistiken zu TopN-Sperren Transaktions-Tags

Anfrage-Tags

Sie können einer Abfrage oder einer Leseanfrage ein optionales Anfrage-Tag hinzufügen. Spanner gruppiert Statistiken nach Anfrage-Tag, das im Feld REQUEST_TAG von sowohl die Abfragestatistiken und Statistiken lesen Tabellen.

Wann werden Anfrage-Tags verwendet?

Im Folgenden sind einige Szenarien aufgeführt, die sich für die Verwendung von Anfrage-Tags eignen.

  • Quelle einer problematischen Abfrage oder eines problematischen Lesevorgangs ermitteln: Spanner erfasst Statistiken für Lesevorgänge und Abfragen in integrierten Statistiktabellen. Wenn Sie in der Statistiktabelle langsame Abfragen oder Lesevorgänge mit hoher CPU-Auslastung sehen, denen Sie bereits Tags zugewiesen haben, können Sie auf Basis der Informationen im Tag die Quelle (Anwendung/Mikrodienst) identifizieren, die diese Vorgänge aufruft.
  • Lesevorgänge oder Abfragen in Statistiktabellen identifizieren: Durch Zuweisen von Anfrage-Tags können Zeilen in der Statistiktabelle anhand der für Sie interessanten Tags gefiltert werden.
  • Ermitteln, ob Abfragen von einer bestimmten Anwendung oder einem bestimmten Mikrodienst langsam sind: Anfrage-Tags können dabei helfen, zu ermitteln, ob Abfragen einer bestimmten Anwendung oder eines bestimmten Mikrodienstes höhere Latenzen haben.
  • Statistiken für mehrere Lesevorgänge oder Abfragen gruppieren: Sie können Anfrage-Tags verwenden, um die Leistung für eine Reihe ähnlicher Lese- oder Abfragevorgänge zu verfolgen, zu vergleichen und zu melden. Wenn beispielsweise mehrere Abfragen auf eine Tabelle oder einen Tabellensatz mit demselben Zugriffsmuster zugreifen, können Sie allen diese Abfragen dasselbe Tag hinzufügen, um sie gemeinsam zu verfolgen.

So weisen Sie Anfrage-Tags zu

Im folgenden Beispiel wird gezeigt, wie Anfrage-Tags mit dem Spanner- Clientbibliotheken.

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

So rufen Sie Anfrage-Tags in der Statistiktabelle auf

Die folgende Abfrage gibt die Abfragestatistiken in Intervallen von zehn Minuten zurück.

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;

Nehmen wir als Beispiel die folgenden Ergebnisse zu unserer Abfrage:

text 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
* aus den Bestellungen auswählen; app=catalogsearch,env=dev,action=list 55 0,02 16 33,35
SELECT SingerId, FirstName, LastName FROM Singers; [Leerer String] 154 0,048 42 486,33

In dieser Ergebnistabelle können Sie sehen, dass eine Abfrage, der Sie einen REQUEST_TAG zugewiesen haben, in der Statistiktabelle erfasst wird. Wenn kein Anfrage-Tag zugewiesen ist, wird sie als leerer String angezeigt.

Für die getaggten Abfragen werden die Statistiken pro Tag aggregiert. Beispiel: Das Anfrage-Tag app=concert,env=dev,action=select hat eine durchschnittliche Latenz von 0,025 Sekunden. Wenn kein Tag zugewiesen ist, werden die Statistiken pro Abfrage aggregiert. Beispiel: Die Abfrage in der dritten Zeile hat eine durchschnittliche Latenz von 0,048 Sekunden.

Transaktions-Tags

Ein optionales Transaktions-Tag kann einzelnen Transaktionen hinzugefügt werden. Spanner gruppiert Statistiken nach Transaktions-Tag, das in der Feld TRANSACTION_TAG von Transaktionsstatistiken Tabellen.

Wann werden Transaktions-Tags verwendet?

Im Folgenden sind einige Szenarien aufgeführt, die sich für die Verwendung von Transaktions-Tags eignen.

  • Quelle einer problematischen Transaktion ermitteln:Spanner Erfasst Statistiken für Lese-Schreib-Transaktionen in der Transaktion Statistiktabelle verwendet. Wenn Sie in der Tabelle mit den Transaktionsstatistiken langsame Transaktionen sehen, denen Sie bereits Tags zugewiesen haben, können Sie auf Basis der Informationen im Tag die Quelle (Anwendung/Mikrodienst) ermitteln, die diese Transaktionen aufruft.
  • Transaktionen in Statistiktabellen identifizieren: Durch Zuweisen von Transaktions-Tags können Zeilen in der Tabelle mit den Transaktionsstatistiken anhand der für Sie interessanten Tags gefiltert werden. Ohne Transaktions-Tags kann es mühsam sein, zu ermitteln, für welchen Vorgang eine Statistik steht. Für Transaktionsstatistiken müssten Sie beispielsweise die entsprechenden Tabellen und Spalten überprüfen, um die nicht getaggte Transaktion zu identifizieren.
  • Ermitteln, ob Transaktionen von einer bestimmten Anwendung oder einem bestimmten Mikrodienst langsam sind: Mit Transaktions-Tags können Sie feststellen, ob Transaktionen einer bestimmten Anwendung oder eines bestimmten Mikrodienstes höhere Latenzen haben.
  • Statistiken für mehrere Transaktionen gruppieren: Sie können Transaktions-Tags verwenden, um die Leistung für eine Reihe ähnlicher Transaktionen zu verfolgen, zu vergleichen und zu melden.
  • Ermitteln, welche Transaktionen auf die Spalten zugreifen, die am Sperrkonflikt beteiligt sind: Durch Transaktions-Tags können in den Tabelle mit den Sperrstatistiken präzise einzelne Transaktionen angezeigt werden, die Sperrkonflikte verursachen.
  • Änderungsdaten von Nutzern aus Spanner mit Änderungsstreams streamen: Datensätze zu Änderungsstreams enthalten Transaktions-Tags für die Transaktionen die die Nutzerdaten geändert haben. So können die Leser eines Änderungsstreams Änderungen anhand von Tags mit dem Transaktionstyp verknüpfen.

So weisen Sie Transaktions-Tags zu

Im folgenden Beispiel wird gezeigt, wie Transaktions-Tags mit Spanner festgelegt werden. Clientbibliotheken. Wenn Sie eine Clientbibliothek verwenden, können Sie zu Beginn des Transaktionsaufrufs ein Transaktions-Tag festlegen, das auf alle einzelnen Vorgänge innerhalb dieser Transaktion angewendet wird.

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

Transaktions-Tags in der Tabelle mit den Transaktionsstatistiken anzeigen

Die folgende Abfrage gibt die Transaktionsstatistiken in Intervallen von 10 Minuten zurück.

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;

Nehmen wir als Beispiel die folgenden Ergebnisse zu unserer Abfrage:

fprint transaction_tag read_columns commit_attempt_count avg_total_latency_seconds
40015598317 app=concert,env=dev [Venues._exists,
Venues.VenueId,
Venues.VenueName,
Venues.Capacity]
278802 0,3508
20524969030 app=product,service=payment [Singers.SingerInfo] 129012 0.0142
77848338483 [Leerer String] [Singers.FirstName, Singers.LastName, Singers._exists] 5357 0,048

In dieser Ergebnistabelle sehen Sie, dass eine Transaktion, der Sie einen TRANSACTION_TAG zugewiesen haben, in der Tabelle mit den Transaktionsstatistiken erfasst wird. Wenn kein Transaktions-Tag zugewiesen ist, wird sie als leerer String angezeigt.

Für die getaggten Transaktionen werden die Statistiken pro Transaktions-Tag zusammengefasst. Beispiel: Das Transaktions-Tag app=concert,env=dev hat eine durchschnittliche Latenz von 0,3508 Sekunden. Wenn kein Tag zugewiesen ist, werden die Statistiken pro FPRINT aggregiert. Beispiel: 77848338483 in der dritten Zeile hat eine durchschnittliche Latenz von 0,048 Sekunden.

Transaktions-Tags in der Tabelle mit den Sperrstatistiken anzeigen

Die folgende Abfrage gibt die Sperrstatistiken über Intervalle von 10 Minuten zurück.

Die Funktion CAST() konvertiert das BYTES-Feld row_range_start_key in einen 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;

Nehmen wir als Beispiel die folgenden Ergebnisse zu unserer Abfrage:

row_range_start_key lock_wait_seconds sample_lock_requests
Songs(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
Alben(2,1+) 0,48 LOCK_MODE: ReaderShared
COLUMN: users._exists1
TRANSACTION_TAG: [empty string]

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

In dieser Ergebnistabelle sehen Sie, dass eine Transaktion, der Sie einen TRANSACTION_TAG zugewiesen haben, in der Tabelle mit den Sperrstatistiken erfasst wird. Wenn kein Transaktions-Tag zugewiesen ist, wird sie als leerer String angezeigt.

Zuordnung zwischen API-Methoden und Anfrage-/Transaktions-Tag

Anfrage- und Transaktions-Tags können auf bestimmte API-Methoden angewendet werden, Gibt an, ob der Transaktionsmodus eine schreibgeschützte Transaktion oder eine Lese-/Schreibtransaktion ist Transaktion. Im Allgemeinen gelten Transaktions-Tags für Lese-/Schreibvorgänge. Transaktionen, während Anfrage-Tags auf schreibgeschützte Transaktionen anwendbar sind. Die folgende Tabelle zeigt die Zuordnung von API-Methoden zu anwendbaren Typen von Tags.

API-Methoden Transaktionsmodi Anfrage-Tag Transaktions-Tag
Read,
StreamingRead
Schreibgeschützte Transaktion Ja Nein
Lese-/Schreibtransaktion Ja Ja
ExecuteSql,
ExecuteStreamingSql1
Schreibgeschützte Transaktion1 Ja1 Nein
Lese-/Schreibtransaktion Ja Ja
ExecuteBatchDml Lese-/Schreibtransaktion Ja Ja
BeginTransaction Lese-/Schreibtransaktion Nein Ja
Commit Lese-/Schreibtransaktion Nein Ja

1 Für Änderungsstreamabfragen, die mit Apache Beam SpannerIO ausgeführt werden Dataflow-Connector: REQUEST_TAG enthält den Namen eines Dataflow-Jobs.

Beschränkungen

Beachten Sie beim Hinzufügen von Tags zu Ihren Lesevorgängen, Abfragen und Transaktionen die folgenden Einschränkungen:

  • Die Länge eines Tag-Strings ist auf 50 Zeichen begrenzt. Strings, die dieses Limit überschreiten, werden abgeschnitten.
  • In einem Tag sind nur ASCII-Zeichen (32–126) zulässig. Beliebige Unicode-Zeichen werden durch Unterstriche ersetzt.
  • Alle vorangestellten Unterstriche (_) werden aus dem String entfernt.
  • Bei Tags wird zwischen Groß- und Kleinschreibung unterschieden. Wenn Sie z. B. das Anfrage-Tag APP=cart,ENV=dev zu einem Satz von Abfragen und fügen Sie app=cart,env=dev zu weitere Abfragen ausführen, aggregiert Spanner Statistiken separat. für jedes Tag.
  • In den Statistiktabellen können Tags unter folgenden Umständen fehlen:

    • Wenn Spanner keine Statistiken für alle getaggten Operationen, die während des Intervalls in Tabellen ausgeführt werden, priorisiert das System Vorgänge mit den meisten Ressourcen während des angegebenen Zeitraums Intervall.

Tag-Benennung

Wenn Sie Ihren Datenbankvorgängen Tags zuweisen, ist es wichtig, welche Informationen Sie in den einzelnen Tag-Strings vermitteln möchten. Die von Ihnen gewählte Konvention oder das Muster macht Ihre Tags effektiver. Beispiel: Das richtige Tag Die Benennung erleichtert den Zusammenhang zwischen Statistiken und Anwendungscode.

Innerhalb der angegebenen Einschränkungen können Sie ein beliebiges Tag auswählen. Sie können jedoch Wir empfehlen, einen Tag-String aus mehreren Schlüssel/Wert-Paaren zu erstellen, die getrennt sind. durch Kommas getrennt.

Angenommen, Sie verwenden eine Spanner-Datenbank für eine im Bereich E-Commerce. Sie können Informationen über die Anwendung, Entwicklungsumgebung und der Aktion, die von der Abfrage in das Anfrage-Tag, das Sie einer bestimmten Abfrage zuweisen. Sie können weisen Sie den Tag-String im Schlüssel/Wert-Format app=cart,env=dev,action=update: Das bedeutet, dass die Abfrage vom Einkaufswagen aus aufgerufen wird. -Anwendung in der Entwicklungsumgebung und wird zum Aktualisieren des Warenkorbs verwendet.

Angenommen, Sie haben eine weitere Abfrage aus einer Katalog-Suchanwendung und weisen den Tag-String als app=catalogsearch,env=dev,action=list zu. Wenn eine dieser Abfragen in der Abfragestatistiktabelle als Abfragen mit hoher Latenz angezeigt wird, können Sie die Quelle einfach durch das Tag identifizieren.

Hier sind einige Beispiele dafür, wie Sie Ihre Kampagnen mithilfe eines Vorgangsstatistiken. Die folgenden Beispiele erheben keinen Anspruch auf Vollständigkeit. können Sie auch in Ihrem Tag-String mit einem Trennzeichen wie einem Komma kombinieren.

Tag-Schlüssel Beispiele für Tag-Wert-Paare Beschreibung
Anwendung app=cart
app=frontend
app=catalogsearch
Hilft beim Identifizieren der Anwendung, die den Vorgang aufruft.
Umgebung env=prod
env=dev
env=test
env=staging
Hilft beim Identifizieren der Umgebung, die mit dem Vorgang verknüpft ist.
Framework framework=spring
framework=django
framework=jetty
Hilft beim Identifizieren des Frameworks, das mit dem Vorgang verknüpft ist.
Aktion action=list
action=retrieve
action=update
Hilft beim Identifizieren der vom Vorgang ausgeführten Aktion.
Dienst service=payment
service=shipping
Hilft beim Identifizieren des Mikrodiensts, der den Vorgang aufruft.

Weitere Hinweise

  • Wenn Sie ein REQUEST_TAG zuweisen, werden Statistiken für mehrere Abfragen mit desselben Tag-Strings in der Abfragestatistik in einer einzigen Zeile gruppiert werden . Im Feld TEXT wird nur der Text einer dieser Suchanfragen angezeigt.
  • Wenn Sie ein REQUEST_TAG zuweisen, werden Statistiken für mehrere Lesevorgänge mit demselben Tag-String in einer einzigen Zeile in der Lesestatistiktabelle gruppiert. Die gelesenen Spalten werden dem Feld READ_COLUMNS hinzugefügt.
  • Wenn Sie ein TRANSACTION_TAG zuweisen, werden Statistiken für Transaktionen mit demselben Tag-String in einer einzigen Zeile in der Tabelle mit den Transaktionsstatistiken gruppiert. Alle Spalten, die von den Transaktionen geschrieben werden, werden dem Feld WRITE_CONSTRUCTIVE_COLUMNS und die gelesenen Spalten dem Feld READ_COLUMNS hinzugefügt.

Fehlerbehebungsszenarien mit Tags

Quelle einer problematischen Transaktion ermitteln

Die folgende Abfrage gibt die Rohdaten für die Top-Transaktionen in der ausgewählten Zeitraum.

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;

Die folgende Tabelle enthält Beispieldaten, die von unserer Abfrage zurückgegeben wurden, wobei wir drei Anwendungen haben, nämlich Warenkorb, Produkt und Frontend, die dieselbe Datenbank besitzen oder abfragen.

Wenn Sie die Transaktionen mit hoher Latenz ermittelt haben, können Sie die zugehörigen Tags verwenden, um den relevanten Teil Ihres Anwendungscodes zu identifizieren und Fehler mithilfe von Transaktionsstatistiken weiter zu beheben.

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 [Leerer String] 0,031 0,015 14 0

Ebenso kann das Anfrage-Tag verwendet werden, um die Quelle einer problematischen Abfrage aus der Abfragestatistiktabelle und die Quelle eines problematischen Lesevorgangs aus der Lesestatistiktabelle zu ermitteln.

Latenz und andere Statistiken für Transaktionen aus einer bestimmten Anwendung oder einem bestimmten Mikrodienst ermitteln

Wenn Sie den Namen der Anwendung oder des Mikrodienstes im Tag-String verwendet haben, erleichtert das die Filterung der Tabelle mit den Transaktionsstatistiken nach Tags, die diesen Anwendungsnamen oder Mikrodienstnamen enthalten.

Angenommen, Sie haben der Zahlungs-App neue Transaktionen hinzugefügt und möchten sich die Latenz und andere Statistiken dieser neuen Transaktionen ansehen. Wenn Sie den Namen der Zahlungsanwendung innerhalb des Tags verwendet, können Sie die Transaktionsstatistiktabelle nur für die Tags, die app=payment enthalten.

Die folgende Abfrage gibt die Transaktionsstatistiken für die Zahlungs-App über Intervalle von 10 Minuten.

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;

Hier einige Ausgabebeispiele:

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

Ebenso können Sie Abfragen oder Lesevorgänge aus einer bestimmten Anwendung in der Abfragestatistiktabelle oder in der Lesestatistiktabelle mithilfe von Anfrage-Tags ermitteln.

Transaktionen im Zusammenhang mit Sperrenkonflikten ermitteln

Um festzustellen, für welche Transaktionen und Zeilenschlüssel die hohen Wartezeiten für Sperren aufgetreten sind, fragen wir die Tabelle LOCK_STAT_TOP_10MINUTE ab, in der die Zeilenschlüssel, Spalten und entsprechenden Transaktionen aufgeführt sind, die am Sperrkonflikt beteiligt sind.

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;

Hier einige Ausgabebeispiele aus unserer Abfrage:

row_range_start_key total_lock_wait_seconds lock_wait_seconds frac_of_total sample_lock_requests
Sänger(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

In dieser Ergebnistabelle sehen Sie, dass der Konflikt in der Tabelle Singers unter dem Schlüssel SingerId=32 aufgetreten ist. Singers.SingerInfo ist die Spalte, in der der Sperrkonflikt zwischen ReaderShared und WriterShared ist aufgetreten. Sie können auch Identifizieren der entsprechenden Transaktionen (app=cart,service=order und app=cart,service=redis), die von dem Konflikt betroffen sind.

Sobald die Transaktionen identifiziert wurden, die die Sperrkonflikte verursacht haben, können Sie sich mithilfe von Transaktionsstatistiken auf diese Transaktionen konzentrieren, um besser zu verstehen, was die Transaktionen tun und ob Sie Konflikte vermeiden können oder die Zeit verkürzen können, für die die Sperren aufrechterhalten werden. Weitere Informationen finden Sie unter Best Practices zur Reduzierung von Sperrkonflikten.

Nächste Schritte