Python で Cloud Spanner を使ってみる

目標

このチュートリアルでは、Python 用の Cloud Spanner クライアント ライブラリを使用する以下の手順について説明します。

  • Cloud Spanner のインスタンスとデータベースを作成します。
  • データベースのデータに対し、書き込み、読み取り、SQL クエリの実行を行います。
  • データベース スキーマを更新します。
  • 読み取り / 書き込みトランザクションを使用してデータを更新します。
  • セカンダリ インデックスをデータベースに追加します。
  • インデックスを使用して、データの読み込みと SQL クエリの実行を行います。
  • 読み取り専用トランザクションを使用してデータを取得します。

料金

このチュートリアルで使用する Cloud Spanner は、Google Cloud Platform の有料コンポーネントです。Cloud Spanner を使用する料金については、料金をご覧ください。

始める前に

  1. 設定に示されている手順を完了します。その手順では、Google Cloud Platform のデフォルト プロジェクトの作成と設定、課金の有効化、Cloud Spanner API の有効化、Cloud Spanner API を使用するのに必要な認証情報を取得するために OAuth 2.0 を設定する方法について説明しています。
    特に、ローカル開発環境で認証情報を設定するために、必ず gcloud auth application-default login を実行してください。

  2. Python 開発環境のセットアップの手順に従います。

  3. ローカルマシンにサンプルアプリのリポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    

    あるいは、zip 形式のサンプルをダウンロードして、ファイルを抽出することもできます。

  4. Cloud Spanner のサンプルコードが含まれるディレクトリに移動します。

    cd python-docs-samples/spanner/cloud-client
    
  5. 切り離された Python 環境を作成し、依存関係をインストールします。

    virtualenv env
    source env/bin/activate
    pip install -r requirements.txt
    

インスタンスの作成

Cloud Spanner を最初に使用するときは、インスタンスを作成する必要があります。インスタンスとは、Cloud Spanner データベースによって使用されるリソースの割り当てのことです。インスタンスを作成するときは、インスタンス構成を選択してデータの格納場所を指定し、さらに使用するノード数も選択して、インスタンスの配信リソースおよびストレージ リソースの量を決定します。

次のコマンドを実行して、1 ノードの us-central1 リージョンに Cloud Spanner インスタンスを作成します。

gcloud spanner instances create test-instance --config=regional-us-central1 \
  --description="Test Instance" --nodes=1

これにより、次の特性を持つインスタンスが作成されます。

  • インスタンス ID test-instance
  • 表示名 Test Instance
  • インスタンス構成 regional-us-central1(リージョン構成ではデータが単一のリージョンに保存され、マルチリージョン構成ではデータが複数のリージョンに分散されます。詳しくは、インスタンスをご覧ください。)
  • ノード数 1(node_count はインスタンスのデータベースで使用可能な配信リソースとストレージ リソースの量に対応します。詳しくは、ノード数をご覧ください。)

以下のように表示されます。

Creating instance...done.

サンプル ファイルの確認

サンプル リポジトリには、Python での Cloud Spanner の使用方法がわかるサンプルがあります。

Cloud Spanner の使用方法については、snippets.py ファイルをご覧ください。このファイルのコードでは、新しいデータベースを作成して使用する方法が示されています。データで使用しているサンプル スキーマは、スキーマとデータモデルのページにあります。

データベースの作成

コマンドラインで次のコマンドを実行して、test-instance というインスタンスに example-db というデータベースを作成します。

python snippets.py test-instance --database-id example-db create_database

以下のように表示されます。

Created database example-db on instance test-instance

これで Cloud Spanner データベースが作成されました。データベースを作成したコードは次のとおりです。

def create_database(instance_id, database_id):
    """Creates a database and tables for sample data."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)

    database = instance.database(database_id, ddl_statements=[
        """CREATE TABLE Singers (
            SingerId     INT64 NOT NULL,
            FirstName    STRING(1024),
            LastName     STRING(1024),
            SingerInfo   BYTES(MAX)
        ) PRIMARY KEY (SingerId)""",
        """CREATE TABLE Albums (
            SingerId     INT64 NOT NULL,
            AlbumId      INT64 NOT NULL,
            AlbumTitle   STRING(MAX)
        ) PRIMARY KEY (SingerId, AlbumId),
        INTERLEAVE IN PARENT Singers ON DELETE CASCADE"""
    ])

    operation = database.create()

    print('Waiting for operation to complete...')
    operation.result()

    print('Created database {} on instance {}'.format(
        database_id, instance_id))

コードでは、基本的な音楽アプリケーション用の 2 つのテーブル SingersAlbums も定義されています。これらのテーブルはこのページ全体で使用されています。まだスキーマ例を見ていない場合は確認してください。

次のステップでは、データベースにデータを書き込みます。

データベース クライアントの作成

読み取りや書き込みを行うには、その前に Client を作成する必要があります。Client はデータベース接続と考えることができます。Cloud Spanner とのすべてのやり取りは Client 経由で実行される必要があります。通常はアプリケーション起動時に Client を作成し、読み込み、書き込み、トランザクションの実行に Client を再利用します。次のコードではクライアントの作成方法を示します。

# Imports the Google Cloud Client Library.
from google.cloud import spanner

# Instantiate a client.
spanner_client = spanner.Client()

# Your Cloud Spanner instance ID.
instance_id = 'my-instance-id'

# Get a Cloud Spanner instance by ID.
instance = spanner_client.instance(instance_id)

# Your Cloud Spanner database ID.
database_id = 'my-database-id'

# Get a Cloud Spanner database by ID.
database = instance.database(database_id)

# Execute a simple SQL statement.
with database.snapshot() as snapshot:
    results = snapshot.execute_sql('SELECT 1')

    for row in results:
        print(row)

詳細については、Client のリファレンスをご覧ください。

DML でのデータの書き込み

読み取り / 書き込みトランザクションでデータ操作言語(DML)を使用してデータを挿入できます。

execute_update() メソッドを使用して DML ステートメントを実行します。

# 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 insert_singers(transaction):
    row_ct = transaction.execute_update(
        "INSERT Singers (SingerId, FirstName, LastName) VALUES "
        "(12, 'Melissa', 'Garcia'), "
        "(13, 'Russell', 'Morales'), "
        "(14, 'Jacqueline', 'Long'), "
        "(15, 'Dylan', 'Shaw')"
    )
    print("{} record(s) inserted.".format(row_ct))

database.run_in_transaction(insert_singers)

insert_with_dml 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db insert_with_dml

以下のように表示されます。

4 record(s) inserted.

ミューテーションでのデータの書き込み

ミューテーションを使ってデータを挿入することもできます。

データの書き込みは Batch オブジェクトを使用して行います。Batch オブジェクトは、ミューテーション オペレーションのコンテナです。ミューテーションは挿入、更新、削除など、Cloud Spanner データベース中のさまざまな行やテーブルに、Cloud Spanner によってアトミックに適用される一連の操作を表します。

Batch クラスの insert() メソッドで、バッチに 1 つ以上の挿入ミューテーションを追加します。1 つのバッチ内のすべてのミューテーションはアトミックに適用されます。

次のコードは、ミューテーションを使用してデータを書き込む方法を示しています。

def insert_data(instance_id, database_id):
    """Inserts sample data into the given database.

    The database and table must already exist and can be created using
    `create_database`.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.batch() as batch:
        batch.insert(
            table='Singers',
            columns=('SingerId', 'FirstName', 'LastName',),
            values=[
                (1, u'Marc', u'Richards'),
                (2, u'Catalina', u'Smith'),
                (3, u'Alice', u'Trentor'),
                (4, u'Lea', u'Martin'),
                (5, u'David', u'Lomond')])

        batch.insert(
            table='Albums',
            columns=('SingerId', 'AlbumId', 'AlbumTitle',),
            values=[
                (1, 1, u'Total Junk'),
                (1, 2, u'Go, Go, Go'),
                (2, 1, u'Green'),
                (2, 2, u'Forever Hold Your Peace'),
                (2, 3, u'Terrified')])

    print('Inserted data.')

insert_data 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db insert_data

以下のように表示されます。

Inserted data.

SQL を使用したデータのクエリ

Cloud Spanner はデータの読み取りに対してネイティブ SQL インターフェースをサポートします。このインターフェースには、コマンドラインで gcloud コマンドライン ツールを使用して、またはプログラムで Python 用 Cloud Spanner クライアント ライブラリを使用して、アクセスできます。

コマンドラインから

Albums テーブルのすべての列から値を読み取るには、次の SQL ステートメントを実行します。

gcloud spanner databases execute-sql example-db --instance=test-instance --sql='SELECT SingerId, AlbumId, AlbumTitle FROM Albums'

結果は次のようになります。

SingerId AlbumId AlbumTitle
1        1       Total Junk
1        2       Go, Go, Go
2        1       Green
2        2       Forever Hold Your Peace
2        3       Terrified

Python 用の Cloud Spanner クライアント ライブラリの使用

コマンドラインで SQL ステートメントを実行するだけでなく、Python 用の Cloud Spanner クライアント ライブラリを使用して、同じ SQL ステートメントをプログラムで発行することもできます。

Snapshot オブジェクトの execute_sql() メソッドを使用して SQL クエリを実行します。Snapshot オブジェクトを取得するには、with ステートメントで Database クラスの snapshot() メソッドを呼び出します。

クエリを発行してデータにアクセスする方法を次に示します。

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(u'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row))

query_data 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db query_data

次のような結果が表示されます。

SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold Your Peace
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified
SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk

読み取り API を使用したデータの読み込み

Cloud Spanner の SQL インターフェースに加えて、Cloud Spanner は読み取りインターフェースもサポートしています。

データベースから行を読み取るには、Snapshot オブジェクトの read() メソッドを使用します。Snapshot オブジェクトを取得するには、with ステートメントで Database クラスの snapshot() メソッドを呼び出します。読み取るキーとキー範囲のコレクションを定義するには、KeySet オブジェクトを使用します。

データを読み取る方法を次に示します。

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(u'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row))

read_data 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db read_data

次のような出力が表示されます。

SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold Your Peace
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified

データベース スキーマの更新

MarketingBudget という列を新たに Albums テーブルに追加する必要があるとします。既存のテーブルに新しい列を追加するには、データベース スキーマの更新が必要です。Cloud Spanner は、トラフィック提供中のデータベースへのスキーマの更新をサポートしています。スキーマの更新では、データベースをオフラインにする必要がなく、テーブル全体や列がロックされないため、スキーマの更新中もデータベースへのデータの書き込みを続けることができます。サポートされるスキーマの更新とスキーマ変更のパフォーマンスの詳細については、スキーマの更新をご覧ください。

列の追加

列の追加は、コマンドラインで gcloud コマンドライン ツールを使用して、またはプログラムで Python 用の Cloud Spanner クライアント ライブラリを使用して、行うことができます。

コマンドラインから

テーブルに新しい列を追加するには、次の ALTER TABLE コマンドを使用します。

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'

以下のように表示されます。

DDL updating...done.

Python 用の Cloud Spanner クライアント ライブラリの使用

Database クラスの update_ddl() メソッドを使用してスキーマを変更します。

def add_column(instance_id, database_id):
    """Adds a new column to the Albums table in the example database."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    operation = database.update_ddl([
        'ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'])

    print('Waiting for operation to complete...')
    operation.result()

    print('Added the MarketingBudget column.')

add_column 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db add_column

以下のように表示されます。

Added the MarketingBudget column.

新しい列へのデータの書き込み

次のコードは、新しい列にデータを書き込みます。MarketingBudget の値を、キーが Albums(1, 1) の行では 100000 に、キーが Albums(2, 2) の行では 500000 に設定します。

def update_data(instance_id, database_id):
    """Updates sample data in the database.

    This updates the `MarketingBudget` column which must be created before
    running this sample. You can add the column by running the `add_column`
    sample or by running this DDL statement against your database:

        ALTER TABLE Albums ADD COLUMN MarketingBudget INT64

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.batch() as batch:
        batch.update(
            table='Albums',
            columns=(
                'SingerId', 'AlbumId', 'MarketingBudget'),
            values=[
                (1, 1, 100000),
                (2, 2, 500000)])

    print('Updated data.')

update_data 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db update_data

SQL クエリまたは読み取り呼び出しを実行して、書き込んだばかりの値を取得することもできます。

クエリを実行するコードを次に示します。

def query_data_with_new_column(instance_id, database_id):
    """Queries sample data from the database using SQL.

    This sample uses the `MarketingBudget` column. You can add the column
    by running the `add_column` sample or by running this DDL statement against
    your database:

        ALTER TABLE Albums ADD COLUMN MarketingBudget INT64
    """
    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, MarketingBudget FROM Albums')

        for row in results:
            print(
                u'SingerId: {}, AlbumId: {}, MarketingBudget: {}'.format(*row))

このクエリを実行するには、query_data_with_new_column 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db query_data_with_new_column

以下のように表示されます。

SingerId: 2, AlbumId: 2, MarketingBudget: 500000
SingerId: 1, AlbumId: 2, MarketingBudget: None
SingerId: 2, AlbumId: 1, MarketingBudget: None
SingerId: 2, AlbumId: 3, MarketingBudget: None
SingerId: 1, AlbumId: 1, MarketingBudget: 100000

データの更新

読み取り / 書き込みトランザクションで DML を使用してデータを更新できます。

execute_update() メソッドを使用して DML ステートメントを実行します。

# 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 transfer_budget(transaction):
    # Transfer marketing budget from one album to another. Performed in a
    # single transaction to ensure that the transfer is atomic.
    first_album_result = transaction.execute_sql(
        "SELECT MarketingBudget from Albums "
        "WHERE SingerId = 1 and AlbumId = 1"
    )
    first_album_row = list(first_album_result)[0]
    first_album_budget = first_album_row[0]

    transfer_amount = 300000

    # Transaction will only be committed if this condition still holds at
    # the time of commit. Otherwise it will be aborted and the callable
    # will be rerun by the client library
    if first_album_budget >= transfer_amount:
        second_album_result = transaction.execute_sql(
            "SELECT MarketingBudget from Albums "
            "WHERE SingerId = 1 and AlbumId = 1"
        )
        second_album_row = list(second_album_result)[0]
        second_album_budget = second_album_row[0]

        first_album_budget -= transfer_amount
        second_album_budget += transfer_amount

        # Update first album
        transaction.execute_update(
            "UPDATE Albums "
            "SET MarketingBudget = @AlbumBudget "
            "WHERE SingerId = 1 and AlbumId = 1",
            params={"AlbumBudget": first_album_budget},
            param_types={"AlbumBudget": spanner.param_types.INT64}
        )

        # Update second album
        transaction.execute_update(
            "UPDATE Albums "
            "SET MarketingBudget = @AlbumBudget "
            "WHERE SingerId = 2 and AlbumId = 2",
            params={"AlbumBudget": second_album_budget},
            param_types={"AlbumBudget": spanner.param_types.INT64}
        )

        print("Transferred {} from Album1's budget to Album2's".format(
            transfer_amount))

database.run_in_transaction(transfer_budget)

write_with_dml_transaction 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db write_with_dml_transaction

以下のように表示されます。

Transferred 300000 from Album1's budget to Album2's

セカンダリ インデックスの使用

Albums から AlbumTitle の値が特定の範囲内である行すべてをフェッチしたいと仮定します。SQL ステートメントまたは読み取りインターフェースを使用して AlbumTitle 列からすべての値を読み取り、基準を満たしていない行を破棄することもできますが、この方法では処理量が大きくなります(特に、行数が多いテーブルの場合)。代わりに、テーブルにセカンダリ インデックスを作成することにより、主キー以外の列を検索するときの行の取得速度を上げることができます。

既存のテーブルにセカンダリ インデックスを追加するには、スキーマの更新が必要です。他のスキーマの更新と同様に、Cloud Spanner ではデータベースがトラフィックを提供している間にインデックスを追加できます。Cloud Spanner は、内部でインデックスにデータを書き込みます(「バックフィル」)。バックフィルには数分かかることがありますが、このプロセスの間に、データベースをオフラインにしたり、特定のテーブルや列への書き込みを避けたりする必要はありません。詳細については、インデックスのバックフィリングをご覧ください。

セカンダリ インデックスの追加

インデックスの追加は、コマンドラインで gcloud コマンドライン ツールを使用して、またはプログラムで Python 用の Cloud Spanner クライアント ライブラリを使用して、行うことができます。

コマンドラインから

データベースにインデックスを追加するには、次の CREATE INDEX コマンドを使用します。

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)'

以下のように表示されます。

DDL updating...done.

Python 用の Cloud Spanner クライアント ライブラリの使用

Database クラスの update_ddl() メソッドを使用してインデックスを追加します。

def add_index(instance_id, database_id):
    """Adds a simple index to the example database."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    operation = database.update_ddl([
        'CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)'])

    print('Waiting for operation to complete...')
    operation.result()

    print('Added the AlbumsByAlbumTitle index.')

add_index 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db add_index

インデックスの追加には数分かかる場合があります。インデックスが追加されると、次のように表示されます。

Added the AlbumsByAlbumTitle index.

インデックスを使用したクエリ

新しいインデックスを使用してクエリを実行するには、コマンドラインまたはクライアント ライブラリを使用します。

コマンドラインから

gcloud コマンドライン ツールで SQL ステートメントを実行し、["Aardvark", "Goo")AlbumsTitle の範囲に対して AlbumsByAlbumTitle インデックスを使用して Albums から AlbumIdAlbumTitleMarketingBudget を取得します。

gcloud spanner databases execute-sql example-db --instance=test-instance --sql='SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo"'

結果は次のようになります。

AlbumId  AlbumTitle               MarketingBudget
2        Go, Go, Go
2        Forever Hold Your Peace  300000

Python 用の Cloud Spanner クライアント ライブラリの使用

プログラマティックにインデックスを使用するコードは、前に使用したクエリのコードと似ています。

def query_data_with_index(
        instance_id, database_id, start_title='Aardvark', end_title='Goo'):
    """Queries sample data from the database using SQL and an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    This sample also uses the `MarketingBudget` column. You can add the column
    by running the `add_column` sample or by running this DDL statement against
    your database:

        ALTER TABLE Albums ADD COLUMN MarketingBudget INT64

    """
    from google.cloud.spanner_v1.proto import type_pb2

    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    params = {
        'start_title': start_title,
        'end_title': end_title
    }
    param_types = {
        'start_title': type_pb2.Type(code=type_pb2.STRING),
        'end_title': type_pb2.Type(code=type_pb2.STRING)
    }

    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(
            "SELECT AlbumId, AlbumTitle, MarketingBudget "
            "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
            "WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title",
            params=params, param_types=param_types)

        for row in results:
            print(
                u'AlbumId: {}, AlbumTitle: {}, '
                'MarketingBudget: {}'.format(*row))

query_data_with_index 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db query_data_with_index

次のような出力が表示されます。

AlbumId: 2, AlbumTitle: Go, Go, Go, MarketingBudget: None
AlbumId: 2, AlbumTitle: Forever Hold Your Peace, MarketingBudget: 300000

詳細については、以下のリファレンスをご覧ください。

インデックスを使用した読み取り

インデックスを使用して読み取りをするには、Snapshot オブジェクトの read() メソッドに Index 引数を指定します。Snapshot オブジェクトを取得するには、with ステートメントで Database クラスの snapshot() メソッドを呼び出します。

def read_data_with_index(instance_id, database_id):
    """Reads sample data from the database using an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    """
    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=('AlbumId', 'AlbumTitle'),
            keyset=keyset,
            index='AlbumsByAlbumTitle')

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

read_data_with_index 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db read_data_with_index

以下のように表示されます。

AlbumId: 2, AlbumTitle: Forever Hold Your Peace
AlbumId: 2, AlbumTitle: Go, Go, Go
AlbumId: 1, AlbumTitle: Green
AlbumId: 3, AlbumTitle: Terrified
AlbumId: 1, AlbumTitle: Total Junk

STORING 句を使用したインデックスの追加

上記の読み取り例に MarketingBudget の列の読み取りが含まれていなかったことに気付かれたかもしれません。これは、Cloud Spanner の読み取りインターフェースが、インデックスとデータテーブルを結合してインデックスに格納されていない値を検索する機能をサポートしていないためです。

MarketingBudget のコピーをインデックスに格納する AlbumsByAlbumTitle の代替定義を作成します。

コマンドラインから

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)'

インデックスの追加には数分かかる場合があります。インデックスが追加されると、次のように表示されます。

DDL updating...done.

Python 用の Cloud Spanner クライアント ライブラリの使用

STORING 句でインデックスを追加するには、Database クラスの update_ddl() メソッドを使用します。

def add_storing_index(instance_id, database_id):
    """Adds an storing index to the example database."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    operation = database.update_ddl([
        'CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)'
        'STORING (MarketingBudget)'])

    print('Waiting for operation to complete...')
    operation.result()

    print('Added the AlbumsByAlbumTitle2 index.')

add_storing_index 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db add_storing_index

以下のように表示されます。

Added the AlbumsByAlbumTitle2 index.

これで、インデックス AlbumsByAlbumTitle2 から AlbumIdAlbumTitleMarketingBudget 列をすべて取得する読み取りを実行できるようになります。

def read_data_with_storing_index(instance_id, database_id):
    """Reads sample data from the database using an index with a storing
    clause.

    The index must exist before running this sample. You can add the index
    by running the `add_soring_index` sample or by running this DDL statement
    against your database:

        CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
        STORING (MarketingBudget)

    """
    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=('AlbumId', 'AlbumTitle', 'MarketingBudget'),
            keyset=keyset,
            index='AlbumsByAlbumTitle2')

        for row in results:
            print(
                u'AlbumId: {}, AlbumTitle: {}, '
                'MarketingBudget: {}'.format(*row))

read_data_with_storing_index 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db read_data_with_storing_index

次のような出力が表示されます。

AlbumId: 2, AlbumTitle: Forever Hold Your Peace, MarketingBudget: 300000
AlbumId: 2, AlbumTitle: Go, Go, Go, MarketingBudget: None
AlbumId: 1, AlbumTitle: Green, MarketingBudget: None
AlbumId: 3, AlbumTitle: Terrified, MarketingBudget: None
AlbumId: 1, AlbumTitle: Total Junk, MarketingBudget: 300000

読み取り専用トランザクションを使用したデータの取得

同じタイムスタンプで複数の読み取りを実行する場合について考えます。読み取り専用トランザクションはトランザクションの commit 履歴の整合性のあるプレフィックスを監視しているので、アプリケーションは常に整合性のあるデータを取得できます。読み取り専用トランザクションを実行するには、Snapshot オブジェクトを使用します。Snapshot オブジェクトを取得するには、with ステートメントで Database クラスの snapshot() メソッドを呼び出します。

同じ読み取り専用トランザクションでクエリと読み取りを実行する方法を次に示します。

def read_only_transaction(instance_id, database_id):
    """Reads data inside of a read-only transaction.

    Within the read-only transaction, or "snapshot", the application sees
    consistent view of the database at a particular timestamp.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot(multi_use=True) as snapshot:
        # Read using SQL.
        results = snapshot.execute_sql(
            'SELECT SingerId, AlbumId, AlbumTitle FROM Albums')

        print('Results from first read:')
        for row in results:
            print(u'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row))

        # Perform another read using the `read` method. Even if the data
        # is updated in-between the reads, the snapshot ensures that both
        # return the same data.
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table='Albums',
            columns=('SingerId', 'AlbumId', 'AlbumTitle',),
            keyset=keyset,)

        print('Results from second read:')
        for row in results:
            print(u'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row))

read_only_transaction 引数を使用してサンプルを実行します。

python snippets.py test-instance --database-id example-db read_only_transaction

次のような出力が表示されます。

Results from first read:
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold Your Peace
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified
SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
Results from second read:
SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold Your Peace
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified

クリーンアップ

このチュートリアルで使用したリソースの Google Cloud Platform アカウントが課金されないようにするため、作成したデータベースとインスタンスを削除します。

データベースの削除

インスタンスを削除すると、それに含まれるすべてのデータベースが自動的に削除されます。このステップでは、インスタンスを削除しないでデータベースを削除する方法を示します(インスタンスの料金は引き続き発生します)。

コマンドラインから

gcloud spanner databases delete example-db --instance=test-instance

GCP Console の使用

  1. Google Cloud Platform Console の [Cloud Spanner インスタンス] ページに移動します。
    [Cloud Spanner インスタンス] ページに移動
  2. インスタンスをクリックします。
  3. 削除するデータベースをクリックします。
  4. [データベースの詳細] ページで [削除] をクリックします。
  5. データベースを削除することを確認し、[削除] をクリックします。

インスタンスの削除

インスタンスを削除すると、そのインスタンスで作成されたすべてのデータベースが自動的に削除されます。

コマンドラインから

gcloud spanner instances delete test-instance

GCP Console の使用

  1. Google Cloud Platform Console の [Cloud Spanner インスタンス] ページに移動します。
    [Cloud Spanner インスタンス] ページに移動
  2. インスタンスをクリックします。
  3. [削除] をクリックします。
  4. インスタンスを削除することを確認し、[削除] をクリックします。

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Spanner のドキュメント