Questa pagina illustra i requisiti dello schema Spanner, come utilizzare lo schema per creare relazioni gerarchiche e le funzionalità dello schema. Introduce anche le tabelle interleaved, che possono migliorare le prestazioni delle query quando vengono eseguite query sulle tabelle in una relazione principale-secondario.
Uno schema è uno spazio dei nomi che contiene oggetti di database, come tabelle, viste, indici e funzioni. Utilizzi gli schemi per organizzare gli oggetti, applicare privilegi di controllo dell'accesso granulare ed evitare conflitti di denominazione. Devi definire uno schema per ogni database in Spanner.
Puoi anche segmentare e archiviare ulteriormente le righe nella tabella del database in diverse regioni geografiche. Per saperne di più, consulta la panoramica del partizionamento geografico.
Dati fortemente tipizzati
I dati in Spanner sono fortemente tipizzati. I tipi di dati includono tipi scalari e complessi, descritti in Tipi di dati in GoogleSQL e Tipi di dati PostgreSQL.
Scegli una chiave principale
I database Spanner possono contenere una o più tabelle. Le tabelle sono strutturate come righe e colonne. Lo schema della tabella definisce una o più colonne della tabella come chiave primaria della tabella, che identifica in modo univoco ogni riga. Le chiavi primarie vengono sempre indicizzate per una rapida ricerca delle righe. Se vuoi aggiornare o eliminare righe esistenti in una tabella, la tabella deve avere una chiave primaria. Una tabella senza colonne di chiave primaria può avere una sola riga. Solo i database con dialetto GoogleSQL possono avere tabelle senza chiave primaria.
Spesso la tua applicazione ha già un campo che si adatta naturalmente all'utilizzo come
chiave primaria. Ad esempio, per una tabella Customers, potrebbe esistere un CustomerId fornito dall'applicazione che funge da chiave primaria. In altri
casi, potrebbe essere necessario generare una chiave primaria durante l'inserimento della riga. In genere, si tratta di un valore intero univoco senza significato commerciale (una chiave primaria surrogata).
In tutti i casi, devi fare attenzione a non creare hotspot con la scelta della chiave primaria. Ad esempio, se inserisci record con un numero intero in aumento monotono come chiave, li inserirai sempre alla fine dello spazio delle chiavi. Ciò non è auspicabile perché Spanner divide i dati tra i server in base agli intervalli di chiavi, il che significa che gli inserimenti verranno indirizzati a un singolo server, creando un hotspot. Esistono tecniche che possono distribuire il carico su più server ed evitare hotspot:
- Esegui l'hashing della chiave e archiviala in una colonna. Utilizza la colonna hash (o la colonna hash insieme alle colonne delle chiavi univoche) come chiave primaria.
- Inverti l'ordine delle colonne nella chiave primaria.
- Utilizza un UUID (Universally Unique Identifier). Si consiglia l'UUID versione 4, perché utilizza valori casuali nei bit di ordine superiore. Non utilizzare un algoritmo UUID (ad esempio UUID versione 1) che memorizza il timestamp nei bit di ordine superiore.
- Inversione dei bit dei valori sequenziali.
Relazioni tra tabelle padre-figlio
Esistono due modi per definire le relazioni padre-figlio in Spanner: interfoliazione delle tabelle e chiavi esterne.
L'interleaving delle tabelle di Spanner è una buona scelta per molte
relazioni padre-figlio. Con l'interleaving, Spanner colloca fisicamente
le righe secondarie con le righe principali nello spazio di archiviazione. La co-location può migliorare
significativamente le prestazioni. Ad esempio, se hai una tabella Customers e una tabella
Invoices e la tua applicazione recupera spesso tutte le fatture per un
cliente, puoi definire Invoices come tabella secondaria interleaved di
Customers. In questo modo dichiari una relazione di località dei dati tra due tabelle indipendenti. Stai dicendo a Spanner
di archiviare una o più righe di Invoices con una riga Customers. Questa
relazione principale-secondaria viene applicata quando è intervallata dalla
clausola INTERLEAVE IN PARENT. Le tabelle secondarie INTERLEAVE IN condividono le stesse caratteristiche di interleaving fisico delle righe, ma Spanner non applica l'integrità referenziale tra la tabella principale e quella secondaria.
Per associare una tabella figlio a una tabella padre, utilizza DDL che dichiara la tabella figlio come interleaved nella tabella padre e includi la chiave primaria della tabella padre come prima parte della chiave primaria composita della tabella figlio.
Per saperne di più sull'interleaving, consulta Creare tabelle con interleaving.
Le chiavi esterne sono una soluzione padre-figlio più generica e sono adatte a ulteriori casi d'uso. Non sono limitate alle colonne di chiave primaria e le tabelle possono avere più relazioni di chiave esterna, sia come padre in alcune relazioni sia come figlio in altre. Tuttavia, una relazione di chiave esterna non implica la co-location delle tabelle nel livello di archiviazione.
Google consiglia di scegliere di rappresentare le relazioni padre-figlio come tabelle con interleaving o come chiavi esterne, ma non entrambe. Per ulteriori informazioni sulle chiavi esterne e sul loro confronto con le tabelle con interleaving, consulta la panoramica delle chiavi esterne.
Chiavi primarie nelle tabelle con interfoliazione
Per l'interleaving, ogni tabella deve avere una chiave primaria. Se dichiari una tabella come figlio interleaved di un'altra tabella, la tabella deve avere una chiave primaria composita che includa tutti i componenti della chiave primaria del padre, nello stesso ordine e, in genere, una o più colonne aggiuntive della tabella figlio.
Spanner archivia le righe in ordine ordinato in base ai valori della chiave primaria, con le righe secondarie inserite tra le righe principali. Vedi un'illustrazione delle righe con interfoliazione nella sezione Crea tabelle con interfoliazione più avanti in questa pagina.
In sintesi, Spanner può collocare fisicamente le righe di tabelle correlate. Gli esempi di schema mostrano l'aspetto di questo layout fisico.
Suddivisioni di database
Puoi definire gerarchie di relazioni padre-figlio con interleaving fino a sette livelli di profondità, il che significa che puoi collocare insieme le righe di sette tabelle indipendenti. Se le dimensioni dei dati nelle tabelle sono ridotte, un singolo server Spanner può probabilmente gestire il database. Ma cosa succede quando le tabelle correlate aumentano e iniziano a raggiungere i limiti delle risorse di un singolo server? Spanner è un database distribuito, il che significa che man mano che il database cresce, Spanner divide i dati in blocchi chiamati "split". Le singole suddivisioni possono spostarsi indipendentemente l'una dall'altra e essere assegnate a server diversi, che possono trovarsi in posizioni fisiche diverse. Una divisione contiene un intervallo di righe contigue. Le chiavi iniziale e finale di questo intervallo sono chiamate "confini della suddivisionee". Spanner aggiunge e rimuove automaticamente i confini della suddivisione in base alle dimensioni e al carico, il che modifica il numero di suddivisioni nel database.
Divisione basata sul carico
Come esempio di come Spanner esegue la suddivisione basata sul carico per mitigare gli hotspot di lettura, supponiamo che il tuo database contenga una tabella con 10 righe che vengono lette più frequentemente di tutte le altre righe della tabella. Spanner può aggiungere confini della suddivisione tra ciascuna di queste 10 righe in modo che ognuna venga gestita da un server diverso, anziché consentire a tutte le letture di queste righe di consumare le risorse di un singolo server.
Come regola generale, se segui le best practice per la progettazione dello schema, Spanner può mitigare gli hotspot in modo che il throughput di lettura migliori ogni pochi minuti finché non saturi le risorse nella tua istanza o non ti imbatti in casi in cui non è possibile aggiungere nuovi confini della suddivisione (perché hai una suddivisione che copre una sola riga senza figli intercalati).
Schemi denominati
Gli schemi denominati ti aiutano a organizzare i dati simili. In questo modo, puoi trovare rapidamente gli oggetti nella console Google Cloud , applicare i privilegi ed evitare conflitti di denominazione.
Gli schemi denominati, come altri oggetti di database, vengono gestiti utilizzando DDL.
Gli schemi denominati di Spanner ti consentono di utilizzare nomi completi
(FQN) per eseguire query sui dati. I nomi di dominio completi consentono di combinare il nome dello schema e il nome dell'oggetto per identificare gli oggetti del database. Ad esempio, potresti creare uno schema
chiamato warehouse per l'unità aziendale magazzino. Le tabelle che utilizzano questo schema potrebbero includere: product, order e customer information. In alternativa, puoi creare uno schema chiamato fulfillment per l'unità aziendale di evasione.
Questo schema potrebbe anche avere tabelle denominate product, order e customer
information. Nel primo esempio, l'FQDN è warehouse.product e nel secondo esempio è fulfillment.product. In questo modo si evita confusione
in situazioni in cui più oggetti condividono lo stesso nome.
Nel DDL CREATE SCHEMA, agli oggetti tabella vengono assegnati sia un nome completo, ad esempio
sales.customers, sia un nome breve, ad esempio sales.
I seguenti oggetti di database supportano gli schemi denominati:
TABLECREATEINTERLEAVE IN [PARENT]FOREIGN KEYSYNONYM
VIEWINDEXFOREIGN KEYSEQUENCE
Per saperne di più sull'utilizzo degli schemi denominati, consulta Gestire gli schemi denominati.
Utilizzare controllo dell'accesso granulare con schemi denominati
Gli schemi denominati ti consentono di concedere l'accesso a livello di schema a ogni oggetto dello schema. Ciò vale per gli oggetti dello schema esistenti al momento della concessione dell'accesso. Devi concedere l'accesso agli oggetti aggiunti in un secondo momento.
Controllo dell'accesso granulare limita l'accesso a interi gruppi di oggetti di database, ad esempio tabelle, colonne e righe della tabella.
Per saperne di più, vedi Concedere privilegi di controllo dell'accesso granulare a schemi denominati.
Esempi di schema
Gli esempi di schema in questa sezione mostrano come creare tabelle padre e figlio con e senza interleaving e illustrano i layout fisici corrispondenti dei dati.
Creare una tabella principale
Supponiamo che tu stia creando un'applicazione musicale e che ti serva una tabella che memorizzi righe di dati sui cantanti:
Tieni presente che la tabella contiene una colonna della chiave primaria, SingerId, che viene visualizzata
a sinistra della riga in grassetto e che le tabelle sono organizzate per righe e
colonne.
Puoi definire la tabella con il seguente 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 );
Tieni presente quanto segue in merito allo schema di esempio:
Singersè una tabella alla radice della gerarchia del database (perché non è definita come tabella secondaria con interfoliazione di un'altra tabella).- Per i database con dialetto GoogleSQL, le colonne della chiave primaria sono in genere annotate con
NOT NULL(anche se puoi omettere questa annotazione se vuoi consentire valoriNULLnelle colonne chiave. Per ulteriori informazioni, vedi Colonne chiave. - Le colonne non incluse nella chiave primaria sono chiamate colonne non chiave
e possono avere un'annotazione
NOT NULLfacoltativa. - Le colonne che utilizzano il tipo
STRINGoBYTESin GoogleSQL devono essere definite con una lunghezza, che rappresenta il numero massimo di caratteri Unicode che possono essere memorizzati nel campo. La specifica della lunghezza è facoltativa per i tipivarcharecharacter varyingdi PostgreSQL. Per saperne di più, consulta Tipi di dati scalari per i database con dialetto GoogleSQL e Tipi di dati PostgreSQL per i database con dialetto PostgreSQL.
Che aspetto ha il layout fisico delle righe nella tabella Singers? Il
diagramma seguente mostra le righe della tabella Singers memorizzate per chiave primaria
("Singers(1)" e poi "Singers(2)", dove il numero tra parentesi è
il valore della chiave primaria.
Il diagramma precedente illustra un esempio di limite di suddivisione tra le righe
chiave Singers(3) e Singers(4), con i dati delle suddivisioni risultanti
assegnati a server diversi. Man mano che questa tabella cresce, è possibile che le righe di dati Singers vengano archiviate in posizioni diverse.
Creare tabelle padre e figlio
Supponiamo che ora tu voglia aggiungere alcuni dati di base sugli album di ogni cantante all'applicazione musicale.
Tieni presente che la chiave primaria di Albums è composta da due colonne: SingerId e
AlbumId, per associare ogni album al suo cantante. Lo schema di esempio seguente
definisce le tabelle Albums e Singers alla radice della gerarchia del database, il che le rende tabelle di pari livello.
-- 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) );
Il layout fisico delle righe di Singers e Albums è simile a quello del
seguente diagramma, con le righe della tabella Albums archiviate per chiave primaria
contigua, poi le righe di Singers archiviate per chiave primaria contigua:
Una nota importante sullo schema è che Spanner non presuppone
relazioni di località dei dati tra le tabelle Singers e Albums, perché
sono tabelle di primo livello. Man mano che il database cresce, Spanner può aggiungere confini della suddivisione tra le righe. Ciò significa che le righe della tabella Albums
potrebbero finire in una suddivisione diversa da quelle della tabella Singers
e le due suddivisioni potrebbero spostarsi in modo indipendente l'una dall'altra.
A seconda delle esigenze della tua applicazione, potrebbe essere opportuno consentire che i dati Albums
si trovino in suddivisioni diverse rispetto ai dati Singers. Tuttavia, ciò potrebbe comportare
una penalità di rendimento a causa della necessità di coordinare letture e aggiornamenti tra
risorse distinte. Se la tua applicazione deve recuperare spesso informazioni su tutti gli album di un determinato cantante, devi creare Albums come tabella secondaria interleaved di Singers, che colloca le righe delle due tabelle lungo la dimensione della chiave primaria. L'esempio successivo spiega questo aspetto in modo più
dettagliato.
Creare tabelle con interfoliazione
Una tabella con interfoliazione è una tabella che dichiari essere una tabella secondaria con interfoliazione di un'altra tabella perché vuoi che le righe della tabella secondaria vengano archiviate fisicamente con la riga principale associata. Come accennato in precedenza, la chiave primaria della tabella padre deve essere la prima parte della chiave primaria composita della tabella figlio.
Una volta intercalata una tabella, l'operazione è definitiva. Non puoi annullare l'inserimento. Devi invece creare di nuovo la tabella ed eseguire la migrazione dei dati.
Mentre progetti la tua applicazione musicale, supponi di renderti conto che l'app
deve accedere spesso alle righe della tabella Albums quando accede a una
riga Singers. Ad esempio, quando accedi alla riga Singers(1), devi accedere anche alle righe Albums(1, 1) e Albums(1, 2). In questo caso, Singers
e Albums devono avere una forte relazione di località dei dati. Puoi dichiarare questa relazione di località dei dati creando Albums come tabella secondaria interleaved di Singers.
-- Schema hierarchy: -- + Singers -- + Albums (interleaved table, child table of Singers)
La riga in grassetto nello schema seguente mostra come creare Albums come
tabella interleaved di 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;
Note su questo schema:
SingerId, che è la prima parte della chiave primaria della tabella figlioAlbums, è anche la chiave primaria della tabella padreSingers.- L'annotazione
ON DELETE CASCADEindica che quando una riga della tabella principale viene eliminata, vengono eliminate automaticamente anche le righe figlio. Se una tabella secondaria non ha questa annotazione o l'annotazione èON DELETE NO ACTION, devi eliminare le righe secondarie prima di poter eliminare la riga principale. - Le righe interleaved vengono ordinate prima in base alle righe della tabella padre, poi in base alle righe contigue della tabella figlio che condividono la chiave primaria del padre. Ad esempio, "Cantanti(1)", poi "Album(1, 1)" e infine "Album(1, 2)".
- La relazione di località dei dati di ogni cantante e dei dati dei relativi album viene mantenuta se questo database viene suddiviso, a condizione che le dimensioni di una riga
Singerse di tutte le relative righeAlbumsrimangano al di sotto del limite di dimensioni della suddivisione e che non ci siano hotspot in nessuna di queste righeAlbums. - La riga principale deve esistere prima di poter inserire le righe secondarie. La riga principale può già esistere nel database o può essere inserita prima dell'inserimento delle righe secondarie nella stessa transazione.
Supponiamo che tu voglia modellare Projects e il relativo Resources come tabelle
interleaved. Alcuni scenari potrebbero trarre vantaggio da INTERLEAVE IN, ovvero la possibilità di
non richiedere l'esistenza della riga Projects per le entità sottostanti (ad esempio, un
progetto è stato eliminato, ma le relative risorse devono essere pulite prima
dell'eliminazione).
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;
Tieni presente che in questo esempio utilizziamo la clausola INTERLEAVE IN Projects anziché INTERLEAVE IN PARENT Projects. Ciò indica che non applichiamo la relazione
padre-figlio tra progetti e risorse.
In questo esempio, le righe Resources(1, 10) e Resources(1, 20) possono esistere nel
database anche se la riga Projects(1) non esiste. Projects(1) può essere
eliminato anche se Resources(1, 10) e Resources(1, 20) esistono ancora e l'eliminazione non influisce su queste righe Resources.
Crea una gerarchia di tabelle con interfoliazione
La relazione padre-figlio tra Singers e Albums può essere estesa a
più tabelle discendenti. Ad esempio, puoi creare una tabella intercalata
chiamata Songs come elemento secondario di Albums per memorizzare l'elenco delle tracce di ogni album:
Songs deve avere una chiave primaria che includa tutte le chiavi primarie delle tabelle
che si trovano a un livello superiore della gerarchia, ovvero 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;
Il seguente diagramma rappresenta una visualizzazione fisica delle righe interleaved.
In questo esempio, man mano che il numero di cantanti aumenta, Spanner aggiunge confini della suddivisionee tra i cantanti per preservare la località dei dati tra un cantante e i dati dei suoi album e delle sue canzoni. Tuttavia, se le dimensioni di una riga del cantante e delle relative righe secondarie superano il limite di suddivisione oppure se viene rilevato un hotspot nelle righe secondarie, Spanner tenta di aggiungere confini della suddivisione per isolare la riga dell'hotspot insieme a tutte le righe secondarie sottostanti.
In sintesi, una tabella padre insieme a tutte le relative tabelle figlio e discendenti forma una gerarchia di tabelle nello schema. Sebbene ogni tabella nella gerarchia sia logicamente indipendente, l'interleaving fisico in questo modo può migliorare le prestazioni, unendo in modo efficace le tabelle e consentendoti di accedere alle righe correlate insieme, riducendo al minimo gli accessi allo spazio di archiviazione.
Join con tabelle con interfoliazione
Se possibile, unisci i dati nelle tabelle interleaved in base alla chiave primaria. Poiché ogni riga
interleaved viene in genere archiviata fisicamente nella stessa suddivisione della riga
principale, Spanner può eseguire join per chiave primaria localmente, riducendo al minimo
l'accesso allo spazio di archiviazione e il traffico di rete. Nel seguente esempio, Singers e
Albums vengono uniti nella chiave primaria 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;
Gruppi di località
Spanner utilizza i gruppi di località per preservare le relazioni di località dei dati tra le colonne delle tabelle. Se non crei esplicitamente gruppi di località per le tue tabelle, Spanner raggruppa tutte le colonne nel gruppo di località default e archivia i dati di tutte le tabelle nell'archivio SSD. Puoi utilizzare i gruppi di località per:
Utilizza l'archiviazione a livelli. L'archiviazione a livelli è una funzionalità di archiviazione completamente gestita che ti consente di scegliere se archiviare i dati su unità a stato solido (SSD) o unità disco rigido (HDD). Per impostazione predefinita, senza utilizzare l'archiviazione a livelli, Spanner archivia tutti i dati sull'archiviazione SSD.
Utilizza il raggruppamento delle colonne per archiviare le colonne specificate separatamente dalle altre. Poiché i dati per le colonne specificate vengono archiviati separatamente, la lettura dei dati da queste colonne è più veloce rispetto a quando tutti i dati sono raggruppati. Per utilizzare il raggruppamento delle colonne, devi creare un gruppo di località senza specificare opzioni di archiviazione a livelli. Spanner utilizza i gruppi di località per archiviare le colonne specificate separatamente. Se specificate, le colonne ereditano le norme di archiviazione a livelli dalla tabella o dal gruppo di località predefinito. Poi, utilizza l'istruzione DDL
CREATE TABLEper impostare un gruppo di località per le colonne specificate o utilizza l'istruzione DDLALTER TABLEper modificare il gruppo di località utilizzato dalla colonna di una tabella. L'istruzione DDL determina le colonne archiviate nel gruppo di località. Infine, puoi leggere i dati in queste colonne in modo più efficiente.
Colonne chiave
Questa sezione include alcune note sulle colonne chiave.
Modificare le chiavi della tabella
Le chiavi di una tabella non possono essere modificate; non puoi aggiungere una colonna chiave a una tabella esistente o rimuovere una colonna chiave da una tabella esistente.
Memorizzare valori NULL in una chiave primaria
In GoogleSQL, se vuoi memorizzare NULL in una colonna della chiave primaria,
ometti la clausola NOT NULL per quella colonna nello schema. (I database con dialetto PostgreSQL non
supportano i valori NULL in una colonna della chiave primaria.)
Ecco un esempio di omissione della clausola NOT NULL nella colonna della chiave primaria
SingerId. Tieni presente che, poiché SingerId è la chiave primaria, può esserci solo
una riga che memorizza NULL in quella colonna.
CREATE TABLE Singers ( SingerId INT64 PRIMARY KEY, FirstName STRING(1024), LastName STRING(1024), );
La proprietà Nullable della colonna di chiave primaria deve corrispondere tra le dichiarazioni della tabella padre e della tabella figlio. In questo esempio, NOT NULL per la colonna
Albums.SingerId non è consentito perché Singers.SingerId lo omette.
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;
Tipi non consentiti
Le seguenti colonne non possono essere di tipo ARRAY:
- Le colonne chiave di una tabella.
- Le colonne chiave di un indice.
Progettazione per il multi-tenancy
Potresti voler implementare il multi-tenancy se memorizzi dati appartenenti a clienti diversi. Ad esempio, un servizio musicale potrebbe voler archiviare separatamente i contenuti di ogni casa discografica.
Multi-tenancy classica
Il modo classico per progettare per il multitenancy è creare un database separato per
ogni cliente. In questo esempio, ogni database ha la propria tabella Singers:
| SingerId | Nome | Cognome |
|---|---|---|
| 1 | Marc | Richards |
| 2 | Catalina | Smith |
| SingerId | Nome | Cognome |
|---|---|---|
| 1 | Alice | Trentor |
| 2 | Gabriel | Wright |
| SingerId | Nome | Cognome |
|---|---|---|
| 1 | Benjamin | Martinez |
| 2 | Hannah | Harris |
Multi-tenancy gestita dallo schema
Un altro modo per progettare per il multi-tenancy in Spanner è avere tutti i clienti in una singola tabella in un unico database e utilizzare un valore di chiave primaria diverso per ogni cliente. Ad esempio, potresti includere una colonna con la chiave CustomerId nelle tabelle. Se imposti CustomerId come prima colonna chiave, i dati di ogni cliente hanno una buona località. Spanner
può quindi utilizzare in modo efficace le divisioni del database per massimizzare
le prestazioni in base alle dimensioni dei dati e ai pattern di carico. Nell'esempio seguente,
è presente una singola tabella Singers per tutti i clienti:
| CustomerId | SingerId | Nome | Cognome |
|---|---|---|---|
| 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 devi avere database separati per ogni tenant, tieni presente i seguenti vincoli:
- Esistono limiti al numero di database per istanza e al numero di tabelle e indici per database. A seconda del numero di clienti, potrebbe non essere possibile avere database o tabelle separati.
- L'aggiunta di nuove tabelle e indici non intercalati può richiedere molto tempo. Potresti non riuscire a ottenere il rendimento che desideri se la progettazione dello schema dipende dall'aggiunta di nuove tabelle e indici.
Se vuoi creare database separati, potresti avere più successo se distribuisci le tabelle nei database in modo che ogni database abbia un numero ridotto di modifiche allo schema alla settimana.
Se crei tabelle e indici separati per ogni cliente della tua applicazione, non inserire tutte le tabelle e gli indici nello stesso database. Dividili invece in più database per mitigare i problemi di rendimento legati alla creazione di un numero elevato di indici.
Per scoprire di più su altri pattern di gestione dei dati e sulla progettazione di applicazioni per il multi-tenancy, consulta Implementazione del multi-tenancy in Spanner.