Linguagem de manipulação de dados particionada

A linguagem de manipulação de dados particionada (DML particionada) foi projetada para atualizações e exclusões em massa:

  • Limpeza periódica e coleta de lixo. Alguns exemplos são excluir linhas antigas ou definir colunas como NULL.
  • Preenchimento de novas colunas com valores padrão. Um exemplo é usar uma declaração UPDATE para definir o valor de uma nova coluna como False, onde atualmente há o valor NULL.

DML e DML particionada

O Cloud Spanner aceita dois modos de execução para declarações DML.

  • A DML é adequada para o processamento de transações.
  • A DML particionada permite operações em larga escala, em todo o banco de dados, com impacto mínimo no processamento de transações simultâneas, particionando o espaço de chave e executando a declaração nas partições em transações separadas, com escopo menor.

A tabela a seguir destaca algumas das diferenças entre os dois modos de execução.

DML DML particionada
Linhas que não correspondem à cláusula WHERE podem ser bloqueadas. Apenas as linhas que correspondem à cláusula WHERE são bloqueadas.
Limites de tamanho de transação são aplicáveis. O Cloud Spanner lida com os limites de transação e os limites de simultaneidade por transação.
As declarações não precisam ser idempotentes. Uma declaração DML precisa ser idempotente para garantir resultados consistentes.
Uma transação pode incluir várias declarações DML e instruções SQL. Uma transação particionada pode incluir apenas uma declaração DML.
Não há restrições à complexidade das declarações. As declarações precisam ser totalmente particionáveis.
Você cria transações de leitura/gravação no seu código de cliente. O Cloud Spanner cria as transações.

Particionável e idempotente

Quando uma declaração DML particionada é executada, as linhas de uma partição não têm acesso a linhas em outras partições, e não é possível escolher como o Cloud Spanner cria as partições. O particionamento garante a escalonabilidade, mas também significa que as declarações DML particionadas precisam ser totalmente particionáveis. Ou seja, a declaração DML particionada precisa ser expressável como a união de um conjunto de declarações, em que cada declaração acessa uma única linha da tabela e cada declaração não acessa outras tabelas. Por exemplo, uma declaração DML que acessa várias tabelas ou realiza uma união automática não é particionável. Se a declaração DML não puder ser particionada, o Cloud Spanner retornará o erro BadUsage.

Estas declarações DML são totalmente particionáveis, porque cada uma pode ser aplicada a uma única linha na tabela:

UPDATE Singers SET Available = TRUE WHERE Available IS NULL

DELETE FROM Concerts
WHERE DATE_DIFF(CURRENT_DATE(), ConcertDate, DAY) > 365

Esta declaração DML não é totalmente particionável, porque acessa várias tabelas:

# Not fully partitionable
DELETE FROM Singers WHERE
SingerId NOT IN (SELECT SingerId FROM Concerts);

O Cloud Spanner pode executar uma declaração DML particionada várias vezes em algumas partições devido a novas tentativas no nível da rede. Como resultado, uma declaração pode ser executada mais de uma vez em uma linha. A declaração precisa, portanto, ser idempotente para produzir resultados consistentes. Uma declaração é idempotente se executá-la várias vezes em uma única linha levar ao mesmo resultado.

Esta declaração DML é idempotente:

UPDATE Singers SET MarketingBudget = 1000 WHERE true

Esta não é:

UPDATE Singers SET MarketingBudget = 1.5 * MarketingBudget WHERE true

Bloqueio

O Cloud Spanner adquire um bloqueio somente se uma linha for candidata a atualização ou exclusão. Esse comportamento é diferente da execução de DML, que pode bloquear as linhas que não correspondem à cláusula WHERE.

Execução e transações

Se uma declaração DML é particionada ou não, depende do método da biblioteca de cliente que você escolhe para execução. Cada biblioteca de cliente fornece métodos separados para execução de DML e execução de DML particionada.

Você pode executar apenas uma declaração DML particionada em uma chamada para o método da biblioteca de cliente.

O Cloud Spanner não aplica as declarações DML particionadas atomicamente em toda a tabela. No entanto, ele aplica declarações DML particionadas atomicamente em cada partição.

A DML particionada não é compatível com consolidação ou reversão. O Cloud Spanner é executado e aplica a declaração DML imediatamente.

  • Se você cancelar a operação, o Cloud Spanner cancela as partições em execução e não inicia as partições restantes. O Cloud Spanner não reverte quaisquer partições que já tenham sido executadas.
  • Se a execução da declaração causar um erro, a execução será interrompida em todas as partições e o Cloud Spanner retornará esse erro para toda a operação. Alguns exemplos de erros são violações de restrições de tipo de dados, violações de UNIQUE INDEX e violações de ON DELETE NO ACTION. Dependendo do momento em que a execução falhou, a declaração pode ter sido executada com êxito em algumas partições e pode nunca ter sido executada em outras.

Se a declaração DML particionada for bem-sucedida, o Cloud Spanner executará a declaração pelo menos uma vez em cada partição do intervalo de chaves.

Contagem de linhas modificadas

Uma declaração DML particionada retorna um limite inferior do número de linhas modificadas. Pode não ser uma contagem exata do número de linhas modificadas, porque não há garantia de que o Cloud Spanner conta todas as linhas modificadas.

Limites de transação

O Cloud Spanner cria as partições e transações necessárias para executar uma declaração DML particionada. Limites de transação ou limites de simultaneidade por transação se aplicam, mas o Cloud Spanner tenta manter as transações dentro dos limites.

O Cloud Spanner permite no máximo 20.000 declarações DML particionadas simultâneas por banco de dados.

Recursos que não são compatíveis

O Cloud Spanner não aceita alguns recursos para DML particionada:

  • INSERT não é compatível.
  • Console do GCP: não é possível executar declarações DML particionadas no Console do GCP.
  • Planos de consulta e criação de perfil: a ferramenta de linha de comando gcloud e as bibliotecas de cliente não aceitam planos de consulta e criação de perfil.

Exemplos

O exemplo de código a seguir atualiza a coluna MarketingBudget da tabela Albums.

C#

Use o método ExecutePartitionedUpdateAsync() para executar uma declaração DML particionada.

public static async Task UpdateUsingPartitionedDmlCoreAsync(
    string projectId,
    string instanceId,
    string databaseId)
{
    string connectionString =
        $"Data Source=projects/{projectId}/instances/{instanceId}"
        + $"/databases/{databaseId}";

    // Create connection to Cloud Spanner.
    using (var connection =
        new SpannerConnection(connectionString))
    {
        await connection.OpenAsync();

        SpannerCommand cmd = connection.CreateDmlCommand(
            "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"
        );
        long rowCount = await cmd.ExecutePartitionedUpdateAsync();
        Console.WriteLine($"{rowCount} row(s) updated...");
    }
}

Go

Use o método PartitionedUpdate() para executar uma declaração DML particionada.

func updateUsingPartitionedDML(ctx context.Context, w io.Writer, client *spanner.Client) error {
	stmt := spanner.Statement{SQL: "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"}
	rowCount, err := client.PartitionedUpdate(ctx, stmt)
	if err != nil {
		return err
	}
	fmt.Fprintf(w, "%d record(s) updated.\n", rowCount)
	return nil
}

Java

Use o método executePartitionedUpdate() para executar uma declaração DML particionada.

static void updateUsingPartitionedDml(DatabaseClient dbClient) {
  String sql = "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1";
  long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql));
  System.out.printf("%d records updated.\n", rowCount);
}

Node.js

Use o método runPartitionedUpdate() para executar uma declaração DML particionada.

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

try {
  const [rowCount] = await database.runPartitionedUpdate({
    sql: `UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1`,
  });
  console.log(`Successfully updated ${rowCount} records.`);
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

Use o método executePartitionedUpdate() para executar uma declaração DML particionada.

use Google\Cloud\Spanner\SpannerClient;

/**
 * Updates sample data in the database by partition with a DML statement.
 *
 * 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
 *
 * Example:
 * ```
 * update_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function update_data_with_partitioned_dml($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $rowCount = $database->executePartitionedUpdate(
        "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"
    );

    printf('Updated %d row(s).' . PHP_EOL, $rowCount);
}

Python

Use o método execute_partitioned_dml() para executar uma declaração DML particionada.

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

row_ct = database.execute_partitioned_dml(
    "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"
)

print("{} records updated.".format(row_ct))

Ruby

Use o método execute_partitioned_update() para executar uma declaração DML particionada.

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

row_count = client.execute_partition_update(
  "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"
)

puts "#{row_count} records updated."

O exemplo de código a seguir exclui linhas da tabela Singers, com base na coluna SingerId.

C#

public static async Task DeleteUsingPartitionedDmlCoreAsync(
    string projectId,
    string instanceId,
    string databaseId)
{
    string connectionString =
        $"Data Source=projects/{projectId}/instances/{instanceId}"
        + $"/databases/{databaseId}";

    // Create connection to Cloud Spanner.
    using (var connection =
        new SpannerConnection(connectionString))
    {
        await connection.OpenAsync();

        SpannerCommand cmd = connection.CreateDmlCommand(
            "DELETE Singers WHERE SingerId > 10"
        );
        long rowCount = await cmd.ExecutePartitionedUpdateAsync();
        Console.WriteLine($"{rowCount} row(s) deleted...");
    }
}

Go

func deleteUsingPartitionedDML(ctx context.Context, w io.Writer, client *spanner.Client) error {
	stmt := spanner.Statement{SQL: "DELETE Singers WHERE SingerId > 10"}
	rowCount, err := client.PartitionedUpdate(ctx, stmt)
	if err != nil {
		return err

	}
	fmt.Fprintf(w, "%d record(s) deleted.", rowCount)
	return nil
}

Java

static void deleteUsingPartitionedDml(DatabaseClient dbClient) {
  String sql = "DELETE FROM Singers WHERE SingerId > 10";
  long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql));
  System.out.printf("%d records deleted.\n", rowCount);
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

try {
  const [rowCount] = await database.runPartitionedUpdate({
    sql: `DELETE Singers WHERE SingerId > 10`,
  });
  console.log(`Successfully deleted ${rowCount} records.`);
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Delete sample data in the database by partition with a DML statement.
 *
 * 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
 *
 * Example:
 * ```
 * update_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function delete_data_with_partitioned_dml($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $rowCount = $database->executePartitionedUpdate(
        "DELETE Singers WHERE SingerId > 10"
    );

    printf('Deleted %d row(s).' . PHP_EOL, $rowCount);
}

Python

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

row_ct = database.execute_partitioned_dml(
    "DELETE Singers WHERE SingerId > 10"
)

print("{} record(s) deleted.".format(row_ct))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

row_count = client.execute_partition_update(
  "DELETE FROM Singers WHERE SingerId > 10"
)

puts "#{row_count} records deleted."
Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Cloud Spanner