Empezar a usar Cloud Spanner en Python

Empezar a usar Cloud Spanner en Python

Objetivos

Con este tutorial, te explicaremos cómo llevar a cabo estos pasos con la biblioteca cliente de Cloud Spanner para Python:

  • Crear una instancia y una base de datos de Cloud Spanner.
  • Escribir, leer y ejecutar consultas SQL sobre datos de la base de datos.
  • Actualizar el esquema de la base de datos.
  • Actualizar datos mediante una transacción de lectura y escritura.
  • Añadir un índice secundario a la base de datos.
  • Usar el índice para leer los datos y ejecutar consultas SQL sobre ellos.
  • Recuperar datos mediante una transacción de solo lectura.

Precios

En este tutorial se usa Cloud Spanner, que es un componente facturable de Google Cloud Platform. Si quieres obtener información sobre el coste de usar Cloud Spanner, consulta el apartado de precios.

Antes de empezar

  1. Completa los pasos descritos en la sección de configuración, en los que se explica cómo crear y configurar un proyecto predeterminado de Google Cloud Platform en el que se han habilitado la facturación y la API Cloud Spanner y se ha configurado OAuth 2.0 con el fin de obtener credenciales de autenticación para usar la API Cloud Spanner.
    En concreto, asegúrate de que ejecutas gcloud auth application-default login para configurar el entorno de desarrollo local con credenciales de autenticación.

  2. Sigue las instrucciones del apartado sobre configuración de un entorno de desarrollo Python.

  3. Clona el repositorio de aplicaciones de muestra en la máquina local:

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

    También puedes descargar la muestra como un archivo en formato zip y extraerla.

  4. Cambia al directorio que contiene el código de ejemplo de Cloud Spanner:

    cd python-docs-samples/spanner/cloud-client
    
  5. Crea un entorno de Python aislado e instala dependencias:

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

Crear una instancia

Cuando usas Cloud Spanner por primera vez, debes crear una instancia, que es una asignación de recursos usada por las bases de datos de Cloud Spanner. Cuando creas una instancia, eliges una configuración de instancia, que determina dónde se almacenan los datos, así como la cantidad de nodos que se usarán, lo que a su vez determina la cantidad de recursos de almacenamiento y de servicio de la instancia.

Ejecuta el siguiente comando para crear una instancia de Cloud Spanner en la región us-central1 con un nodo:

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

Ten en cuenta que de este modo se crea una instancia con las siguientes características:

  • ID de instancia test-instance
  • Nombre visible Test Instance
  • Configuración de instancia regional-us-central1 (las configuraciones regionales almacenan datos en una región, mientras que las configuraciones de varias regiones distribuyen datos entre varias regiones; más información en el apartado sobre instancias)
  • Recuento de nodo de 1 (node_count corresponde a la cantidad de recursos de almacenamiento y servicio disponibles para las bases de datos de la instancia; más información en el apartado sobre recuento de nodos)

Deberías ver lo siguiente:

Creating instance...done.

Consultar los archivos de muestra

El repositorio de muestras contiene una muestra en la que se explica cómo usar Cloud Spanner con Python.

Echa un vistazo al archivo snippets.py, en el que se muestra cómo usar Cloud Spanner. El código muestra cómo crear y usar una nueva base de datos. Los datos usan el esquema de ejemplo que se muestra en la página sobre el esquema y el modelo de datos.

Crear una base de datos

Crea una base de datos llamada "example-db" en la instancia que se denomina "test-instance" al ejecutar este método en la línea de comandos.

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

Deberías ver lo siguiente:

Created database example-db on instance test-instance

Acabas de crear una base de datos de Cloud Spanner. Este es el código que ha creado la base de datos.

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

El código también define dos tablas, Singers y Albums, con las que se crea una aplicación de música básica. Estas tablas se usan en esta página. Echa un vistazo al esquema de ejemplo si aún no lo has hecho.

El siguiente paso consiste en escribir datos en la base de datos.

Crear un cliente de base de datos

Antes de que puedas leer o escribir, debes crear un Client. Puedes concebir Client como si fuera una conexión de base de datos: todas las interacciones con Cloud Spanner deben pasar por un Client. Por lo general, creas un Client cuando se inicia la aplicación; después, reutilizas ese Client para leer, escribir y ejecutar transacciones. El siguiente código muestra cómo crear un cliente.

# 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)

Obtén más información en la referencia de Client.

Escribir datos

Se escriben datos mediante un objeto Batch. Un objeto Batch es un contenedor para operaciones de mutación. Una mutación representa una secuencia de insertos, actualizaciones, eliminaciones y más que se pueden aplicar atómicamente a diferentes filas y tablas de una base de datos de Cloud Spanner.

El método insert() de la clase Batch se usa para añadir una o más mutaciones de inserción al lote. Todas las mutaciones de un solo lote se aplican de forma atómica.

Con este código se muestra cómo escribir los datos:

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.')

(Si deseas obtener más detalles sobre los datos, consulta el esquema de ejemplo sobre tablas Singers y Albums).

Ejecuta la muestra mediante el argumento insert_data.

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

Deberías ver lo siguiente:

Inserted data.

Consultar datos mediante SQL

Cloud Spanner es compatible con una interfaz SQL nativa de lectura de datos, a la que se puede acceder desde la línea de comandos mediante la herramienta de línea de comandos gcloud o mediante programación, a través de la biblioteca cliente de Cloud Spanner para Python.

En la línea de comandos

Ejecuta esta declaración SQL para leer los valores de todas las columnas de la tabla Albums:

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

Deberías ver el siguiente resultado:

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

Usar la biblioteca cliente de Cloud Spanner para Python

Además de ejecutar una declaración SQL en la línea de comandos, puedes emitir la misma declaración SQL mediante programación a través de la biblioteca cliente de Cloud Spanner para Python.

El método execute_sql() de la clase Database sirve para ejecutar la consulta SQL.

A continuación, se indica cómo emitir la consulta y acceder a los 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(u'SingerId: {}, AlbumId: {}, AlbumTitle: {}'.format(*row))

Ejecuta la muestra mediante el argumento query_data.

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

Deberías ver el siguiente resultado:

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

Leer datos mediante la API de lectura

Además de con la interfaz SQL de Cloud Spanner, Cloud Spanner también es compatible con una interfaz de lectura.

Usa el método read() de la clase Database para leer las filas de la base de datos. Usa un objeto KeySet con el fin de definir una colección de claves e intervalos de claves para leerlos.

A continuación, mostramos cómo leer los datos:

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

Ejecuta la muestra mediante el argumento read_data.

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

El resultado que verás debe parecerse al siguiente:

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

Actualizar el esquema de la base de datos

Supongamos que necesitas añadir una nueva columna llamada "MarketingBudget" a la tabla Albums. Si deseas añadir una nueva columna a una tabla, es preciso actualizar el esquema de base de datos. Cloud Spanner es compatible con actualizaciones de esquema de bases de datos mientras que dicha base de datos sigue atendiendo el tráfico. No hace falta que la base de datos esté desconectada y no se bloquean tablas ni columnas enteras durante la actualización de un esquema; puedes continuar escribiendo datos en la base de datos durante ese periodo. Obtén más información sobre las actualizaciones de esquema compatibles y el rendimiento de cambio de esquema en el apartado sobre actualización de esquemas.

Añadir una columna

Puedes añadir una columna en la línea de comandos mediante la herramienta de línea de comandos gcloud o a través de la biblioteca del cliente de Cloud Spanner para Python.

En la línea de comandos

El siguiente comando ALTER TABLE sirve para añadir la nueva columna a la tabla:

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

Deberías ver lo siguiente:

DDL updating...done.

Usar la biblioteca cliente de Cloud Spanner para Python

El método update_ddl() de la clase Database sirve para modificar el siguiente esquema:

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.')

Ejecuta la muestra mediante el argumento add_column.

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

Deberías ver lo siguiente:

Added the MarketingBudget column.

Escribir datos en la nueva columna

El siguiente código sirve para escribir datos en la nueva columna. Establece MarketingBudget en 100000 en la fila marcada como Albums(1, 1), y 500000 en la fila marcada como Albums(2, 2).

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.')

Ejecuta la muestra mediante el argumento update_data.

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

También puedes ejecutar una consulta SQL o una llamada de lectura para recuperar los valores que acabas de escribir.

Aquí está el código para ejecutar la consulta:

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

Si quieres ejecutar esta consulta, hazlo mediante la muestra con el argumento query_data_with_new_column.

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

Deberías ver lo siguiente:

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

Actualizar datos mediante una transacción de lectura y escritura

Supongamos que las ventas de Albums(1, 1) son más bajas de lo esperado. Por este motivo, deseas mover 200.000 USD del presupuesto de comercialización de Albums(2, 2) a Albums(1, 1), pero solo si el presupuesto de Albums(2, 2) es de, al menos, 300.000 USD.

Debido a que esta transacción puede escribir datos que difieren según los valores que se leen, tienes que usar una transacción de lectura y escritura para realizar las lecturas y escrituras de forma automática.

El método run_in_transaction() de la clase Database sirve para ejecutar una transacción.

Este es el código para ejecutar la transacción:

def read_write_transaction(instance_id, database_id):
    """Performs a read-write transaction to update two sample records in the
    database.

    This will transfer 200,000 from the `MarketingBudget` field for the second
    Album to the first Album. If the `MarketingBudget` is too low, it will
    raise an exception.

    Before running this sample, you will need to run the `update_data` sample
    to populate the fields.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    def update_albums(transaction):
        # Read the second album budget.
        second_album_keyset = spanner.KeySet(keys=[(2, 2)])
        second_album_result = transaction.read(
            table='Albums', columns=('MarketingBudget',),
            keyset=second_album_keyset, limit=1)
        second_album_row = list(second_album_result)[0]
        second_album_budget = second_album_row[0]

        transfer_amount = 200000

        if second_album_budget < 300000:
            # Raising an exception will automatically roll back the
            # transaction.
            raise ValueError(
                'The second album doesn\'t have enough funds to transfer')

        # Read the first album's budget.
        first_album_keyset = spanner.KeySet(keys=[(1, 1)])
        first_album_result = transaction.read(
            table='Albums', columns=('MarketingBudget',),
            keyset=first_album_keyset, limit=1)
        first_album_row = list(first_album_result)[0]
        first_album_budget = first_album_row[0]

        # Update the budgets.
        second_album_budget -= transfer_amount
        first_album_budget += transfer_amount
        print(
            'Setting first album\'s budget to {} and the second album\'s '
            'budget to {}.'.format(
                first_album_budget, second_album_budget))

        # Update the rows.
        transaction.update(
            table='Albums',
            columns=(
                'SingerId', 'AlbumId', 'MarketingBudget'),
            values=[
                (1, 1, first_album_budget),
                (2, 2, second_album_budget)])

    database.run_in_transaction(update_albums)

    print('Transaction complete.')

Ejecuta la muestra mediante el argumento read_write_transaction.

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

Deberías ver lo siguiente:

Transaction complete.

Consulta los datos de nuevo:

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

Deberías ver lo siguiente:

SingerId: 2, AlbumId: 2, MarketingBudget: 300000
SingerId: 1, AlbumId: 2, MarketingBudget: None
SingerId: 2, AlbumId: 1, MarketingBudget: None
SingerId: 2, AlbumId: 3, MarketingBudget: None
SingerId: 1, AlbumId: 1, MarketingBudget: 300000

Usar un índice secundario

Supongamos que quieres buscar todas las filas de Albums que tengan valores de AlbumTitle en un intervalo determinado. Puedes leer todos los valores de la columna AlbumTitle mediante una declaración SQL o una lectura de llamada y descartar después las filas que no cumplan los criterios, pero realizar esta búsqueda completa en la tabla resulta caro, especialmente cuando se trata de tablas con muchas filas. En su lugar, puedes acelerar la recuperación de filas al buscar columnas de clave no principales mediante la creación de un índice secundario en la tabla.

Si se desea añadir un índice secundario a una tabla, es preciso actualizar el esquema. Cloud Spanner permite añadir un índice mientras la base de datos continúa atendiendo el tráfico, al igual que sucede con otras actualizaciones de esquema. Cloud Spanner rellena el índice con datos (un proceso que también se conoce como "reposición"). Las reposiciones pueden tardar unos minutos en completarse, pero no es necesario que desconectes la base de datos o evites escribir en algunas tablas o columnas durante este proceso. Si quieres obtener más detalles, consulta el apartado sobre reposiciones de índice.

Añadir un índice secundario

Puedes añadir un índice en la línea de comandos con la herramienta de línea de comandos de gcloud o mediante programación, a través de la biblioteca del cliente de Cloud Spanner para Python.

En la línea de comandos

Usa este comando CREATE INDEX para añadir un índice a la base de datos:

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

Deberías ver lo siguiente:

DDL updating...done.

Usar la biblioteca cliente de Cloud Spanner para Python

El método update_ddl() de la clase Database sirve para añadir un índice:

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.')

Ejecuta la muestra mediante el argumento add_index.

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

Añadir un índice es un proceso que puede llevar unos minutos. Después de añadir el índice, esto es lo que deberías ver:

Added the AlbumsByAlbumTitle index.

Realizar consultas mediante el índice

Puedes realizar consultas mediante el nuevo índice en la línea de comandos o mediante la biblioteca cliente.

En la línea de comandos

Ejecuta una declaración SQL con la herramienta de línea de comandos gcloud para buscar AlbumId, AlbumTitle y MarketingBudget en Albums mediante el índice AlbumsByAlbumTitle con el intervalo de AlbumsTitle en ["Aardvark", "Goo").

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"'

Deberías ver el siguiente resultado:

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

Usar la biblioteca cliente de Cloud Spanner para Python

El código para usar el índice mediante programación es similar al código de consulta utilizado anteriormente.

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

Ejecuta la muestra mediante el argumento query_data_with_index.

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

El resultado que verás debe parecerse al siguiente:

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

Si quieres obtener más detalles, consulta estas referencias:

Leer datos mediante el índice

Si quieres leer mediante el índice, proporciona un argumento Index al método read() de la clase Database.

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

Ejecuta la muestra mediante el argumento read_data_with_index.

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

Deberías ver lo siguiente:

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

Añadir un índice con una cláusula STORING

Puede que hayas notado que el ejemplo de lectura anterior no incluye la lectura de la columna MarketingBudget. Esto se debe a que la interfaz de lectura de Cloud Spanner no admite la capacidad de unirse a un índice con una tabla de datos para buscar valores que no están almacenados en él.

Crea una definición alternativa de AlbumsByAlbumTitle que almacene una copia de MarketingBudget en el índice.

En la línea de comandos

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

Añadir un índice es un proceso que puede llevar unos minutos. Después de añadir el índice, esto es lo que deberías ver:

DDL updating...done.

Usar la biblioteca cliente de Cloud Spanner para Python

El método update_ddl() de la clase Database sirve para añadir un índice con una cláusula STORING:

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.')

Ejecuta la muestra mediante el argumento add_storing_index.

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

Deberías ver lo siguiente:

Added the AlbumsByAlbumTitle2 index.

Ahora puedes ejecutar una lectura que recopile las columnas AlbumId, AlbumTitle, y MarketingBudget del índice AlbumsByAlbumTitle2:

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

Ejecuta la muestra mediante el argumento read_data_with_storing_index.

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

El resultado que verás debe parecerse al siguiente:

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

Recuperar datos mediante transacciones de solo lectura

Supongamos que deseas ejecutar más de una lectura en la misma marca de tiempo. En las transacciones de solo lectura se observa un prefijo uniforme del historial de confirmación de transacción, por lo que la aplicación siempre obtiene datos coherentes. El objeto Snapshot sirve para ejecutar transacciones de solo lectura. Por otra parte, el método snapshot() de la clase Database sirve para obtener un objeto Snapshot.

A continuación, se muestra cómo ejecutar una consulta y realizar una lectura en la misma transacción de solo lectura:

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

Ejecuta la muestra mediante el argumento read_only_transaction.

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

El resultado que verás debe parecerse al siguiente:

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

Limpieza

Si quieres evitar que se generen más costes en la cuenta de Google Cloud Platform debido a los recursos usados en este tutorial, elimina la base de datos y la instancia que creaste.

Eliminar la base de datos

Al eliminar una instancia, se eliminan automáticamente todas sus bases de datos. En este paso se muestra cómo eliminar una base de datos sin eliminar una instancia (se seguirían generando costes por la instancia).

En la línea de comandos

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

Mediante la consola de GCP

  1. Visita la página sobre las instancias de Cloud Spanner en la consola de Google Cloud Platform.
    Ir a la página de instancias de Cloud Spanner
  2. Haz clic en la instancia.
  3. Haz clic en la base de datos que quieras eliminar.
  4. En la página sobre detalles de la base de datos, haz clic en Eliminar.
  5. Confirma que deseas eliminar la base de datos y haz clic en Eliminar.

Eliminar la instancia

Al eliminar una instancia, se borran todas las bases de datos creadas en dicha instancia.

En la línea de comandos

gcloud spanner instances delete test-instance

Mediante la consola de GCP

  1. Visita la página sobre las instancias de Cloud Spanner en la consola de Google Cloud Platform.
    Ir a la página de instancias de Cloud Spanner
  2. Haz clic en la instancia.
  3. Haz clic en Eliminar.
  4. Confirma que deseas eliminar la instancia y haz clic en Eliminar.

Siguientes pasos

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…

Cloud Spanner Documentation