Primeiros passos com o Cloud Spanner em C++

Objetivos

Este tutorial apresenta as seguintes etapas usando a biblioteca de cliente do Cloud Spanner para C++:

  • Criar uma instância e um banco de dados do Cloud Spanner
  • Gravar, ler e executar consultas SQL em dados contidos no banco de dados.
  • Atualizar o esquema do banco de dados.
  • Atualizar dados usando uma transação de leitura e gravação.
  • Adicionar um índice secundário ao banco de dados.
  • Usar o índice para ler e executar consultas SQL nos dados.
  • Recuperar dados usando uma transação somente leitura.

Custos

Neste tutorial, usamos o Cloud Spanner, que é um componente faturável do Google Cloud. Para informações sobre o custo de utilização do Cloud Spanner, consulte Preços.

Antes de começar

Conclua as etapas descritas em Configurar, que abrangem a criação e a configuração de um projeto padrão do Google Cloud, o faturamento, a API Cloud Spanner e a configuração do OAuth 2.0 para receber credenciais de autenticação para usar a API Cloud Spanner.

Especificamente, execute gcloud auth application-default login para configurar o ambiente de desenvolvimento local com credenciais de autenticação.

Preparar o ambiente C++ local

  1. Clone o repositório do app de amostra na máquina local:

    git clone https://github.com/googleapis/google-cloud-cpp $HOME/google-cloud-cpp
    
  2. Instale o Bazel para Linux usando estas instruções.

  3. Mude para o diretório que contém o código de amostra do Cloud Spanner:

    cd $HOME/google-cloud-cpp
    
  4. Crie as amostras com este comando:

    bazel build //google/cloud/spanner/samples:samples
    
  5. Configure a autenticação e a autorização para o projeto google-cloud-cpp.

    gcloud auth application-default login
    
  6. Crie uma variável de ambiente chamada GCLOUD_PROJECT. Substitua [MY_PROJECT_ID] pelo ID do projeto do Google Cloud. Você pode encontrar esse ID no painel do seu projeto.

    export GCLOUD_PROJECT=[MY_PROJECT_ID]
    

Crie uma instância

Ao usar o Cloud Spanner pela primeira vez, é necessário criar uma instância, que é uma alocação de recursos usados pelos bancos de dados do Cloud Spanner. Ao criar uma instância, escolha uma configuração que determine onde os dados serão armazenados e também o número de nós a serem usados. Isso determina a quantidade de recursos de exibição e armazenamento na instância.

Execute o seguinte comando para criar uma instância do Cloud Spanner na região us-central1 com um nó:

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

A instância criada tem as seguintes características:

  • Código da instância: test-instance
  • Nome de exibição: Test Instance
  • Configuração da instância: regional-us-central1 as configurações regionais armazenam dados em uma região, enquanto as configurações multirregionais distribuem dados em várias regiões. Saiba mais em Instâncias.
  • Um nó node_count corresponde à quantidade de recursos de exibição e armazenamento disponíveis aos bancos de dados na instância. Saiba mais em Contagem de nós.

Você verá:

Creating instance...done.

Examinar arquivos de amostra

No repositório de amostras há uma que demonstra como usar o Cloud Spanner com C++.

Dê uma olhada no arquivo google/cloud/spanner/samples/samples.cc, que mostra como criar um banco de dados e modificar um esquema de banco de dados. Os dados usam o esquema de exemplo que aparece na página Modelo de dados e esquema.

Criar um banco de dados

Crie um banco de dados chamado example-db na instância denominada test-instance executando o seguinte comando na linha de comando.

bazel run //google/cloud/spanner/samples:samples -- \
    create-database $GCLOUD_PROJECT test-instance example-db

Você verá:

Created database [projects/${GCLOUD_PROJECT}/instances/test-instance/databases/example-db]

Você acabou de criar um banco de dados do Cloud Spanner. Veja a seguir o código usado na criação do banco de dados.

void CreateDatabase(google::cloud::spanner::DatabaseAdminClient client,
                    std::string const& project_id,
                    std::string const& instance_id,
                    std::string const& database_id) {
  using ::google::cloud::future;
  using ::google::cloud::StatusOr;
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  future<StatusOr<google::spanner::admin::database::v1::Database>> f =
      client.CreateDatabase(database, {R"""(
          CREATE TABLE Singers (
              SingerId   INT64 NOT NULL,
              FirstName  STRING(1024),
              LastName   STRING(1024),
              SingerInfo BYTES(MAX)
          ) PRIMARY KEY (SingerId))""",
                                       R"""(
          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)"""});
  StatusOr<google::spanner::admin::database::v1::Database> db = f.get();
  if (!db) throw std::runtime_error(db.status().message());
  std::cout << "Created database [" << database << "]\n";
}

O código também define duas tabelas, Singers e Albums, para um aplicativo básico de música. Essas tabelas são usadas em toda esta página. Caso ainda não tenha feito isso, confira o exemplo de esquema.

A próxima etapa é gravar dados no seu banco de dados.

Criar um cliente de banco de dados

Antes de fazer leituras ou gravações, você deve criar um Client.

Ver no GitHub (em inglês) Feedback
auto database = spanner::Database(project_id, instance_id, database_id);
auto connection = spanner::MakeConnection(database);
auto client = spanner::Client(connection);

Um Client permite ler, gravar, consultar e executar transações em um banco de dados do Cloud Spanner. Normalmente, você cria um Client quando seu aplicativo é iniciado, então reutiliza esse Client para ler, gravar e executar transações. Cada cliente usa recursos no Cloud Spanner. O destrutor de Client limpará automaticamente os recursos Client, incluindo as conexões de rede.

Leia mais sobre Client na Referência de C++ do Google Cloud Spanner.

Gravar dados com DML

Você pode inserir dados usando a linguagem de manipulação de dados (DML, na sigla em inglês) em uma transação de leitura/gravação.

Use a função Client::ExecuteDml() para executar uma instrução DML.

void DmlGettingStartedInsert(google::cloud::spanner::Client client) {
  using ::google::cloud::StatusOr;
  namespace spanner = ::google::cloud::spanner;

  auto commit_result = client.Commit(
      [&client](spanner::Transaction txn) -> StatusOr<spanner::Mutations> {
        auto insert = client.ExecuteDml(
            std::move(txn),
            spanner::SqlStatement(
                "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES"
                " (12, 'Melissa', 'Garcia'),"
                " (13, 'Russell', 'Morales'),"
                " (14, 'Jacqueline', 'Long'),"
                " (15, 'Dylan', 'Shaw')"));
        if (!insert) return insert.status();
        return spanner::Mutations{};
      });
  if (!commit_result) {
    throw std::runtime_error(commit_result.status().message());
  }
  std::cout << "Insert was successful [spanner_dml_getting_started_insert]\n";
}

Execute a amostra usando o argumento getting-started-insert.

bazel run //google/cloud/spanner/samples:samples -- \
    getting-started-insert $GCLOUD_PROJECT test-instance example-db

Você verá:

Insert was successful [spanner_dml_getting_started_insert]

Gravar dados com mutações

Também é possível inserir dados usando mutações.

Grave dados usando um objeto Client. A função Client::Commit() cria e confirma uma transação para gravações executadas atomicamente em um único ponto lógico no tempo em colunas, linhas e tabelas de um banco de dados.

Este código mostra como gravar dados usando mutações:

void InsertData(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto insert_singers = spanner::InsertMutationBuilder(
                            "Singers", {"SingerId", "FirstName", "LastName"})
                            .EmplaceRow(1, "Marc", "Richards")
                            .EmplaceRow(2, "Catalina", "Smith")
                            .EmplaceRow(3, "Alice", "Trentor")
                            .EmplaceRow(4, "Lea", "Martin")
                            .EmplaceRow(5, "David", "Lomond")
                            .Build();

  auto insert_albums = spanner::InsertMutationBuilder(
                           "Albums", {"SingerId", "AlbumId", "AlbumTitle"})
                           .EmplaceRow(1, 1, "Total Junk")
                           .EmplaceRow(1, 2, "Go, Go, Go")
                           .EmplaceRow(2, 1, "Green")
                           .EmplaceRow(2, 2, "Forever Hold Your Peace")
                           .EmplaceRow(2, 3, "Terrified")
                           .Build();

  auto commit_result =
      client.Commit(spanner::Mutations{insert_singers, insert_albums});
  if (!commit_result) {
    throw std::runtime_error(commit_result.status().message());
  }
  std::cout << "Insert was successful [spanner_insert_data]\n";
}

Execute a amostra usando o argumento insert-data.

bazel run //google/cloud/spanner/samples:samples -- \
    insert-data $GCLOUD_PROJECT test-instance example-db

Você verá:

Insert was successful [spanner_insert_data]

Consultar dados usando SQL

O Cloud Spanner aceita uma interface SQL nativo para leitura de dados, que você acessa na linha de comando usando a ferramenta de linha de comando gcloud ou programaticamente usando a biblioteca de cliente do Cloud Spanner para C++.

Na linha de comando

Execute a instrução SQL a seguir para ler os valores de todas as colunas da tabela Albums:

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

O resultado será:

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

Como usar a biblioteca de cliente do Cloud Spanner para C++

Além de executar uma instrução SQL na linha de comando, é possível emiti-la de maneira programática usando a biblioteca de cliente do Cloud Spanner para C++.

Use a função Client::ExecuteQuery() para executar a consulta SQL. Veja como emitir a consulta e acessar os dados:

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 const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    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";
}

Execute a amostra usando o argumento query_data.

bazel run //google/cloud/spanner/samples:samples -- \
    query-data $GCLOUD_PROJECT test-instance example-db

Você verá o resultado a seguir:

SingerId: 1     LastName: Richards
SingerId: 2     LastName: Smith
SingerId: 3     LastName: Trentor
SingerId: 4     LastName: Martin
SingerId: 5     LastName: Lomond
SingerId: 12    LastName: Garcia
SingerId: 13    LastName: Morales
SingerId: 14    LastName: Long
SingerId: 15    LastName: Shaw

Consulta usando um parâmetro SQL

É possível usar parâmetros para incluir valores personalizados em instruções SQL. Veja um exemplo de como usar @lastName como parâmetro na cláusula WHERE para consultar registros que contêm um valor específico para LastName.

Ver no GitHub (em inglês) Feedback
void QueryWithParameter(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

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

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

Execute a amostra usando o comando query-with-parameter.

bazel run //google/cloud/spanner/samples:samples -- \
    query-with-parameter $GCLOUD_PROJECT test-instance example-db

Você verá o resultado a seguir:

SingerId: 12    FirstName: Melissa      LastName: Garcia

Ler dados usando a API de leitura

Além da interface SQL, o Cloud Spanner também é compatível com uma interface de leitura.

Use a função Client::Read() para ler linhas do banco de dados. Use um objeto KeySet para definir um conjunto de chaves e intervalos de chaves a serem lidos.

Veja como ler os dados:

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

  auto rows = client.Read("Albums", google::cloud::spanner::KeySet::All(),
                          {"SingerId", "AlbumId", "AlbumTitle"});
  using RowType = std::tuple<std::int64_t, std::int64_t, std::string>;
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumId: " << std::get<1>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<2>(*row) << "\n";
  }

  std::cout << "Read completed for [spanner_read_data]\n";
}

Execute a amostra usando o argumento read-data.

bazel run //google/cloud/spanner/samples:samples -- \
    read-data $GCLOUD_PROJECT test-instance example-db

Você verá uma saída como:

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

Atualizar o esquema do banco de dados

Suponha que você precise adicionar uma nova coluna denominada MarketingBudget à tabela Albums. Para isso, é necessário atualizar seu esquema de banco de dados. O Cloud Spanner é compatível com atualizações de esquema em um banco de dados enquanto esse banco continua a disponibilizar o tráfego. Para fazer atualizações no esquema, não é necessário desconectar o banco de dados, nem bloquear tabelas ou colunas inteiras. É possível continuar gravando dados no banco de dados durante a atualização do esquema. Leia mais sobre as atualizações de esquemas compatíveis e o desempenho das alterações de esquemas em Atualizações de esquema.

Adicionar uma coluna

É possível adicionar uma coluna na linha de comando usando a ferramenta de linha de comando gcloud ou programaticamente usando a biblioteca de cliente do Cloud Spanner para C++.

Na linha de comando

Use o seguinte comando ALTER TABLE para adicionar a nova coluna à tabela:

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

Você verá:

Schema updating...done.

Como usar a biblioteca de cliente do Cloud Spanner para C++

Use a função DatabaseAdminClient::UpdateDatabase() para modificar o esquema.

void AddColumn(google::cloud::spanner::DatabaseAdminClient client,
               std::string const& project_id, std::string const& instance_id,
               std::string const& database_id) {
  using ::google::cloud::future;
  using ::google::cloud::StatusOr;
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  future<
      StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>>
      f = client.UpdateDatabase(
          database, {"ALTER TABLE Albums ADD COLUMN MarketingBudget INT64"});
  StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>
      metadata = f.get();
  if (!metadata) throw std::runtime_error(metadata.status().message());
  std::cout << "Added MarketingBudget column\n";
}

Execute a amostra usando o comando add-column.

bazel run //google/cloud/spanner/samples:samples -- \
    add-column $GCLOUD_PROJECT test-instance example-db

Você verá:

Added MarketingBudget column

Gravar dados na nova coluna

O código a seguir grava dados na coluna nova. Ele define MarketingBudget como 100000 para a linha indexada por Albums(1, 1) e como 500000 para a linha indexada por Albums(2, 2).

void UpdateData(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto commit_result = client.Commit(spanner::Mutations{
      spanner::UpdateMutationBuilder("Albums",
                                     {"SingerId", "AlbumId", "MarketingBudget"})
          .EmplaceRow(1, 1, 100000)
          .EmplaceRow(2, 2, 500000)
          .Build()});
  if (!commit_result) {
    throw std::runtime_error(commit_result.status().message());
  }
  std::cout << "Update was successful [spanner_update_data]\n";
}

Execute a amostra usando o argumento update-data.

bazel run //google/cloud/spanner/samples:samples -- \
    update-data $GCLOUD_PROJECT test-instance example-db

Também é possível executar uma consulta SQL ou uma chamada de leitura para coletar os valores que você acabou de gravar.

Veja a seguir o código para executar a consulta:

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

  spanner::SqlStatement select(
      "SELECT SingerId, AlbumId, MarketingBudget FROM Albums");

  using RowType =
      std::tuple<std::int64_t, std::int64_t, absl::optional<std::int64_t>>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumId: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << marketing_budget.value() << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_read_data_with_new_column]\n";
}

Para executar essa consulta, execute a amostra usando o argumento query-new-column.

bazel run //google/cloud/spanner/samples:samples -- \
    query-new-column $GCLOUD_PROJECT test-instance example-db

Você verá:

SingerId: 1 AlbumId: 1  MarketingBudget: 100000
SingerId: 1 AlbumId: 2  MarketingBudget: NULL
SingerId: 2 AlbumId: 1  MarketingBudget: NULL
SingerId: 2 AlbumId: 2  MarketingBudget: 500000
SingerId: 2 AlbumId: 3  MarketingBudget: NULL

Atualizar dados

É possível atualizar dados usando DML em uma transação de leitura/gravação.

Use a função Client::ExecuteDml() para executar uma instrução DML.

void DmlGettingStartedUpdate(google::cloud::spanner::Client client) {
  using ::google::cloud::StatusOr;
  namespace spanner = ::google::cloud::spanner;

  // A helper to read the budget for the given album and singer.
  auto get_budget = [&](spanner::Transaction txn, std::int64_t album_id,
                        std::int64_t singer_id) -> StatusOr<std::int64_t> {
    auto key = spanner::KeySet().AddKey(spanner::MakeKey(album_id, singer_id));
    auto rows = client.Read(std::move(txn), "Albums", key, {"MarketingBudget"});
    using RowType = std::tuple<absl::optional<std::int64_t>>;
    auto row = spanner::GetSingularRow(spanner::StreamOf<RowType>(rows));
    if (!row) return row.status();
    auto const budget = std::get<0>(*row);
    return budget ? *budget : 0;
  };

  // A helper to update the budget for the given album and singer.
  auto update_budget = [&](spanner::Transaction txn, std::int64_t album_id,
                           std::int64_t singer_id, std::int64_t budget) {
    auto sql = spanner::SqlStatement(
        "UPDATE Albums SET MarketingBudget = @AlbumBudget "
        "WHERE SingerId = @SingerId AND AlbumId = @AlbumId",
        {{"AlbumBudget", spanner::Value(budget)},
         {"AlbumId", spanner::Value(album_id)},
         {"SingerId", spanner::Value(singer_id)}});
    return client.ExecuteDml(std::move(txn), std::move(sql));
  };

  auto const transfer_amount = 20000;
  auto commit_result = client.Commit(
      [&](spanner::Transaction const& txn) -> StatusOr<spanner::Mutations> {
        auto budget1 = get_budget(txn, 1, 1);
        if (!budget1) return budget1.status();
        if (*budget1 < transfer_amount) {
          return google::cloud::Status(
              google::cloud::StatusCode::kUnknown,
              "cannot transfer " + std::to_string(transfer_amount) +
                  " from budget of " + std::to_string(*budget1));
        }
        auto budget2 = get_budget(txn, 2, 2);
        if (!budget2) return budget2.status();
        auto update = update_budget(txn, 1, 1, *budget1 - transfer_amount);
        if (!update) return update.status();
        update = update_budget(txn, 2, 2, *budget2 + transfer_amount);
        if (!update) return update.status();
        return spanner::Mutations{};
      });
  if (!commit_result) {
    throw std::runtime_error(commit_result.status().message());
  }
  std::cout << "Update was successful [spanner_dml_getting_started_update]\n";
}

Execute a amostra usando o argumento getting-started-update.

bazel run //google/cloud/spanner/samples:samples -- \
    getting-started-update $GCLOUD_PROJECT test-instance example-db

Você verá:

Update was successful [spanner_dml_getting_started_update]

Usar um índice secundário

Suponha que você queira buscar todas as linhas de Albums que tenham valores AlbumTitle em um determinado intervalo. É possível ler todos os valores da coluna AlbumTitle usando uma instrução SQL ou uma chamada de leitura e descartar as linhas que não satisfazem os critérios, mas fazer essa verificação na tabela inteira é caro, especialmente para tabelas com muitas linhas. Em vez disso, acelere a recuperação de linhas ao pesquisar por colunas de chaves não primárias por meio da criação de um índice secundário na tabela.

Adicionar um índice secundário a uma tabela requer uma atualização de esquema. Como outras atualizações de esquema, o Cloud Spanner é compatível com a adição de um índice enquanto o banco de dados continua a disponibilizar o tráfego. O Cloud Spanner preenche automaticamente o índice com seus dados atuais. Os preenchimentos podem levar alguns minutos para serem concluídos, mas você não precisa ficar off-line ou evitar gravar na tabela indexada durante esse processo. Para mais detalhes, consulte o preenchimento de índices.

Depois que você adiciona um índice secundário, o Cloud Spanner o usa automaticamente para consultas SQL que provavelmente serão executadas mais rapidamente com o índice. Se você usar a interface de leitura, deverá especificar o índice que quer usar.

Adicionar um índice secundário

É possível adicionar um índice na linha de comando usando a ferramenta de linha de comando gcloud ou programaticamente usando a biblioteca de cliente do Cloud Spanner para C++.

Na linha de comando

Use o comando CREATE INDEX a seguir para adicionar um índice ao banco de dados:

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

Você verá:

Schema updating...done.

Como usar a biblioteca de cliente do Cloud Spanner para C++

Use a função DatabaseAdminClient::UpdateDatabase() para adicionar um índice:

void AddIndex(google::cloud::spanner::DatabaseAdminClient client,
              std::string const& project_id, std::string const& instance_id,
              std::string const& database_id) {
  using ::google::cloud::future;
  using ::google::cloud::StatusOr;
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  future<
      StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>>
      f = client.UpdateDatabase(
          database, {"CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"});
  StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>
      metadata = f.get();
  if (!metadata) throw std::runtime_error(metadata.status().message());
  std::cout << "`AlbumsByAlbumTitle` Index successfully added, new DDL:\n"
            << metadata->DebugString() << "\n";
}

Execute a amostra usando o argumento add-index.

bazel run //google/cloud/spanner/samples:samples -- \
    add-index $GCLOUD_PROJECT test-instance example-db

A adição do índice pode levar alguns minutos. Depois que o índice for adicionado, você verá um resultado semelhante a este:

`AlbumsByAlbumTitle` Index successfully added, new DDL:
database: "projects/$GCLOUD_PROJECT/instances/test-instance/databases/example-db"
statements: "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"
commit_timestamps {
  seconds: 1581011550
  nanos: 531102000
}

Ler usando o índice

Para consultas SQL, o Cloud Spanner usa automaticamente um índice apropriado. Na interface de leitura, especifique o índice em sua solicitação.

Para usar o índice na interface de leitura, use a função Client::Read(), que lê zero ou mais linhas de um banco de dados usando um índice.

O código a seguir busca todas as colunas AlbumId e AlbumTitle do índice AlbumsByAlbumTitle.

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

  spanner::ReadOptions read_options;
  read_options.index_name = "AlbumsByAlbumTitle";
  auto rows = client.Read("Albums", google::cloud::spanner::KeySet::All(),
                          {"AlbumId", "AlbumTitle"}, read_options);
  using RowType = std::tuple<std::int64_t, std::string>;
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\n";
  }
  std::cout << "Read completed for [spanner_read_data_with_index]\n";
}

Execute a amostra usando o argumento read-data-with-index.

bazel run //google/cloud/spanner/samples:samples -- \
    read-data-with-index $GCLOUD_PROJECT test-instance example-db

Você verá:

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

Adicionar um índice com uma cláusula STORING

Talvez você tenha percebido que o exemplo de leitura acima não incluiu a leitura da coluna MarketingBudget. Isso ocorre porque a interface de leitura do Cloud Spanner não é compatível com a capacidade de fazer a junção de um índice a uma tabela de dados para pesquisar valores que não estão armazenados no índice.

Crie uma definição alternativa de AlbumsByAlbumTitle que armazene uma cópia de MarketingBudget no índice.

Na linha de comando

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

A adição do índice pode levar alguns minutos. Depois da adição, você verá:

Schema updating...done.

Como usar a biblioteca de cliente do Cloud Spanner para C++

Use a função DatabaseAdminClient::UpdateDatabase() para adicionar um índice com uma cláusula STORING:

void AddStoringIndex(google::cloud::spanner::DatabaseAdminClient client,
                     std::string const& project_id,
                     std::string const& instance_id,
                     std::string const& database_id) {
  using ::google::cloud::future;
  using ::google::cloud::StatusOr;
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  future<
      StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>>
      f = client.UpdateDatabase(database, {R"""(
            CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
                STORING (MarketingBudget))"""});
  StatusOr<google::spanner::admin::database::v1::UpdateDatabaseDdlMetadata>
      metadata = f.get();
  if (!metadata) throw std::runtime_error(metadata.status().message());
  std::cout << "`AlbumsByAlbumTitle2` Index successfully added, new DDL:\n"
            << metadata->DebugString() << "\n";
}

Execute a amostra usando o argumento add-storing-index.

bazel run //google/cloud/spanner/samples:samples -- \
    add-storing-index $GCLOUD_PROJECT test-instance example-db

A resposta será parecida com esta:

`AlbumsByAlbumTitle2` Index successfully added, new DDL:
database: "projects/$GCLOUD_PROJECT/instances/test-instance/databases/example-db"
statements: "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)"
commit_timestamps {
  seconds: 1581012328
  nanos: 416682000
}

Agora é possível executar uma leitura que busque todas as colunas AlbumId, AlbumTitle e MarketingBudget do índice AlbumsByAlbumTitle2:

Leia os dados usando o índice de armazenamento criado, executando uma consulta que especifique de forma explícita o índice:

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

  spanner::ReadOptions read_options;
  read_options.index_name = "AlbumsByAlbumTitle2";
  auto rows =
      client.Read("Albums", google::cloud::spanner::KeySet::All(),
                  {"AlbumId", "AlbumTitle", "MarketingBudget"}, read_options);
  using RowType =
      std::tuple<std::int64_t, std::string, absl::optional<std::int64_t>>;
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << marketing_budget.value() << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_read_data_with_storing_index]\n";
}

Execute a amostra usando o argumento read-data-with-storing-index.

bazel run //google/cloud/spanner/samples:samples -- \
    read-data-with-storing-index $GCLOUD_PROJECT test-instance example-db

Você verá uma saída como:

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

Recuperar dados usando transações somente leitura

Suponha que você queira executar mais de uma leitura no mesmo carimbo de data/hora. As transações somente leitura observam um prefixo consistente do histórico de confirmações da transação. Portanto, o aplicativo sempre recebe dados consistentes. O tipo Transaction é usado para representar todos os tipos de transações. Use a função de fábrica MakeReadOnlyTransaction() para criar uma transação somente leitura.

Veja a seguir como executar uma consulta e fazer uma leitura na mesma transação somente leitura.

void ReadOnlyTransaction(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto read_only = spanner::MakeReadOnlyTransaction();

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

  // Read#1.
  auto rows1 = client.ExecuteQuery(read_only, select);
  std::cout << "Read 1 results\n";
  for (auto const& row : spanner::StreamOf<RowType>(rows1)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "SingerId: " << std::get<0>(*row)
              << " AlbumId: " << std::get<1>(*row)
              << " AlbumTitle: " << std::get<2>(*row) << "\n";
  }
  // Read#2. Even if changes occur in-between the reads the transaction ensures
  // that Read #1 and Read #2 return the same data.
  auto rows2 = client.ExecuteQuery(read_only, select);
  std::cout << "Read 2 results\n";
  for (auto const& row : spanner::StreamOf<RowType>(rows2)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "SingerId: " << std::get<0>(*row)
              << " AlbumId: " << std::get<1>(*row)
              << " AlbumTitle: " << std::get<2>(*row) << "\n";
  }
}

Execute a amostra usando o argumento read-only-transaction.

bazel run //google/cloud/spanner/samples:samples -- \
    read-only-transaction $GCLOUD_PROJECT test-instance example-db

Você verá uma saída como:

Read 1 results
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
Read 2 results
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

Limpeza

Para não gerar cobranças extras na sua conta do Google Cloud pelos recursos usados neste tutorial, suspenda o banco de dados e exclua a instância que você criou.

Excluir o banco de dados

Se você excluir uma instância, todos os bancos de dados nela serão excluídos automaticamente. Nesta etapa, mostramos como excluir um banco de dados sem remover a instância. Ainda pode haver cobrança em relação à instância.

Na linha de comando

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

No Console do Cloud

  1. Acesse a página instâncias do Cloud Spanner no Console do Google Cloud.

    Acessar a página "Instâncias"

  2. Clique na instância.

  3. Clique no banco de dados que você quer excluir.

  4. Na página Detalhes do banco de dados, clique em Excluir.

  5. Confirme se quer excluir o banco de dados e clique em Excluir.

Excluir a instância

A exclusão de uma instância descarta automaticamente todos os bancos de dados criados nela.

Na linha de comando

gcloud spanner instances delete test-instance

No Console do Cloud

  1. Acesse a página instâncias do Cloud Spanner no Console do Google Cloud.

    Acessar a página "Instâncias"

  2. Clique na sua instância.

  3. Clique em Excluir.

  4. Confirme se quer excluir a instância e clique em Excluir.

A seguir

  • Acesse o Cloud Spanner em uma instância de máquina virtual: crie uma instância de máquina virtual com acesso ao seu banco de dados do Cloud Spanner.
  • Saiba mais sobre credenciais de autorização e autenticação em Primeiros passos da autenticação.
  • Saiba mais sobre os conceitos do Cloud Spanner.