Vista geral dos esquemas

Esta página aborda os requisitos do esquema do Spanner, como usar o esquema para criar relações hierárquicas e funcionalidades do esquema. Também introduz tabelas intercaladas, que podem melhorar o desempenho das consultas quando consulta tabelas numa relação principal-secundário.

Um esquema é um espaço de nomes que contém objetos de base de dados, como tabelas, vistas, índices e funções. Usa esquemas para organizar objetos, aplicar privilégios de controlo de acesso detalhados e evitar conflitos de nomenclatura. Tem de definir um esquema para cada base de dados no Spanner.

Também pode segmentar e armazenar ainda mais linhas na tabela da base de dados em diferentes regiões geográficas. Para mais informações, consulte a Vista geral da partição geográfica.

Dados fortemente tipados

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.

Escolha uma chave principal

As bases 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 da tabela como a chave primária da tabela, que identifica exclusivamente cada linha. As chaves primárias são sempre indexadas para uma procura rápida de linhas. Se quiser atualizar ou eliminar linhas existentes numa tabela, esta tem de ter uma chave principal. Uma tabela sem colunas de chave principal só pode ter uma linha. Apenas as bases de dados com dialeto GoogleSQL podem ter tabelas sem uma chave primária.

Muitas vezes, a sua aplicação já tem um campo que se adequa naturalmente para utilização como a chave principal. Por exemplo, para uma tabela Customers, pode existir um CustomerId fornecido pela aplicação que funcione bem como chave principal. Noutros casos, pode ter de gerar uma chave principal ao inserir a linha. Normalmente, este seria um valor inteiro único sem significado empresarial (uma chave principal substituta).

Em todos os casos, deve ter cuidado para não criar pontos críticos com a escolha da sua chave principal. Por exemplo, se inserir registos com um número inteiro monotonicamente crescente como chave, vai sempre inserir no final do seu espaço de chaves. Isto é indesejável porque o Spanner divide os dados entre servidores por intervalos de chaves, o que significa que as suas inserções são direcionadas para um único servidor, criando um ponto crítico. Existem técnicas que podem distribuir a carga por vários servidores e evitar pontos críticos:

Relações entre tabelas principal-secundário

Existem duas formas de definir relações principal-secundário no Spanner: intercalação de tabelas e chaves externas.

A intercalação de tabelas do Spanner é uma boa escolha para muitas relações principal/secundário. Com a intercalação, o Spanner coloca fisicamente as linhas secundárias com as linhas principais no armazenamento. A colocação conjunta pode melhorar significativamente o desempenho. Por exemplo, se tiver uma tabela Customers e uma tabela Invoices, e a sua aplicação obtenha frequentemente todas as faturas de um cliente, pode definir Invoices como uma tabela secundária intercalada de Customers. Ao fazê-lo, está a declarar uma relação de localidade de dados entre duas tabelas independentes. Está a indicar ao Spanner que armazene uma ou mais linhas de Invoices com uma linha de Customers. Esta relação superior-secundário é aplicada quando intercalada com a cláusula INTERLEAVE IN PARENT. As tabelas secundárias INTERLEAVE IN partilham as mesmas características de intercalação de linhas físicas, mas o Spanner não aplica a integridade referencial entre a principal e a secundária.

Associa uma tabela secundária a uma tabela principal através de DDL que declara a tabela secundária como intercalada na principal e incluindo a chave principal da tabela principal como a primeira parte da chave principal composta da tabela secundária.

Para mais informações sobre a intercalação, consulte o artigo Crie tabelas intercaladas.

As chaves externas são uma solução principal-secundária mais geral e abordam casos de utilização adicionais. Não se limitam a colunas de chave principal e as tabelas podem ter várias relações de chave externa, tanto como principal em algumas relações como secundária noutras. No entanto, uma relação de chave externa não implica a colocação conjunta das tabelas na camada de armazenamento.

A Google recomenda que opte por representar as relações principal-secundário como tabelas intercaladas ou como chaves externas, mas não ambas. Para mais informações sobre chaves estrangeiras e a respetiva comparação com tabelas intercaladas, consulte a vista geral das chaves estrangeiras.

Chaves principais em tabelas intercaladas

Para a intercalação, todas as tabelas têm de ter uma chave primária. Se declarar que uma tabela é um filho intercalado de outra tabela, a tabela tem de ter uma chave principal composta que inclua todos os componentes da chave principal do pai, pela mesma ordem e, normalmente, uma ou mais colunas da tabela filho adicionais.

O Spanner armazena linhas por ordem de classificação dos valores da chave primária, com as linhas secundárias inseridas entre as linhas principais. Veja uma ilustração das linhas intercaladas em Crie tabelas intercaladas mais adiante nesta página.

Em resumo, o Spanner pode colocar fisicamente linhas de tabelas relacionadas. Os exemplos de esquema mostram o aspeto deste esquema físico.

Divisões de bases de dados

Pode definir hierarquias de relações principais/secundárias intercaladas com uma profundidade máxima de sete camadas, o que significa que pode colocar linhas de sete tabelas independentes. Se o tamanho dos dados nas suas tabelas for pequeno, é provável que um único servidor do Spanner consiga processar a sua base de dados. Mas o que acontece quando as tabelas relacionadas aumentam e começam a atingir os limites de recursos de um servidor individual? O Spanner é uma base de dados distribuída, o que significa que, à medida que a sua base de dados cresce, o Spanner divide os dados em partes denominadas "divisões". As divisões individuais podem mover-se independentemente umas das outras e ser atribuídas a diferentes servidores, que podem estar em localizações físicas diferentes. Uma divisão contém um intervalo de linhas contíguas. As chaves de início e de fim deste intervalo são denominadas "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 na base de dados.

Divisão baseada na carga

Como exemplo de como o Spanner realiza a divisão baseada na carga para mitigar os pontos críticos de leitura, suponha que a sua base 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 seguir as práticas recomendadas para a conceção de esquemas, o Spanner pode mitigar os pontos críticos de tal forma que o débito de leitura deve melhorar a cada poucos minutos até saturar os recursos na sua instância ou encontrar casos em que não é possível adicionar novos limites de divisão (porque tem uma divisão que abrange apenas uma única linha sem filhos intercalados).

Esquemas com nome

Os esquemas com nomes ajudam a organizar dados semelhantes em conjunto. Isto ajuda a encontrar rapidamente objetos na Google Cloud consola, aplicar privilégios e evitar conflitos de nomes.

Os esquemas com nome, como outros objetos de base de dados, são geridos através de DDL.

Os esquemas com nomes do Spanner permitem-lhe usar nomes totalmente qualificados (FQNs) para consultar dados. Os FQNs permitem-lhe combinar o nome do esquema e o nome do objeto para identificar objetos da base de dados. Por exemplo, pode criar um esquema denominado warehouse para a unidade de negócio do armazém. As tabelas que usam este esquema podem incluir: product, order e customer information. Em alternativa, pode criar um esquema denominado fulfillment para a unidade de negócio de processamento. Este esquema também pode ter tabelas denominadas product, order e customer information. No primeiro exemplo, o FQN é warehouse.product e, no segundo exemplo, o FQN é fulfillment.product. Isto evita confusões em situações em que vários objetos partilham o mesmo nome.

No 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 base de dados suportam esquemas com nome:

  • TABLE
    • CREATE
    • INTERLEAVE IN [PARENT]
    • FOREIGN KEY
    • SYNONYM
  • VIEW
  • INDEX
  • FOREIGN KEY
  • SEQUENCE

Para mais informações sobre a utilização de esquemas com nome, consulte o artigo Faça a gestão de esquemas com nome.

Use o controlo de acesso detalhado com esquemas com nome

Os esquemas com nome permitem-lhe conceder acesso ao nível do esquema a cada objeto no esquema. Isto aplica-se a objetos de esquema que existam no momento em que concede acesso. Tem de conceder acesso a objetos adicionados mais tarde.

O controlo de acesso detalhado limita o acesso a grupos inteiros de objetos da base de dados, como tabelas, colunas e linhas na tabela.

Para mais informações, consulte o artigo Conceda privilégios de controlo de acesso detalhado a esquemas denominados.

Exemplos de esquemas

Os exemplos de esquemas nesta secção mostram como criar tabelas principais e secundárias com e sem intercalação, e ilustram os esquemas físicos correspondentes dos dados.

Crie uma tabela principal

Suponhamos que está a criar uma aplicação de música e precisa de uma tabela que armazene linhas de dados de cantores:

Tabela de cantores com cinco linhas e quatro colunas

Tenha em atenção que a tabela contém uma coluna de chave principal, SingerId, que aparece à esquerda da linha a negrito, e que as tabelas estão organizadas por linhas e colunas.

Pode definir a tabela com o seguinte LDD:

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

Tenha em atenção o seguinte acerca do esquema de exemplo:

  • Singers é uma tabela na raiz da hierarquia da base de dados (porque não está definida como um filho intercalado de outra tabela).
  • Para bases de dados com dialeto GoogleSQL, as colunas de chave primária são normalmente anotadas com NOT NULL (embora possa omitir esta anotação se quiser permitir valores NULL nas colunas de chave. Para mais informações, consulte Colunas principais).
  • As colunas que não estão incluídas na chave principal são denominadas colunas não principais e podem ter uma anotação NOT NULL opcional.
  • As colunas que usam o tipo STRING ou BYTES no GoogleSQL têm de ser definidas com um comprimento, que representa o número máximo de carateres Unicode que podem ser armazenados no campo. A especificação de comprimento é opcional para os tipos varchar e character varying do PostgreSQL. Para mais informações, consulte Tipos de dados escalares para bases de dados de dialeto GoogleSQL e Tipos de dados PostgreSQL para bases de dados de dialeto PostgreSQL.

Qual é o aspeto da disposição física das linhas na tabela Singers? O diagrama seguinte mostra linhas da tabela Singers armazenadas pela chave primária ("Singers(1)" e, em seguida, "Singers(2)", em que o número entre parênteses é o valor da chave primária).

Exemplos de linhas de uma tabela armazenada por ordem da chave principal

O diagrama anterior ilustra um exemplo de limite de divisão entre as linhas identificadas por Singers(3) e Singers(4), com os dados das divisões resultantes atribuídos a servidores diferentes. À medida que esta tabela cresce, é possível que as linhas de dados Singers sejam armazenadas em localizações diferentes.

Crie tabelas principais e secundárias

Suponha que agora quer adicionar alguns dados básicos sobre os álbuns de cada cantor à aplicação de música.

Tabela de álbuns com cinco linhas e três colunas

Tenha em atenção que a chave primária de Albums é composta por duas colunas: SingerId e AlbumId, para associar cada álbum ao respetivo cantor. O esquema de exemplo seguinte define as tabelas Albums e Singers na raiz da hierarquia da base de dados, o que as torna 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)
);

A disposição física das linhas de Singers e Albums tem o seguinte aspeto, com linhas da tabela Albums armazenadas por chave primária contígua e, em seguida, linhas de Singers armazenadas por chave primária contígua:

Esquema físico das linhas

Uma nota importante sobre o esquema é que o Spanner não assume relações de localidade de dados entre as tabelas Singers e Albums, porque são tabelas de nível superior. À medida que a base de dados cresce, o Spanner pode adicionar limites de divisão entre qualquer uma das linhas. Isto significa que as linhas da tabela Albums podem acabar numa divisão diferente das linhas da tabela Singers e as duas divisões podem mover-se independentemente uma da outra.

Consoante as necessidades da sua aplicação, pode ser aceitável permitir que os dados do Albums estejam localizados em divisões diferentes dos dados do Singers. No entanto, isto pode incorrer numa penalização do desempenho devido à necessidade de coordenar leituras e atualizações em recursos distintos. Se a sua aplicação precisar frequentemente de obter informações sobre todos os álbuns de um determinado cantor, deve criar Albums como uma tabela secundária intercalada de Singers, que coloca linhas das duas tabelas ao longo da dimensão da chave primária. O exemplo seguinte explica isto com mais detalhe.

Crie tabelas intercaladas

Uma tabela intercalada é uma tabela que declara ser um elemento secundário intercalado de outra tabela porque quer que as linhas da tabela secundária sejam armazenadas fisicamente com a linha principal associada. Como mencionado anteriormente, a chave principal da tabela principal tem de ser a primeira parte da chave principal composta da tabela secundária.

Depois de intercalar uma tabela, a ação é permanente. Não é possível anular a intercalação. Em alternativa, tem de criar novamente a tabela e migrar os dados para a mesma.

À medida que cria a sua aplicação de música, suponha que se apercebe de que a app precisa de aceder frequentemente a linhas da tabela Albums quando acede a uma linha Singers. Por exemplo, quando acede à linha Singers(1), também tem de aceder às linhas Albums(1, 1) e Albums(1, 2). Neste caso, Singers e Albums têm de ter uma forte relação de localidade dos dados. Pode declarar esta 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 a negrito no esquema seguinte 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;

Notas sobre este esquema:

  • SingerId, que é a primeira parte da chave principal da tabela secundária Albums, também é a chave principal da respetiva tabela principal Singers.
  • A anotação ON DELETE CASCADE significa que, quando uma linha da tabela principal é eliminada, as respetivas linhas secundárias também são eliminadas automaticamente. Se uma tabela secundária não tiver esta anotação ou a anotação for ON DELETE NO ACTION, tem de eliminar as linhas secundárias antes de poder eliminar a linha principal.
  • As linhas intercaladas são ordenadas primeiro pelas linhas da tabela principal e, em seguida, pelas linhas contíguas da tabela secundária que partilham a chave principal da tabela principal. Por exemplo, "Cantores(1)", depois "Álbuns(1, 1)" e, em seguida, "Álbuns(1, 2)".
  • A relação de localidade dos dados de cada cantor e os respetivos dados de álbuns são preservados se esta base de dados for dividida, desde que o tamanho de uma linha Singers e todas as respetivas linhas Albums permaneçam abaixo do limite de tamanho da divisão e que não exista nenhum ponto crítico em nenhuma destas linhas Albums.
  • A linha principal tem de existir antes de poder inserir linhas secundárias. A linha principal pode já existir na base de dados ou pode ser inserida antes da inserção das linhas secundárias na mesma transação.

As linhas de álbuns estão intercaladas entre as linhas de cantores

Suponhamos que quer modelar Projects e o respetivo Resources como tabelas intercaladas. Determinados cenários podem beneficiar da INTERLEAVE IN, ou seja, a capacidade de não exigir que a linha Projects exista para que as entidades abaixo dela existam (por exemplo, um projeto foi eliminado, mas os respetivos recursos têm de ser limpos antes da eliminaçã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;

Tenha em atenção que, neste exemplo, usamos a cláusula INTERLEAVE IN Projects em vez de INTERLEAVE IN PARENT Projects. Isto indica que não aplicamos a relação principal-secundário entre projetos e recursos.

Neste exemplo, as linhas Resources(1, 10) e Resources(1, 20) podem existir na base de dados, mesmo que a linha Projects(1) não exista. Projects(1) pode ser eliminado mesmo que Resources(1, 10) e Resources(1, 20) ainda existam, e a eliminação não afeta estas linhas Resources.

Crie uma hierarquia de tabelas intercaladas

A relação principal-secundário entre Singers e Albums pode ser expandida para mais tabelas descendentes. Por exemplo, pode criar uma tabela intercalada denominada Songs como filha de Albums para armazenar a lista de faixas de cada álbum:

Tabela de músicas com seis linhas e quatro colunas

Songs tem de ter uma chave principal que inclua todas as chaves principais das tabelas que estão a um nível superior 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 seguinte representa uma vista física das linhas intercaladas.

As músicas estão intercaladas nos álbuns, que estão intercalados entre os cantores

Neste exemplo, à medida que o número de cantores aumenta, o Spanner adiciona limites de divisão entre os cantores para preservar a localidade dos dados entre um cantor e os dados do respetivo álbum e música. No entanto, se o tamanho de uma linha principal e das respetivas linhas secundárias exceder o limite de tamanho da divisão ou for detetado um ponto crítico nas linhas secundárias, o Spanner tenta adicionar limites de divisão para isolar essa linha principal, juntamente com todas as linhas secundárias abaixo da mesma.

Em resumo, uma tabela principal, juntamente com todas as respetivas tabelas secundárias e descendentes, forma uma hierarquia de tabelas no esquema. Embora cada tabela na hierarquia seja logicamente independente, a intercalação física desta forma pode melhorar o desempenho, juntando as tabelas de forma eficaz e permitindo-lhe aceder a linhas relacionadas em conjunto, ao mesmo tempo que minimiza os acessos ao armazenamento.

Junções com tabelas intercaladas

Se possível, junte dados em tabelas intercaladas pela chave primária. Uma vez que cada linha intercalada é normalmente armazenada fisicamente na mesma divisão que a respetiva linha principal, o Spanner pode realizar junções pela chave primária localmente, minimizando o acesso ao armazenamento e o tráfego de rede. No exemplo seguinte, Singers e Albums são unidos na chave principal 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 nas colunas das tabelas. Se não criar explicitamente grupos de localidades para as suas tabelas, o Spanner agrupa todas as colunas no grupo de localidades default e armazena os dados de todas as tabelas no armazenamento SSD. Pode usar grupos de localidades para fazer o seguinte:

  • Use o armazenamento hierárquico. O armazenamento em camadas é uma funcionalidade de armazenamento totalmente gerida que lhe permite escolher se quer armazenar os seus dados em unidades de estado sólido (SSD) ou unidades de discos rígidos (HDD). Por predefinição, sem usar o armazenamento hierárquico, o Spanner armazena todos os dados no armazenamento SSD.

  • Use o agrupamento de colunas para armazenar colunas especificadas separadamente de outras colunas. Uma vez que os dados das colunas especificadas são armazenados separadamente, a leitura dos dados dessas colunas é mais rápida do que se todos os dados estivessem agrupados. Para usar o agrupamento de colunas, tem de criar um grupo de localidades sem especificar nenhuma opção de armazenamento hierárquico. O Spanner usa grupos de localidades para armazenar as colunas especificadas separadamente. Se especificado, as colunas herdam a respetiva política de armazenamento em camadas do grupo de localidades predefinido ou da tabela. Em seguida, use a CREATE TABLE declaração DDL para definir um grupo de localidades para as colunas especificadas ou use a ALTER TABLE declaração DDL para alterar o grupo de localidades usado pela coluna de uma tabela. A declaração DDL determina as colunas que são armazenadas no grupo de localidade. Por último, pode ler dados nestas colunas de forma mais eficiente.

Colunas principais

Esta secção inclui algumas notas sobre as colunas principais.

Altere as chaves da tabela

As chaves de uma tabela não podem ser alteradas. Não pode adicionar uma coluna de chave a uma tabela existente nem remover uma coluna de chave de uma tabela existente.

Armazene valores NULL numa chave principal

No GoogleSQL, se quiser armazenar NULL numa coluna de chave primária, omita a cláusula NOT NULL para essa coluna no esquema. (As bases de dados com dialeto PostgreSQL não suportam valores NULL numa coluna de chave primária.)

Segue-se um exemplo da omissão da cláusula NOT NULL na coluna de chave principalSingerId. Tenha em atenção que, uma vez que SingerId é a chave principal, só pode existir uma linha que armazene NULL nessa coluna.

CREATE TABLE Singers (
  SingerId   INT64 PRIMARY KEY,
  FirstName  STRING(1024),
  LastName   STRING(1024),
);

A propriedade anulável da coluna de chave primária tem de corresponder entre as declarações da tabela principal e da tabela secundária. 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:

  • As colunas principais de uma tabela.
  • As colunas de chave de um índice.

Crie em função da multi-posse

Pode querer implementar a multi-tenancy se estiver a armazenar dados pertencentes a diferentes clientes. Por exemplo, um serviço de música pode querer armazenar o conteúdo de cada editora discográfica individualmente.

Multi-inquilino clássico

A forma clássica de criar designs para multi-tenancy é criar uma base de dados separada para cada cliente. Neste exemplo, cada base de dados tem a sua própria tabela Singers:

Database 1: Ackworth Records
SingerId FirstName LastName
1MarcRichards
2CatalinaSmith
Base de dados 2: Cama Records
SingerId FirstName LastName
1AliceTrentor
2GabrielWright
Base de dados 3: Eagan Records
SingerId FirstName LastName
1BenjaminMartinez
2HannahHarris

Multitenancy gerida por esquemas

Outra forma de criar um design para a multi-posse no Spanner é ter todos os clientes numa única tabela numa única base de dados e usar um valor de chave primária diferente para cada cliente. Por exemplo, pode incluir uma coluna de CustomerIdchave nas suas tabelas. Se tornar CustomerId a primeira coluna de chaves, os dados de cada cliente têm uma boa localidade. Em seguida, o Spanner pode usar eficazmente as divisões da base de dados para maximizar o desempenho com base no tamanho dos dados e nos padrões de carregamento. No exemplo seguinte, existe uma única tabela Singers para todos os clientes:

Base de dados multi-inquilino do Spanner
CustomerId SingerId FirstName LastName
11MarcRichards
12CatalinaSmith
21AliceTrentor
22GabrielWright
31BenjaminMartinez
32HannahHarris

Se tiver de ter bases de dados separadas para cada inquilino, existem restrições a ter em atenção:

  • Existem limites quanto ao número de bases de dados por instância e ao número de tabelas e índices por base de dados. Consoante o número de clientes, pode não ser possível ter bases de dados ou tabelas separadas.
  • A adição de novas tabelas e índices não intercalados pode demorar muito tempo. Pode não conseguir o desempenho pretendido se a estrutura do esquema depender da adição de novas tabelas e índices.

Se quiser criar bases de dados separadas, pode ter mais sucesso se distribuir as tabelas pelas bases de dados de forma que cada base de dados tenha um número reduzido de alterações ao esquema por semana.

Se criar tabelas e índices separados para cada cliente da sua aplicação, não coloque todas as tabelas e índices na mesma base de dados. Em alternativa, divida-os em várias bases de dados para mitigar os problemas de desempenho com a criação de um grande número de índices.

Para saber mais acerca de outros padrões de gestão de dados e design de aplicações para a multi-posse, consulte o artigo Implementar a multi-posse no Spanner