Linguaggio di manipolazione dei dati partizionato

Data Manipulation Language (partizionato) DML) è progettato per i seguenti tipi di aggiornamenti ed eliminazioni collettive:

  • Pulizia periodica e garbage collection. Alcuni esempi sono l'eliminazione di righe precedenti o impostando le colonne su NULL.
  • Backfill di nuove colonne con valori predefiniti. Un esempio è l'utilizzo di UPDATE per impostare il valore di una nuova colonna su False, dove è attualmente NULL.

Il DML partizionato non è adatto per l'elaborazione di transazioni su scala ridotta. Se eseguire un'istruzione su più righe, utilizzare DML transazionali con e le chiavi primarie. Per ulteriori informazioni, consulta la sezione Utilizzo di DML.

Se devi eseguire il commit di un numero elevato di scritture cieche, ma non è necessario una transazione atomica, puoi modificare collettivamente le tabelle Spanner utilizzando la scrittura in batch. Per saperne di più, consulta Modificare i dati utilizzando le scritture in batch.

Puoi ottenere insight sulle query DML partizionate attive e sul loro avanzamento o tabelle di statistiche nel tuo database Spanner. Per ulteriori informazioni, consulta le statistiche relative alle DML partizionate attive.

DML e DML partizionato

Spanner supporta due modalità di esecuzione per le istruzioni DML:

  • DML, adatto all'elaborazione delle transazioni. Per ulteriori informazioni, vedi Utilizzo di DML.

  • DML partizionato, che consente operazioni su larga scala a livello di database con Impatto minimo sull'elaborazione delle transazioni simultanee grazie al partizionamento della chiave spazio ed eseguire l'istruzione su partizioni in aree separate e con ambito più ristretto transazioni. Per maggiori informazioni, consulta la sezione Utilizzo di DML partizionato.

La tabella seguente evidenzia alcune delle differenze tra i due modelli di esecuzione diverse.

DML DML partizionato
Le righe che non corrispondono alla clausola WHERE potrebbero essere bloccate. Solo le righe che corrispondono alla clausola WHERE vengono bloccate.
Si applicano limiti per le dimensioni delle transazioni. Spanner gestisce i limiti delle transazioni e i limiti di contemporaneità per transazione.
Le dichiarazioni non devono essere idempotenti. Un'istruzione DML deve essere idempotente per garantire risultati coerenti.
Una transazione può includere più istruzioni DML e SQL. Una transazione partizionata può includere una sola istruzione DML.
Non ci sono limitazioni alla complessità degli estratti conto. Le istruzioni devono essere completamente partizionabili.
Puoi creare transazioni di lettura/scrittura nel codice client. Spanner crea le transazioni.

Partizionabile e idempotente

Quando viene eseguita un'istruzione DML partizionata, le righe di una partizione non hanno accesso alle righe in altre partizioni e non puoi scegliere il modo in cui Spanner crea le partizioni. Il partizionamento garantisce scalabilità, ma significa anche che le istruzioni DML partizionate devono essere completamente partizionabili. Vale a dire che l'istruzione DML partizionata deve essere espressibile come l'unione di un insieme dove ogni istruzione accede a una singola riga della tabella non accede ad altre tabelle. Ad esempio, un'istruzione DML che accede più tabelle o esegue un self-join non partizionabili. Se il file DML non è partizionabile, Spanner restituisce l'errore BadUsage.

Queste istruzioni DML sono completamente partizionabili, perché ogni istruzione essere applicati a una singola riga nella tabella:

UPDATE Singers SET LastName = NULL WHERE LastName = '';

DELETE FROM Albums WHERE MarketingBudget > 10000;

Questa istruzione DML non è completamente partizionabile perché accede a più tabelle:

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

Spanner potrebbe eseguire più volte un'istruzione DML partizionata in alcune partizioni a causa di nuovi tentativi a livello di rete. Di conseguenza, una dichiarazione potrebbe essere eseguito più di una volta su una riga. L'affermazione deve quindi essere idempotente per ottenere risultati coerenti. Un'istruzione è idempotente se viene eseguita più volte rispetto a una singola riga porta allo stesso risultato.

Questa istruzione DML è idempotente:

UPDATE Singers SET MarketingBudget = 1000 WHERE true;

Questa istruzione DML non è idempotente:

UPDATE Singers SET MarketingBudget = 1.5 * MarketingBudget WHERE true;

Blocco delle righe

Spanner acquisisce un blocco solo se una riga è candidata per l'aggiornamento o l'eliminazione dei dati. Questo comportamento è diverso Esecuzione di DML, che potrebbe essere bloccata in lettura righe che non corrispondono alla clausola WHERE.

Esecuzione e transazioni

Il fatto che un'istruzione DML sia partizionata o meno dipende dalla libreria client che scegli per l'esecuzione. Ogni libreria client fornisce per l'esecuzione di DML e Esecuzione DML partizionata.

Puoi eseguire una sola istruzione DML partizionata in una chiamata al client .

Spanner non applica le istruzioni DML partizionate atomicamente nell'intera tabella. Spanner, invece, applica partizionate Istruzioni DML atomicamente su ogni partizione.

Il DML partizionato non supporta il commit o il rollback. Spanner esegue e applica immediatamente l'istruzione DML.

  • Se annulli l'operazione, Spanner annulla l'esecuzione e non avvia le partizioni rimanenti. Spanner non esegue il rollback delle partizioni che sono già eseguito.
  • Se l'esecuzione dell'istruzione causa un errore, l'esecuzione si interrompe tra tutte le partizioni e Spanner restituisce questo errore per l'intera operazione. Alcuni esempi di errori sono violazioni del tipo di dati vincoli, violazioni di UNIQUE INDEX e violazioni di ON DELETE NO ACTION. A seconda del momento in cui l'esecuzione non è riuscita, potrebbe essere stata eseguita correttamente su alcune partizioni non sono mai state eseguite su altre partizioni.

Se l'istruzione DML partizionata ha esito positivo, Spanner ha eseguito almeno una volta per ogni partizione dell'intervallo di chiavi.

Conteggio delle righe modificate

Un'istruzione DML partizionata restituisce un limite inferiore al numero di righe. Potrebbe non essere un conteggio esatto del numero di righe modificate, non c'è alcuna garanzia che Spanner conteggi tutte le righe modificate.

Limiti transazioni

Spanner crea le partizioni e le transazioni di cui ha bisogno un'istruzione DML partizionata. Limiti di transazioni o per transazione si applicano limiti di contemporaneità, ma Spanner tenta di mantenere transazioni entro i limiti.

Spanner consente un massimo di 20.000 istruzioni DML partizionate in contemporanea per database.

Funzionalità non supportate

Spanner non supporta alcune funzionalità per il DML partizionato:

  • INSERT non supportato.
  • Console Google Cloud: non puoi eseguire istruzioni DML partizionate nella nella console Google Cloud.
  • Piani di query e profilazione: Google Cloud CLI e il client le librerie non supportano piani di query e profilazione.
  • Sottoquery che leggono da un'altra tabella o da un'altra riga della stessa tabella.

Per scenari complessi, come lo spostamento di una tabella o trasformazioni che richiedono join tra le tabelle, prendi in considerazione l'utilizzo del connettore Dataflow.

Esempi

Il seguente esempio di codice aggiorna la colonna MarketingBudget di Albums .

C++

Puoi utilizzare la funzione ExecutePartitionedDml() per eseguire un'istruzione DML partizionata.

void DmlPartitionedUpdate(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto result = client.ExecutePartitionedDml(
      spanner::SqlStatement("UPDATE Albums SET MarketingBudget = 100000"
                            "  WHERE SingerId > 1"));
  if (!result) throw std::move(result).status();
  std::cout << "Updated at least " << result->row_count_lower_bound
            << " row(s) [spanner_dml_partitioned_update]\n";
}

C#

Utilizzi il metodo ExecutePartitionedUpdateAsync() per eseguire un'istruzione DML partizionata.


using Google.Cloud.Spanner.Data;
using System;
using System.Threading.Tasks;

public class UpdateUsingPartitionedDmlCoreAsyncSample
{
    public async Task<long> UpdateUsingPartitionedDmlCoreAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        using var cmd = connection.CreateDmlCommand("UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1");
        long rowCount = await cmd.ExecutePartitionedUpdateAsync();

        Console.WriteLine($"{rowCount} row(s) updated...");
        return rowCount;
    }
}

Vai

Utilizzi il metodo PartitionedUpdate() per eseguire un'istruzione DML partizionata.


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

func updateUsingPartitionedDML(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: "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

Utilizzi il metodo executePartitionedUpdate() per eseguire un'istruzione DML partizionata.

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

Utilizzi il metodo runPartitionedUpdate() per eseguire un'istruzione DML partizionata.

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

Utilizzi il metodo executePartitionedUpdate() per eseguire un'istruzione DML partizionata.

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(string $instanceId, string $databaseId): void
{
    $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

Utilizzi il metodo execute_partitioned_dml() per eseguire un'istruzione DML partizionata.

# 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

Utilizzi il metodo execute_partitioned_update() per eseguire un'istruzione DML partizionata.

# 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."

Il seguente esempio di codice elimina le righe dalla tabella Singers in base al SingerId.

C++

void DmlPartitionedDelete(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  auto result = client.ExecutePartitionedDml(
      spanner::SqlStatement("DELETE FROM Singers WHERE SingerId > 10"));
  if (!result) throw std::move(result).status();
  std::cout << "Deleted at least " << result->row_count_lower_bound
            << " row(s) [spanner_dml_partitioned_delete]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System;
using System.Threading.Tasks;

public class DeleteUsingPartitionedDmlCoreAsyncSample
{
    public async Task<long> DeleteUsingPartitionedDmlCoreAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        using var cmd = connection.CreateDmlCommand("DELETE FROM Singers WHERE SingerId > 10");
        long rowCount = await cmd.ExecutePartitionedUpdateAsync();

        Console.WriteLine($"{rowCount} row(s) deleted...");
        return rowCount;
    }
}

Vai


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

func deleteUsingPartitionedDML(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{SQL: "DELETE FROM 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 FROM 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(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $rowCount = $database->executePartitionedUpdate(
        'DELETE FROM 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 FROM 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."

Passaggi successivi