トランザクション外部の読み取り

このページでは、読み取り専用トランザクションと読み書きトランザクションのコンテキスト外で Spanner で読み取りを行う方法を説明します。次のいずれかに該当する場合は、トランザクションのページをご覧ください。

  • 1 つ以上の読み取り結果に応じて書き込みを行う必要がある場合は、読み取り / 書き込みトランザクションの中で読み取りを実行する必要があります。詳細については、読み取り / 書き込みトランザクションをご覧ください。

  • データの一貫したビューを取得するため、複数の読み取り呼び出しを行う場合、読み取り専用トランザクションの中で読み取りを実行する必要があります。詳細については、読み取り / 書き込みトランザクションをご覧ください。

読み取りのタイプ

Spanner には以下の 2 種類の読み取り方法が用意されており、これによってデータを読み取るときのデータの新しさを指定できます。

  • 「強力な読み取り」は現在のタイムスタンプでの読み取りであり、この読み取りの開始時までに commit されたすべてのデータを確実に確認できます。Spanner のデフォルトでは、強力な読み取りを使用して読み取りリクエストが処理されます。
  • 「ステイル読み取り」は、過去のタイムスタンプによる読み取りです。レイテンシの影響を受けやすいものの古いデータは許容できるアプリケーションの場合、ステイル読み取りを使用するとパフォーマンスが向上することがあります。

使用する読み取りのタイプを選択するには、読み取りリクエストでタイムスタンプ バウンドを設定します。タイムスタンプ バウンドを選択する際は、次のおすすめの方法を参考にしてください。

  • 可能な限り、強力な読み取りを選択してください。これは Spanner の読み取り(読み取り専用トランザクションを含む)におけるデフォルトのタイムスタンプ バウンドです。強力な読み取りでは、どのレプリカが読み取りを受け取るかに関係なく、読み取りの開始前に commit されたすべてのトランザクションの結果を確実に確認できます。このため強力な読み取りでは、アプリケーション コードが簡略化され、アプリケーションの信頼性も向上します。Spanner の整合性プロパティについては、TrueTime と外部整合性をご覧ください。

    必要に応じて、強い整合性を必要とする読み取り専用トランザクションの読み取りレイテンシを短縮するために、非リーダー、読み取り / 書き込み、または読み取り専用リージョンに読み取りリース リージョンのステータスを付与できます。これを行うには、Spanner 読み取りリースを使用します。読み取りリース リージョンは、デュアルリージョンまたはマルチリージョン インスタンスで強力な読み取りレイテンシを低減するために役立ちます。ただし、読み取りリースを使用すると、書き込みのレイテンシが長くなります。

  • レイテンシの影響で強力な読み取りが不可能な場合は、ステイル読み取りを使用してください(bounded-staleness または exact-staleness)。読み取り結果をできるだけ新しいものにする必要がない場合は、これによってパフォーマンスを向上させることができます。レプリケーションのページで説明されているように、良好なパフォーマンスを実現するために、ステイルネスの値として 15 秒を使用することが妥当です。

データベース ロールを使用してデータを読み取る

細かいアクセス制御のユーザーの場合は、SQL ステートメントとクエリを実行し、データベースで行オペレーションを実行するには、データベース ロールを選択する必要があります。選択したロールは、ロールを変更するまでセッション全体を通じて維持されます。

データベース ロールを使用して読み取りを実行する方法については、細かいアクセス制御を使用してデータベースにアクセスするをご覧ください。

単一読み取りメソッド

Spanner では、次の場合に、データベースに対する単一読み取りメソッド(トランザクションのコンテキスト外での読み取りなど)を使用できます。

  • SQL クエリ ステートメントとして読み取りを実行する。あるいは、Cloud Spanner の読み取り API を使用する。
  • 強力な読み取りを実行してテーブルから単一行または複数行を読み取る。
  • ステイル読み取りを実行してテーブルから単一行または複数行を読み取る。
  • セカンダリ インデックスから単一行または複数行を読み取る。

マルチリージョン インスタンス構成またはカスタム リージョン構成(オプションの読み取り専用リージョンを含む)内の特定のレプリカまたはリージョンに単一読み取りをルーティングする場合は、有向読み取りをご覧ください。

以降のセクションでは、Spanner クライアント ライブラリを使用して読み取りメソッドを使用する方法について説明します。

クエリを実行する

以下では、データベースに対して SQL クエリ ステートメントを実行する方法を説明します。

GoogleSQL

C++

ExecuteQuery() を使用して、データベースに対する 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#

ExecuteReaderAsync() を使用して、データベースに対するクエリを実行します。


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

Client.Single().Query を使用して、データベースに対するクエリを実行します。


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

ReadContext.executeQuery を使用して、データベースに対するクエリを実行します。

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

Database.run を使用して、データベースに対するクエリを実行します。

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

Database::execute を使用して、データベースに対するクエリを実行します。

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

Database.execute_sql を使用して、データベースに対するクエリを実行します。

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

Client#execute を使用して、データベースに対するクエリを実行します。

# 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

SQL ステートメントを作成するときは、SQL クエリ構文のリファレンスと関数と演算子のリファレンスをご覧ください。

強力な読み取りの実行

以下では、強力な読み取りを実行してデータベースから 0 以上の行を読み取る方法を説明します。

GoogleSQL

C++

データを読み取るためのコードは、SQL クエリを実行して Spanner をクエリする前述のサンプルと同じです。

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#

データを読み取るためのコードは、SQL クエリを実行して Spanner をクエリする前述のサンプルと同じです。


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

Client.Single().Read を使用して、データベースから行を読み取ります。


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