Nesta página, discutimos os requisitos do esquema do Spanner, como usar o esquema para criar relações hierárquicas e os recursos do esquema. Ele também apresenta tabelas intercaladas, que podem melhorar o desempenho da consulta ao consultar tabelas em uma relação pai-filho.
Um esquema é um namespace que contém objetos de banco de dados, como tabelas, visualizações, índices e funções. Você usa esquemas para organizar objetos, aplicar privilégios de controle de acesso refinado e evitar conflitos de nomenclatura. É necessário definir um esquema para cada banco de dados no Spanner.
Também é possível segmentar e armazenar linhas na tabela de banco de dados em diferentes regiões geográficas. Para mais informações, consulte a Visão geral do geocarticionamento.
Dados com tipos bem definidos
Os dados no Spanner são fortemente tipados. Os tipos de dados incluem tipos escalares e complexos, que são descritos em Tipos de dados no GoogleSQL e Tipos de dados do PostgreSQL.
Escolher chave principal
Os bancos de dados do Spanner podem conter uma ou mais tabelas. As tabelas são estruturadas como linhas e colunas. O esquema da tabela define uma ou mais colunas como a chave primária, que identifica exclusivamente cada linha. As chaves primárias são sempre indexadas para pesquisa rápida de linhas. Para atualizar ou excluir linhas existentes em uma tabela, é preciso que ela tenha uma chave primária. Uma tabela sem colunas de chave primária pode ter apenas uma linha. Somente bancos de dados do dialeto GoogleSQL podem ter tabelas sem uma chave primária.
Muitas vezes, o aplicativo já tem um campo que é uma escolha natural para uso como chave primária. Por exemplo, em uma tabela Customers, pode haver um CustomerId fornecido pelo aplicativo que funciona como a chave primária. Em outros casos, pode ser necessário gerar uma chave primária ao inserir a linha. Normalmente, esse valor é um número inteiro exclusivo sem significado comercial (uma chave primária substituta).
Em todos os casos, tenha cuidado para não criar pontos de acesso com a escolha da chave primária. Por exemplo, se você inserir registros com um número inteiro monotônico crescente como a chave, a inserção sempre ocorrerá no final do espaço da chave. Isso não é desejável, porque o Spanner divide os dados entre servidores por intervalos de chaves, o que significa que as inserções serão direcionadas para um único servidor, criando um ponto de acesso. Há técnicas que podem espalhar a carga em vários servidores e evitar pontos de acesso:
- Gere a chave e armazene-a em uma coluna. Use a coluna hash (ou a coluna hash com as colunas de chave única) como a chave primária.
- Troque a ordem das colunas na chave primária.
- Use um identificador universal exclusivo (UUID). Recomendamos a versão 4 do UUID porque ela usa valores aleatórios nos bits de ordem superior. Não use um algoritmo UUID (como a versão 1 do UUID) que armazene o carimbo de data/hora nos bits de ordem superior.
- Faça reversão em bits dos valores sequenciais.
Relacionamentos de tabelas pai e filho
Há duas maneiras de definir relações pai-filho no Spanner: intercalação de tabelas e chaves externas.
A intercalação de tabelas do Spanner é uma boa opção para muitos relacionamentos pai-filho. Com o entrelaçamento, o Spanner coloca fisicamente as linhas filhas com as linhas mãe no armazenamento. A colocalização pode melhorar significativamente o desempenho. Por exemplo, se você tiver uma tabela Customers e uma Invoices, e seu aplicativo frequentemente busca todas as faturas de um cliente, defina Invoices como uma tabela filha intercalada de Customers. Ao fazer isso, você declara uma relação de localidade de dados entre
duas tabelas independentes. Você está instruindo o Spanner a armazenar uma ou mais linhas de Invoices com uma linha Customers. Essa relação entre
pai e filho é aplicada quando intercalada com a cláusula
INTERLEAVE IN PARENT. As tabelas filhas INTERLEAVE IN compartilham as mesmas características de intercalação física de linhas, mas o Spanner não impõe a integridade referencial entre pai e filho.
Para associar uma tabela filha a uma tabela mãe, use a DDL que declara a tabela filha como intercalada na mãe e inclua a chave primária da tabela mãe como a primeira parte da chave primária composta da tabela filha.
Para mais informações sobre intercalação, consulte Criar tabelas intercaladas.
As chaves externas são uma solução pai-filho mais geral e abordam casos de uso adicionais. Elas não estão limitadas a colunas de chave primária, e as tabelas podem ter várias relações de chave externa, como pai em alguns relacionamentos e filho em outros. No entanto, uma relação de chave externa não sugere a co-localização das tabelas na camada de armazenamento.
O Google recomenda que você escolha representar relações pai-filho como tabelas intercaladas ou chaves externas, mas não ambas. Para mais informações sobre chaves externas e a comparação delas com tabelas intercaladas, consulte Visão geral de chaves externas.
Chaves primárias em tabelas intercaladas
Para intercalação, todas as tabelas precisam ter uma chave primária. Se você declarar uma tabela como filha intercalada de outra, ela precisará ter uma chave primária composta que inclua todos os componentes da chave primária da mãe, na mesma ordem e, geralmente, uma ou mais colunas adicionais da tabela filha.
O Spanner armazena linhas classificadas em ordem por valores de chave primária. As linhas filhas são inseridas entre linhas mães. Consulte uma ilustração de linhas intercaladas em Criar tabelas intercaladas mais adiante nesta página.
Resumidamente, o Spanner pode colocalizar fisicamente linhas de tabelas relacionadas. Os exemplos de esquema mostram o aspecto desse layout físico.
Divisões de banco de dados
É possível definir hierarquias de relações pai-filho intercaladas com até sete camadas de profundidade, o que significa que você pode colocalizar linhas de sete tabelas independentes. Se o tamanho dos dados nas tabelas for pequeno, um único servidor do Spanner provavelmente poderá processar seu banco de dados. Mas o que acontece quando as tabelas relacionadas crescem e começam a atingir os limites de recursos de um servidor individual? O Spanner é um banco de dados distribuído, o que significa que, conforme seu banco de dados cresce, o Spanner divide seus dados em partes chamadas de "divisões". As divisões podem ser movimentadas independentemente entre si e atribuídas a servidores diferentes, que podem estar em locais físicos distintos. Uma divisão contém um intervalo de linhas contíguas. As chaves de início e término desse intervalo são chamadas de "limites de divisão". O Spanner adiciona e remove automaticamente limites de divisão com base no tamanho e na carga, o que altera o número de divisões no banco de dados.
Divisão baseada em carga
Como um exemplo de como o Spanner executa a divisão baseada em carga para reduzir os pontos de acesso de leitura, imagine que seu banco de dados contém uma tabela com 10 linhas que são lidas com mais frequência do que todas as outras linhas na tabela. O Spanner pode adicionar limites de divisão entre cada uma dessas 10 linhas para que cada uma seja processada por um servidor diferente, em vez de permitir que todas as leituras dessas linhas consumam os recursos de um único servidor.
Como regra geral, se você seguir as práticas recomendadas para design de esquema, o Spanner poderá reduzir os gargalos para que a capacidade de leitura melhore a cada poucos minutos até que você sature os recursos na instância ou encontre casos em que não seja possível adicionar novos limites de divisão (porque você tem uma divisão que abrange apenas uma única linha sem filhos intercalados).
Esquemas nomeados
Os esquemas nomeados ajudam a organizar dados semelhantes. Isso ajuda você a encontrar objetos rapidamente no console Google Cloud , aplicar privilégios e evitar conflitos de nomenclatura.
Esquemas nomeados, como outros objetos de banco de dados, são gerenciados usando DDL.
Os esquemas nomeados do Spanner permitem usar nomes totalmente qualificados (FQNs) para consultar dados. Os FQNs permitem combinar o nome do esquema e o nome do objeto para identificar objetos do banco de dados. Por exemplo, você pode criar um esquema chamado warehouse para a unidade de negócios do depósito. As tabelas que usam esse
esquema podem incluir: product, order e customer information. Ou você pode criar um esquema chamado fulfillment para a unidade de negócios de atendimento.
Esse esquema também pode ter tabelas chamadas product, order e customer
information. No primeiro exemplo, o FQN é warehouse.product e, no segundo, é fulfillment.product. Isso evita confusão em
situações em que vários objetos compartilham o mesmo nome.
Na DDL CREATE SCHEMA, os objetos de tabela recebem um FQN, por exemplo, sales.customers, e um nome abreviado, por exemplo, sales.
Os seguintes objetos de banco de dados são compatíveis com esquemas nomeados:
TABLECREATEINTERLEAVE IN [PARENT]FOREIGN KEYSYNONYM
VIEWINDEXFOREIGN KEYSEQUENCE
Para mais informações sobre como usar esquemas nomeados, consulte Gerenciar esquemas nomeados.
Usar controle de acesso refinado com esquemas nomeados
Com os esquemas nomeados, é possível conceder acesso no nível do esquema a cada objeto nele. Isso se aplica a objetos de esquema que existem no momento em que você concede acesso. Você precisa conceder acesso aos objetos adicionados depois.
O controle de acesso refinado limita o acesso a grupos inteiros de objetos de banco de dados, como tabelas, colunas e linhas na tabela.
Para mais informações, consulte Conceder privilégios de controle de acesso minucioso a esquemas nomeados.
Exemplos de esquema
Os exemplos de esquema nesta seção mostram como criar tabelas mãe e filha com e sem intercalação e ilustram os layouts físicos correspondentes de dados.
Criar uma tabela mãe
Imagine que você esteja criando um aplicativo de música e precise de uma tabela que armazene linhas de dados do cantor:
Observe que a tabela contém uma coluna de chave primária, SingerId, que aparece à esquerda da linha em negrito, e que as tabelas são organizadas por linhas e colunas.
É possível definir a tabela com a seguinte DDL:
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), );
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA );
Observe o seguinte sobre o esquema do exemplo:
Singersé uma tabela na raiz da hierarquia do banco de dados (porque não é definida como filha intercalada de outra tabela).- Para bancos de dados do dialeto GoogleSQL, as colunas de chave primária geralmente são anotadas com
NOT NULL, mas é possível omitir essa anotação se você quiser permitir valoresNULLem colunas-chave. Para mais informações, consulte Colunas de chave. - As colunas que não estão incluídas na chave primária são chamadas de colunas não chave e podem ter uma anotação
NOT NULLopcional. - As colunas que usam o tipo
STRINGouBYTESno GoogleSQL precisam ser definidas com um comprimento, que representa o número máximo de caracteres Unicode que podem ser armazenados no campo. A especificação de comprimento é opcional para os tiposvarcharecharacter varyingdo PostgreSQL. Para mais informações, consulte Tipos de dados escalares para bancos de dados do dialeto GoogleSQL e Tipos de dados do PostgreSQL para bancos de dados do dialeto PostgreSQL.
O que parece o layout físico das linhas na tabela Singers? O diagrama a seguir mostra as linhas da tabela Singers armazenadas por chave primária ("Singers(1)" e "Singers(2)", em que o número entre parênteses é o valor da chave primária.
O diagrama acima ilustra um exemplo de limite de divisão entre as linhas com chave Singers(3) e Singers(4), com os dados das divisões resultantes atribuídas a diferentes servidores. Conforme essa tabela cresce, é possível que linhas de dados de
Singers sejam armazenadas em locais diferentes.
Criar tabelas mãe e filha
Imagine que agora você queira adicionar alguns dados básicos sobre os álbuns de cada cantor ao aplicativo de música.
A chave primária de Albums é composta por duas colunas: SingerId e AlbumId, para associar cada álbum à sua cantora. O exemplo de esquema a seguir define as tabelas Albums e Singers na raiz da hierarquia do banco de dados, o que torna essas tabelas irmãs.
-- Schema hierarchy: -- + Singers (sibling table of Albums) -- + Albums (sibling table of Singers)
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ); CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId);
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) );
O layout físico das linhas de Singers e Albums é semelhante ao diagrama a seguir, com linhas da tabela Albums armazenadas por chave primária contígua, em seguida, linhas de Singers armazenadas por chave primária contígua:
Uma observação importante sobre o esquema: o Spanner não pressupõe relações de localidade de dados entre as tabelas Singers e Albums, porque são tabelas de nível superior. À medida que o banco de dados cresce, o Spanner pode adicionar limites de divisão entre qualquer uma das linhas. Isso significa que as linhas da tabela Albums podem ter uma divisão diferente das linhas da tabela Singers, e as duas divisões podem se mover de forma independente.
Dependendo das necessidades do seu aplicativo, convém permitir que os dados Albums sejam localizados em divisões diferentes dos dados Singers. No entanto, isso pode gerar uma penalidade de desempenho devido à necessidade de coordenar leituras e atualizações em recursos distintos. Se o aplicativo precisar recuperar informações sobre todos os álbuns de um determinado cantor com frequência, crie Albums como uma tabela filha intercalada de Singers, que posiciona as linhas das duas tabelas ao longo da dimensão da chave primária. O próximo exemplo explica isso com mais detalhes.
Criar tabelas intercaladas
Uma tabela intercalada é uma tabela que você declara como filha intercalada de outra porque quer que as linhas da tabela filha sejam armazenadas fisicamente com a linha mãe associada. Como mencionado anteriormente, a chave primária da tabela mãe precisa ser a primeira parte da chave primária composta da tabela filha.
Depois de intercalar uma tabela, ela se torna permanente. Não é possível desfazer o entrelaçamento. Em vez disso, crie a tabela novamente e migre os dados para ela.
Ao projetar seu aplicativo de música, suponha que você perceba que ele precisa acessar com frequência linhas da tabela Albums ao acessar uma linha Singers. Por exemplo, ao acessar a linha Singers(1), também é necessário acessar as linhas Albums(1, 1) e Albums(1, 2). Nesse caso, Singers e Albums precisam ter uma forte relação de localidade de dados. É possível declarar essa relação de localidade de dados criando Albums como uma tabela filha intercalada de Singers.
-- Schema hierarchy: -- + Singers -- + Albums (interleaved table, child table of Singers)
A linha em negrito no esquema a seguir mostra como criar Albums como uma tabela intercalada de Singers.
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ); 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;
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) ) INTERLEAVE IN PARENT singers ON DELETE CASCADE;
Observações sobre este esquema:
SingerId, que é a primeira parte da chave primária da tabela filhaAlbums, também é a chave primária da tabela mãeSingers.- A anotação
ON DELETE CASCADEsignifica que, quando uma linha da tabela mãe é excluída, as respectivas linhas filhas também são excluídas automaticamente. Se uma tabela filha não tiver essa anotação ou se a anotação forON DELETE NO ACTION, exclua as linhas filhas antes de excluir a linha mãe. - As linhas intercaladas são ordenadas primeiro por linhas da tabela mãe e, em seguida, por linhas contíguas da tabela filha que compartilham a chave primária da tabela mãe. Por exemplo, "Singers(1)", depois "Albums(1, 1)" e "Albums(1, 2)".
- A relação de localidade de dados de cada cantor e dados dos álbuns é preservada se esse banco de dados se dividir, desde que o tamanho de uma linha
Singerse todas as linhasAlbumsfiquem abaixo do limite de tamanho da divisão e não haja ponto de acesso em nenhuma dessas linhasAlbums. - A linha pai já precisa existir para que seja possível inserir linhas filho. A linha mãe pode já existir no banco de dados ou pode ser inserida antes da inserção das linhas filhas na mesma transação.
Suponha que você queira modelar Projects e Resources como tabelas intercaladas. Alguns cenários podem se beneficiar do INTERLEAVE IN, que permite que a linha Projects não precise existir para que as entidades abaixo dela existam. Por exemplo, um projeto foi excluído, mas os recursos precisam ser limpos antes da exclusão.
GoogleSQL
CREATE TABLE Projects ( ProjectId INT64 NOT NULL, ProjectName STRING(1024), ) PRIMARY KEY (ProjectId); CREATE TABLE Resources ( ProjectId INT64 NOT NULL, ResourceId INT64 NOT NULL, ResourceName STRING(1024), ) PRIMARY KEY (ProjectId, ResourceId), INTERLEAVE IN Projects;
PostgreSQL
CREATE TABLE Projects ( ProjectId BIGINT PRIMARY KEY, ProjectName VARCHAR(1024), ); CREATE TABLE Resources ( ProjectId BIGINT, ResourceId BIGINT, ResourceName VARCHAR(1024), PRIMARY KEY (ProjectId, ResourceId) ) INTERLEAVE IN Projects;
Neste exemplo, usamos a cláusula INTERLEAVE IN Projects em vez de INTERLEAVE IN PARENT Projects. Isso indica que não aplicamos a relação
pai-filho entre projetos e recursos.
Neste exemplo, as linhas Resources(1, 10) e Resources(1, 20) podem existir no banco de dados mesmo que a linha Projects(1) não exista. Projects(1) pode ser excluído mesmo que Resources(1, 10) e Resources(1, 20) ainda existam, e a exclusão não afeta essas linhas Resources.
Criar uma hierarquia de tabelas intercaladas
A relação mãe e filha entre Singers e Albums pode ser estendido para mais tabelas descendentes. Por exemplo, é possível criar uma tabela intercalada chamada Songs como filha de Albums para armazenar a lista de faixas de cada álbum:
Songs precisa ter uma chave primária que inclua todas as chaves primárias das tabelas que estão em um nível mais alto na hierarquia, ou seja, SingerId e AlbumId.
-- Schema hierarchy: -- + Singers -- + Albums (interleaved table, child table of Singers) -- + Songs (interleaved table, child table of Albums)
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ); 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; CREATE TABLE Songs ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, TrackId INT64 NOT NULL, SongName STRING(MAX), ) PRIMARY KEY (SingerId, AlbumId, TrackId), INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
PostgreSQL
CREATE TABLE singers ( singer_id BIGINT PRIMARY KEY, first_name VARCHAR(1024), last_name VARCHAR(1024), singer_info BYTEA ); CREATE TABLE albums ( singer_id BIGINT, album_id BIGINT, album_title VARCHAR, PRIMARY KEY (singer_id, album_id) ) INTERLEAVE IN PARENT singers ON DELETE CASCADE; CREATE TABLE songs ( singer_id BIGINT, album_id BIGINT, track_id BIGINT, song_name VARCHAR, PRIMARY KEY (singer_id, album_id, track_id) ) INTERLEAVE IN PARENT albums ON DELETE CASCADE;
O diagrama a seguir representa uma visualização física de linhas intercaladas.
Neste exemplo, conforme o número de cantores cresce, o Spanner adiciona limites de divisão entre os cantores para preservar a localidade dos dados entre um cantor e seus dados de álbum e música. No entanto, se o tamanho de uma linha de cantor e as linhas filhas excederem o limite de tamanho da divisão ou se um ponto de acesso for detectado nas linhas filhas, o Spanner tentará adicionar limites de divisão para isolar essa linha de ponto de acesso junto com todas as linhas filhas abaixo dela.
Em resumo, uma tabela mãe e todas as tabelas filhas e descendentes formam uma hierarquia de tabelas no esquema. Ainda que cada tabela na hierarquia seja logicamente independente, a intercalação física delas pode melhorar o desempenho, efetivamente juntando previamente as tabelas e permitindo que você acesse linhas relacionadas e minimize os acessos ao armazenamento.
Junções com tabelas intercaladas
Se possível, vincule dados em tabelas intercaladas por chave principal. Como cada linha intercalada geralmente é armazenada fisicamente na mesma divisão que a linha mãe, o Spanner pode executar junções por chave primária localmente, minimizando o acesso ao armazenamento e o tráfego de rede. No exemplo a seguir, Singers e Albums são unidos na chave primária SingerId.
GoogleSQL
SELECT s.FirstName, a.AlbumTitle FROM Singers AS s JOIN Albums AS a ON s.SingerId = a.SingerId;
PostgreSQL
SELECT s.first_name, a.album_title FROM singers AS s JOIN albums AS a ON s.singer_id = a.singer_id;
Grupos de localidades
O Spanner usa grupos de localidade para preservar as relações de localidade de dados em colunas de tabelas. Se você não criar explicitamente nenhum grupo de localidade para suas tabelas, o Spanner agrupará todas as colunas no grupo de localidade default e armazenará os dados de todas as tabelas no armazenamento SSD. Você
pode usar grupos de localidades para fazer o seguinte:
Use o armazenamento em camadas. O armazenamento em camadas é um recurso de armazenamento totalmente gerenciado que permite escolher se os dados serão armazenados em unidades de estado sólido (SSD) ou unidades de disco rígido (HDD). Por padrão, sem usar o armazenamento em camadas, o Spanner armazena todos os dados em armazenamento SSD.
Use o agrupamento de colunas para armazenar as colunas especificadas separadamente das outras. Como os dados das colunas especificadas são armazenados separadamente, a leitura deles é mais rápida do que se todos os dados estivessem agrupados. Para usar o agrupamento de colunas, crie um grupo de localidade sem especificar opções de armazenamento em camadas. O Spanner usa grupos de localidade para armazenar as colunas especificadas separadamente. Se especificado, as colunas herdam a política de armazenamento em camadas da tabela ou do grupo de localidade padrão. Em seguida, use a instrução DDL
CREATE TABLEpara definir um grupo de localidade para as colunas especificadas ou use a instrução DDLALTER TABLEpara alterar o grupo de localidade usado por uma coluna de tabela. A instrução DDL determina as colunas armazenadas no grupo de localidade. Por fim, você pode ler dados nessas colunas de maneira mais eficiente.
Colunas principais
Esta seção inclui algumas observações sobre colunas importantes.
Mudar chaves de tabela
As chaves de uma tabela não podem mudar. Não é possível adicionar ou remover uma coluna de chave a uma tabela existente.
Armazenar NULLs em uma chave primária
No GoogleSQL, se quiser armazenar NULL em uma coluna de chave primária,
omita a cláusula NOT NULL dessa coluna no esquema. (Bancos de dados com dialeto PostgreSQL não aceitam valores NULL em uma coluna de chave primária.)
Veja um exemplo de como omitir a cláusula NOT NULL na coluna da chave principal SingerId. Como SingerId é a chave primária, pode haver apenas uma linha que armazena NULL nessa coluna.
CREATE TABLE Singers ( SingerId INT64 PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), );
A propriedade anulável da coluna da chave primária precisa coincidir entre as instruções da tabela mãe e as da tabela filha. Neste exemplo, NOT NULL para a coluna Albums.SingerId não é permitido porque Singers.SingerId o omite.
CREATE TABLE Singers ( SingerId INT64 PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), ); 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;
Tipos não permitidos
As seguintes colunas não podem ser do tipo ARRAY:
- colunas de chave de uma tabela
- colunas de chave de um índice
Design para multilocação
Caso estejam sendo armazenados dados pertencentes a diferentes clientes, talvez seja interessante implementar a multilocação. Por exemplo, um serviço de música pode querer armazenar o conteúdo de cada gravadora separadamente.
Multilocação clássica
A maneira clássica de desenvolver um design para multilocação é criar um banco de dados separado para cada cliente. Neste exemplo, cada banco de dados tem sua própria tabela Singers:
| SingerId | FirstName | LastName |
|---|---|---|
| 1 | Marc | Richards |
| 2 | Catalina | Smith |
| SingerId | FirstName | LastName |
|---|---|---|
| 1 | Alice | Trentor |
| 2 | Gabriel | Wright |
| SingerId | FirstName | LastName |
|---|---|---|
| 1 | Benjamin | Martinez |
| 2 | Hannah | Harris |
Multilocação gerenciada por esquema
Outra maneira de projetar para multilocação no Spanner é ter todos os clientes em uma única tabela em um único banco de dados e usar um valor de chave primária diferente para cada cliente. Por exemplo, é possível incluir uma coluna de chave CustomerId nas suas tabelas. Se você tornar CustomerId a primeira coluna de chave, os dados de cada cliente terão uma boa localidade. O Spanner pode usar divisões de banco de dados para maximizar o desempenho com base no tamanho dos dados e nos padrões de carga. No exemplo a seguir, há uma única tabela Singers para todos os clientes:
| CustomerId | SingerId | FirstName | LastName |
|---|---|---|---|
| 1 | 1 | Marc | Richards |
| 1 | 2 | Catalina | Smith |
| 2 | 1 | Alice | Trentor |
| 2 | 2 | Gabriel | Wright |
| 3 | 1 | Benjamin | Martinez |
| 3 | 2 | Hannah | Harris |
Se forem necessários bancos de dados separados para cada locatário, é preciso estar ciente das seguintes restrições:
- Há limites para a quantidade de bancos de dados por instância e de tabelas e índices por banco de dados. Dependendo do número de clientes, pode não ser possível ter bancos de dados ou tabelas separados.
- Adicionar novas tabelas e índices não intercalados pode levar muito tempo. Pode não ser possível alcançar o desempenho que você quer se o design do esquema depender da adição de novas tabelas e índices.
Se for preciso criar bancos de dados separados, pode ser mais interessante distribuir suas tabelas entre eles de maneira que cada banco tenha um número baixo de alterações de esquema por semana.
Se você criar tabelas e índices separados para cada cliente do seu aplicativo, não coloque todas as tabelas e índices no mesmo banco de dados. Em vez disso, divida-os entre vários bancos de dados para reduzir os problemas de desempenho com a criação de um grande número de índices.
Para saber mais sobre outros padrões de gerenciamento de dados e design de aplicativos para multilocação, consulte Como implementar a multilocação no Spanner