Lecturas fuera de las transacciones

En esta página se describe cómo realizar lecturas en Spanner fuera del contexto de las transacciones de solo lectura y de lectura y escritura. Si se da alguna de las siguientes situaciones, consulta la página Transacciones:

  • Si necesitas escribir en función del valor de una o varias lecturas, debes ejecutar la lectura como parte de una transacción de lectura y escritura. Para obtener más información, consulta Transacciones de lectura y escritura.

  • Si vas a hacer varias llamadas de lectura que requieran una vista coherente de tus datos, debes ejecutar las lecturas como parte de una transacción de solo lectura. Para obtener más información, consulta el artículo sobre transacciones de solo lectura.

Leer tipos

Spanner te permite determinar la actualidad de los datos cuando los lees. Para ello, ofrece dos tipos de lecturas:

  • Una lectura sólida es una lectura en una marca de tiempo actual y tiene la garantía de ver todos los datos que se han confirmado hasta el inicio de esta lectura. Spanner usa de forma predeterminada lecturas sólidas para atender las solicitudes de lectura.
  • Una lectura inactiva se lee en una marca de tiempo en el pasado. Si tu aplicación es sensible a la latencia, pero tolera los datos obsoletos, las lecturas obsoletas pueden ofrecer ventajas de rendimiento.

Para elegir el tipo de lectura que quieras, define un límite de marca de tiempo en la solicitud de lectura. Sigue estas prácticas recomendadas al elegir un límite de marca de tiempo:

  • Elige lecturas fuertes siempre que sea posible. Esta es la marca de tiempo predeterminada de las lecturas de Spanner, incluidas las transacciones de solo lectura. Se garantiza que las lecturas sólidas observarán los efectos de todas las transacciones que se hayan confirmado antes del inicio de la operación, independientemente de la réplica que reciba la lectura. Por este motivo, las lecturas sólidas simplifican el código de las aplicaciones y hacen que estas sean más fiables. Consulta más información sobre las propiedades de coherencia de Spanner en TrueTime y coherencia externa.

    De forma opcional, para reducir la latencia de lectura de las transacciones de solo lectura que requieren una coherencia sólida, puedes asignar el estado región de concesión de lectura a regiones que no sean líderes, de lectura y escritura o de solo lectura. Para ello, usa concesiones de lectura de Spanner. Las regiones de concesión de lectura ayudan a tu base de datos a reducir la latencia de lectura fuerte en instancias birregionales o multirregionales. Sin embargo, las escrituras experimentan una latencia mayor cuando usas el bloqueo de lectura.

  • Si la latencia hace que las lecturas sólidas no sean viables en algunas situaciones, utiliza lecturas obsoletas (obsolescencia limitada u obsolescencia exacta) para mejorar el rendimiento en los casos en los que no necesites que las lecturas sean lo más recientes posible. Como se describe en la página Replicación, 15 segundos es un valor de obsolescencia razonable para obtener un buen rendimiento.

Leer datos con un rol de base de datos

Si eres usuario del control de acceso pormenorizado, debes seleccionar un rol de base de datos para ejecutar instrucciones y consultas SQL, así como para realizar operaciones de filas en una base de datos. El rol que elijas se mantendrá durante toda la sesión hasta que lo cambies.

Para obtener instrucciones sobre cómo realizar una lectura con un rol de base de datos, consulta Acceder a una base de datos con un control de acceso pormenorizado.

Métodos de lectura única

Spanner admite métodos de lectura única (es decir, una lectura fuera del contexto de una transacción) en una base de datos para lo siguiente:

  • Ejecutar la lectura como una instrucción de consulta de SQL o mediante la API de lectura de Spanner.
  • Realizar una lectura fuerte desde una sola fila o varias filas en una tabla.
  • Realizar una lectura inactiva de una sola fila o varias filas en una tabla.
  • Leer desde una sola fila o varias filas en un índice secundario.

Si quieres dirigir lecturas únicas a una réplica o región específicas en una configuración de instancia multirregional o en una configuración regional personalizada con una o varias regiones de solo lectura opcionales, consulta Lecturas dirigidas.

En las siguientes secciones se describe cómo usar los métodos de lectura con las bibliotecas de cliente de Spanner.

Ejecutar una consulta

A continuación, se muestra cómo ejecutar una instrucción de consulta SQL en una base de datos.

GoogleSQL

C++

Usa ExecuteQuery() para ejecutar una instrucción de consulta SQL en una base de datos.

void QueryData(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::SqlStatement select("SELECT SingerId, LastName FROM Singers");
  using RowType = std::tuple<std::int64_t, std::string>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "LastName: " << std::get<1>(*row) << "\n";
  }

  std::cout << "Query completed for [spanner_query_data]\n";
}

C#

Usa ExecuteReaderAsync() para consultar la base de datos.


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

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

    public async Task<List<Album>> QuerySampleDataAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        var albums = new List<Album>();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");

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

Go

Usa Client.Single().Query para consultar la base de datos.


import (
	"context"
	"fmt"
	"io"

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

func query(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: `SELECT 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)
	}
}

Java

Usa ReadContext.executeQuery para consultar la base de datos.

static void query(DatabaseClient dbClient) {
  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\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Node.js

Usa Database.run para consultar la base de datos.

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

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

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

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

const query = {
  sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
};

// Queries rows from the Albums table
try {
  const [rows] = await database.run(query);

  rows.forEach(row => {
    const json = row.toJSON();
    console.log(
      `SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`,
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  await database.close();
}

PHP

Usa Database::execute para consultar la base de datos.

use Google\Cloud\Spanner\SpannerClient;

/**
 * Queries sample data from the database using SQL.
 * Example:
 * ```
 * query_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function query_data(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $results = $database->execute(
        'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'
    );

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

Python

Usa Database.execute_sql para consultar la base de datos.

def query_data(instance_id, database_id):
    """Queries sample data from the database using SQL."""
    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"
        )

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

Ruby

Usa Client#execute para consultar la base de datos.

# 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, AlbumTitle FROM Albums").rows.each do |row|
  puts "#{row[:SingerId]} #{row[:AlbumId]} #{row[:AlbumTitle]}"
end

Consulta las referencias de sintaxis de consultas y funciones y operadores de SQL al crear una instrucción SQL.

Realizar una lectura fuerte

A continuación, se muestra cómo realizar una lectura fuerte de cero o más filas desde una base de datos.

GoogleSQL

C++

El código para leer datos es el mismo que en el ejemplo anterior para consultar Spanner ejecutando una consulta SQL.

void QueryData(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::SqlStatement select("SELECT SingerId, LastName FROM Singers");
  using RowType = std::tuple<std::int64_t, std::string>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "LastName: " << std::get<1>(*row) << "\n";
  }

  std::cout << "Query completed for [spanner_query_data]\n";
}

C#

El código para leer datos es el mismo que en el ejemplo anterior para consultar Spanner ejecutando una consulta SQL.


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

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

    public async Task<List<Album>> QuerySampleDataAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        var albums = new List<Album>();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");

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

Go

Usa Client.Single().Read para leer filas de la base de datos.


import (
	"context"
	"fmt"
	"io"

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

func read(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}