Cloud Spanner é o banco de dados relacional distribuído do GCP. Combina transações ACID com consistência externa e escalabilidade horizontal ilimitada, algo que o teorema CAP dizia ser impossível, viabilizado pelo uso de relógios atômicos (TrueTime).
É a implementação comercial do paper “Spanner: Google’s Globally-Distributed Database” (2012).
Quando usar Spanner
| Use Spanner | Não use |
|---|---|
| ACID + escala horizontal (> 2 TB ou > 2000 QPS de escrita) | Cargas analíticas (use gcp-bigquery) |
| Distribuição global com consistência garantida | Dataset pequeno ou previsível (use gcp-cloud-sql) |
| SLA 99.999% em multi-região | Dados de documento hierárquico (use gcp-firestore) |
| Migração de bancos relacionais que não cabem mais em um nó | Cache ou lookups por chave simples (use gcp-memorystore) |
Hierarquia de objetos
Instance (configuração regional/global)
└── Database
├── Tables
├── Views
├── Indexes
└── Change Streams
Configurações de instância
| Tipo | Exemplo de config | Latência de escrita | SLA |
|---|---|---|---|
| Regional | regional-southamerica-east1 | ~5ms | 99.99% |
| Multi-regional US | nam4 | ~10ms | 99.999% |
| Multi-regional EU | eur3 | ~10ms | 99.999% |
| Global | nam-eur-asia1 | ~20-30ms | 99.999% |
Capacidade de compute
Compute é provisionado em Processing Units (PUs) ou nodes (1 node = 1000 PUs):
# Criar instância com 1 node (produção)
gcloud spanner instances create minha-instancia \
--config=regional-southamerica-east1 \
--nodes=1 \
--description="Produção"
# Instância menor para dev/staging (100 PUs = ~$65/mês)
gcloud spanner instances create dev-instancia \
--config=regional-southamerica-east1 \
--processing-units=100Schema e DDL
Spanner suporta GoogleSQL (padrão) e PostgreSQL dialect (modo de compatibilidade):
-- Tabela principal
CREATE TABLE Pedidos (
PedidoId STRING(36) NOT NULL,
ClienteId STRING(36) NOT NULL,
Status STRING(20),
Total NUMERIC,
CriadoEm TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (PedidoId);
-- Índice secundário
CREATE INDEX PedidosPorCliente ON Pedidos(ClienteId);
-- Índice com STORING (copia colunas para evitar lookup na tabela base)
CREATE INDEX PedidosPorStatus ON Pedidos(Status)
STORING (CriadoEm, ClienteId, Total);Interleaving (co-localização de dados)
Tabelas interleaved têm suas linhas armazenadas fisicamente junto com as linhas pai. Elimina round-trips de rede em JOINs pai-filho frequentes:
CREATE TABLE ItensPedido (
PedidoId STRING(36) NOT NULL,
ItemId STRING(36) NOT NULL,
Produto STRING(100),
Quantidade INT64,
Preco NUMERIC
) PRIMARY KEY (PedidoId, ItemId),
INTERLEAVE IN PARENT Pedidos ON DELETE CASCADE;Design de row key
O primary key determina onde os dados ficam armazenados. Hotspots surgem quando muitas escritas vão para o mesmo “split” (fragmento):
-- Ruim: timestamp crescente como prefixo concentra escritas no split mais recente
PRIMARY KEY (CriadoEm, EventoId)
-- Ruim: auto-increment sequencial concentra escritas no maior valor
PRIMARY KEY (Id INT64 GENERATED BY DEFAULT AS IDENTITY)
-- Bom: UUID v4 distribui escritas uniformemente entre splits
PRIMARY KEY (PedidoId) -- STRING(36) com UUID
-- Alternativa: hash como prefixo para IDs sequenciais
PRIMARY KEY (MOD(FARM_FINGERPRINT(CAST(id AS STRING)), 20), id, ts)Transações
Read-write transaction
from google.cloud import spanner
client = spanner.Client(project="meu-projeto")
instance = client.instance("minha-instancia")
database = instance.database("meu-banco")
def transferir_saldo(transaction, origem_id, destino_id, valor):
# Leitura consistente dentro da transação
resultados = transaction.read(
"Contas",
columns=["ContaId", "Saldo"],
keyset=spanner.KeySet(keys=[[origem_id], [destino_id]])
)
contas = {r[0]: r[1] for r in resultados}
if contas[origem_id] < valor:
raise ValueError("Saldo insuficiente")
transaction.update("Contas", columns=["ContaId", "Saldo"], values=[
[origem_id, contas[origem_id] - valor],
[destino_id, contas[destino_id] + valor],
])
database.run_in_transaction(transferir_saldo, "conta-A", "conta-B", Decimal("100.00"))Mutations API
Mais eficiente que DML para escritas em massa (sem verificação de constraints intermediária):
with database.batch() as batch:
batch.insert(
table="Pedidos",
columns=["PedidoId", "ClienteId", "Status", "CriadoEm"],
values=[
["uuid-1", "cliente-A", "PENDENTE", spanner.COMMIT_TIMESTAMP],
["uuid-2", "cliente-B", "PENDENTE", spanner.COMMIT_TIMESTAMP],
]
)
batch.delete("CarrinhoTemp", spanner.KeySet(keys=[["uuid-1"], ["uuid-2"]]))Stale reads
Para queries que toleram dados ligeiramente desatualizados, com latência menor (não precisa contatar o líder da região):
import datetime
with database.snapshot(exact_staleness=datetime.timedelta(seconds=15)) as snapshot:
rows = snapshot.execute_sql(
"SELECT PedidoId, Status FROM Pedidos WHERE ClienteId = @cliente",
params={"cliente": "cliente-A"},
param_types={"cliente": spanner.param_types.STRING}
)
for row in rows:
print(row)DML
-- INSERT, UPDATE, DELETE padrão
INSERT INTO Pedidos (PedidoId, ClienteId, Status)
VALUES ('uuid-novo', 'cliente-X', 'RASCUNHO');
UPDATE Pedidos
SET Status = 'CONFIRMADO'
WHERE PedidoId = 'uuid-novo';
-- Partitioned DML: para operações em massa sem manter uma transação longa
-- (pode processar milhões de linhas em paralelo)
UPDATE Pedidos SET Status = 'ARQUIVADO'
WHERE CriadoEm < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 365 DAY);Change Streams
Captura mudanças nas tabelas em tempo real para CDC (Change Data Capture):
CREATE CHANGE STREAM MudancasPedidos
FOR Pedidos(Status, ClienteId, Total);Pode ser consumido pelo gcp-dataflow para propagar mudanças ao gcp-bigquery, gcp-pubsub ou outros sistemas.
Conexão via gcloud
# Executar query diretamente
gcloud spanner databases execute-sql meu-banco \
--instance=minha-instancia \
--sql="SELECT COUNT(*) FROM Pedidos WHERE Status = 'PENDENTE'"
# Spanner Studio (interface web) está disponível no console GCPMonitoramento
- Spanner Studio: interface no console para queries ad-hoc e DDL
- Query insights: planos de execução, CPU por query, queries mais lentas
- Cloud Monitoring:
spanner/instance/cpu/utilization,spanner/query/execution_count - Lock insights: identifica contenção de bloqueio em transações concorrentes
Ver também: gcp | gcp-cloud-sql | gcp-alloydb | gcp-bigquery | gcp-dataflow | gcp-boas-praticas | db-tipos-de-bancos-de-dados