Membaca di luar transaksi

Halaman ini menjelaskan cara melakukan pembacaan di Spanner di luar konteks transaksi hanya baca dan baca-tulis. Jika salah satu hal berikut berlaku, Anda harus membaca halaman Transaksi:

  • Jika Anda perlu menulis, bergantung pada nilai satu atau beberapa pembacaan, Anda harus menjalankan pembacaan sebagai bagian dari transaksi baca-tulis. Untuk mengetahui informasi selengkapnya, lihat transaksi baca-tulis.

  • Jika Anda melakukan beberapa panggilan baca yang memerlukan tampilan data yang konsisten, Anda harus menjalankan pembacaan sebagai bagian dari transaksi hanya baca. Untuk mengetahui informasi selengkapnya, lihat transaksi hanya baca.

Jenis bacaan

Spanner memungkinkan Anda menentukan seberapa baru data yang harus ada saat Anda membaca data dengan menawarkan dua jenis pembacaan:

  • Pembacaan yang andal adalah pembacaan pada stempel waktu saat ini dan dijamin akan melihat semua data yang telah di-commit hingga awal pembacaan ini. Spanner secara default menggunakan operasi baca yang kuat untuk melayani permintaan baca.
  • Pembacaan basi dibaca pada stempel waktu di masa lalu. Jika aplikasi Anda sensitif terhadap latensi tetapi toleran terhadap data yang sudah tidak berlaku, maka pembacaan data yang sudah tidak berlaku dapat memberikan manfaat performa.

Untuk memilih jenis pembacaan yang Anda inginkan, tetapkan batas stempel waktu pada permintaan pembacaan. Gunakan praktik terbaik berikut saat memilih batas stempel waktu:

  • Pilih bacaan yang kuat jika memungkinkan. Ini adalah batas stempel waktu default untuk pembacaan Spanner, termasuk transaksi hanya baca. Pembacaan kuat dijamin akan mengamati efek semua transaksi yang di-commit sebelum dimulainya operasi, terlepas dari replika mana yang menerima pembacaan. Oleh karena itu, pembacaan kuat membuat kode aplikasi lebih sederhana dan aplikasi lebih tepercaya. Baca selengkapnya tentang properti konsistensi Spanner di TrueTime and External Consistency.

    Secara opsional, untuk mengurangi latensi baca untuk transaksi hanya baca yang memerlukan konsistensi kuat, Anda dapat memberikan status region sewa baca kepada region non-leader, baca-tulis, atau hanya baca. Untuk melakukannya, gunakan lease baca Spanner. Region sewa baca membantu database Anda mengurangi latensi operasi baca yang kuat di instance dual-region atau multi-region. Namun, penulisan mengalami latensi yang lebih tinggi saat Anda menggunakan sewa baca.

  • Jika latensi membuat pembacaan kuat tidak dapat dilakukan dalam beberapa situasi, gunakan pembacaan tidak berlaku (keterbatasan tidak berlaku atau tidak berlaku persis) untuk meningkatkan performa di tempat yang tidak memerlukan pembacaan yang seakurat mungkin. Seperti yang dijelaskan di halaman Replikasi, 15 detik adalah nilai tidak berlaku yang wajar untuk digunakan agar performa baik.

Membaca data dengan peran database

Jika Anda adalah pengguna kontrol akses terperinci, Anda harus memilih peran database untuk menjalankan pernyataan dan kueri SQL, serta melakukan operasi baris pada database. Pilihan peran Anda akan tetap ada selama sesi Anda hingga Anda mengubah peran.

Untuk mengetahui petunjuk tentang cara melakukan pembacaan dengan peran database, lihat Mengakses database dengan kontrol akses terperinci.

Metode baca tunggal

Spanner mendukung metode baca tunggal (yaitu, baca di luar konteks transaksi) di database untuk:

  • Mengeksekusi pembacaan sebagai pernyataan kueri SQL atau menggunakan read API Spanner.
  • Melakukan pembacaan yang kuat dari satu baris atau beberapa baris dalam tabel.
  • Melakukan pembacaan basi dari satu baris atau beberapa baris dalam tabel.
  • Membaca dari satu baris atau beberapa baris dalam indeks sekunder.

Jika Anda ingin merutekan pembacaan tunggal ke replika atau region tertentu dalam konfigurasi instance multi-region atau konfigurasi regional kustom dengan region hanya baca opsional, lihat Pembacaan terarah.

Bagian berikut menjelaskan cara menggunakan metode baca menggunakan library klien Spanner.

Menjalankan kueri

Berikut ini menunjukkan cara menjalankan pernyataan kueri SQL terhadap database.

GoogleSQL

C++

Gunakan ExecuteQuery() untuk menjalankan pernyataan kueri SQL terhadap database.

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#

Gunakan ExecuteReaderAsync() untuk membuat kueri database.


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

Gunakan Client.Single().Query untuk membuat kueri database.


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

Gunakan ReadContext.executeQuery untuk membuat kueri database.

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

Gunakan Database.run untuk membuat kueri database.

// 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

Gunakan Database::execute untuk membuat kueri database.

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

Gunakan Database.execute_sql untuk membuat kueri database.

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

Gunakan Client#execute untuk membuat kueri database.

# 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

Lihat referensi Sintaksis Kueri dan Fungsi dan Operator SQL saat membuat pernyataan SQL.

Lakukan pembacaan yang kuat

Berikut ini menunjukkan cara melakukan pembacaan kuat nol atau beberapa baris dari database.

GoogleSQL

C++

Kode untuk membaca data sama dengan contoh sebelumnya untuk mengkueri Spanner dengan menjalankan kueri 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#

Kode untuk membaca data sama dengan contoh sebelumnya untuk mengkueri Spanner dengan menjalankan kueri 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

Gunakan Client.Single().Read untuk membaca baris dari database.


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
	}
	defer client.Close()

	iter := client.Single().Read(ctx, "Albums", spanner.AllKeys(),
		[]string{"SingerId", "AlbumId", "AlbumTitle"})
	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)
	}
}

Contoh ini menggunakan AllKeys untuk menentukan kumpulan kunci atau rentang kunci yang akan dibaca.

Java

Gunakan ReadContext.read untuk membaca baris dari database.

static void read(DatabaseClient dbClient) {
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .read(
              "Albums",
              KeySet.all(), // Read all rows in a table.
              Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Contoh ini menggunakan KeySet untuk menentukan kumpulan kunci atau rentang kunci yang akan dibaca.

Node.js

Gunakan Table.read untuk membaca baris dari database.

// 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);

// Reads rows from the Albums table
const albumsTable = database.table('Albums');

const query = {
  columns: ['SingerId', 'AlbumId', 'AlbumTitle'],
  keySet: {
    all: true,
  },
};

try {
  const [rows] = await albumsTable.read(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();
}

Contoh ini menggunakan keySet untuk menentukan kumpulan kunci atau rentang kunci yang akan dibaca.

PHP

Gunakan Database::read untuk membaca baris dari database.

use Google\Cloud\Spanner\SpannerClient;

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

    $keySet = $spanner->keySet(['all' => true]);
    $results = $database->read(
        'Albums',
        $keySet,
        ['SingerId', 'AlbumId', 'AlbumTitle']
    );

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

Contoh ini menggunakan keySet untuk menentukan kumpulan kunci atau rentang kunci yang akan dibaca.

Python

Gunakan Database.read untuk membaca baris dari database.

def read_data(instance_id, database_id):
    """Reads sample data from the database."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table="Albums", columns=("SingerId", "AlbumId", "AlbumTitle"), keyset=keyset
        )

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

Contoh ini menggunakan KeySet untuk menentukan kumpulan kunci atau rentang kunci yang akan dibaca.

Ruby

Gunakan Client#read untuk membaca baris dari database.

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

Melakukan pembacaan yang tidak berlaku

Kode contoh berikut menunjukkan cara melakukan stale read nol atau lebih baris dari database menggunakan batas stempel waktu exact-staleness. Untuk mengetahui petunjuk tentang cara melakukan stale read menggunakan batas stempel waktu bounded-staleness, lihat catatan setelah kode contoh. Lihat Batas stempel waktu untuk mengetahui informasi selengkapnya tentang berbagai jenis batas stempel waktu yang tersedia.

GoogleSQL

C++

Gunakan ExecuteQuery() dengan MakeReadOnlyTransaction() dan Transaction::ReadOnlyOptions() untuk melakukan pembacaan yang tidak terbaru.

void ReadStaleData(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  // The timestamp chosen using the `exact_staleness` parameter is bounded
  // below by the creation time of the database, so the visible state may only
  // include that generated by the `extra_statements` executed atomically with
  // the creation of the database. Here we at least know `Albums` exists.
  auto opts = spanner::Transaction::ReadOnlyOptions(std::chrono::seconds(15));
  auto read_only = spanner::MakeReadOnlyTransaction(std::move(opts));

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

  auto rows = client.ExecuteQuery(std::move(read_only), std::move(select));
  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#

Gunakan metode BeginReadOnlyTransactionAsync pada connection dengan nilai TimestampBound.OfExactStaleness() yang ditentukan untuk mengkueri database.