Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Best practice di progettazione dello schema

Questa pagina contiene informazioni sulla progettazione dello schema di Cloud Bigtable. Prima di leggere questa pagina, dovresti conoscere la panoramica di Bigtable. In questa pagina vengono trattati i seguenti argomenti:

  • Concetti generali: concetti di base da tenere a mente durante la progettazione dello schema.
  • Best practice: linee guida per la progettazione applicabili alla maggior parte dei casi d'uso, suddivise in base al componente della tabella.
  • Casi d'uso speciali: consigli per alcuni casi d'uso e modelli di dati specifici.

Concetti generali

La progettazione di uno schema Bigtable è diversa dalla progettazione di uno schema per un database relazionale. In Bigtable, uno schema è un progetto o un modello di tabella, inclusa la struttura dei seguenti componenti:

  • Chiavi riga
  • Famiglie di colonne, incluse le relative norme sulla garbage collection
  • Colonne

I seguenti concetti generali si applicano alla progettazione degli schemi Bigtable:

  • Bigtable è un archivio chiave/valore, non un negozio relazionale. Non supporta le unioni e le transazioni sono supportate solo all'interno di una singola riga.
  • Ogni tabella ha un solo indice, la chiave di riga. Non sono presenti indici secondari. Ogni chiave riga deve essere univoca.
  • Le righe sono ordinate letteralmente in base alla chiave di riga, dalla stringa di byte più bassa a quella con il valore più alto. Le chiavi di riga sono ordinate in ordine di byte big-endian, a volte chiamato anche byte di rete, l'equivalente binario dell'ordine alfabetico.
  • Le famiglie di colonne non vengono memorizzate in un ordine specifico.
  • Le colonne sono raggruppate per famiglia di colonne e ordinate in ordine alfabetico all'interno della famiglia di colonne. Ad esempio, in una famiglia di colonne chiamata SysMonitor con qualificatori di colonna ProcessName, User, %CPU, ID, Memory, DiskRead e Priority, Bigtable archivia le colonne in questo ordine:
SysMonitor
%CPU Lettura disco ID Memoria Priorità NomeProcesso Utente
  • L'intersezione di una riga e di una colonna può contenere più celle con timestamp. Ogni cella contiene una versione univoca con timestamp dei dati per quella riga e quella colonna.
  • Tutte le operazioni sono atomiche a livello di riga. Un'operazione interessa una riga intera o nessuna.
  • Idealmente, sia le operazioni di lettura che di scrittura devono essere distribuite uniformemente nello spazio delle righe di una tabella.

  • I tavoli delle tabelle comuni sono sparsi. Una colonna non occupa spazio in una riga che non la utilizza.

Best practice

Uno schema efficace offre prestazioni e scalabilità eccellenti, mentre uno schema progettato in modo errato può generare un sistema con prestazioni insoddisfacenti. Ogni caso d'uso è diverso e richiede un design personalizzato, ma le seguenti best practice si applicano alla maggior parte dei casi d'uso. Le eccezioni sono indicate.

Partendo dal livello di tabella fino al livello di chiave di riga, sono descritte le seguenti sezioni che descrivono le best practice per la progettazione dello schema:

Tutti gli elementi della tabella, in particolare le chiavi di riga, devono essere progettati tenendo in considerazione le richieste di lettura pianificate. Controlla quote e limiti per conoscere i limiti di dimensioni consigliati e rigidi per tutti gli elementi della tabella.

Poiché tutte le tabelle in un'istanza sono archiviate sugli stessi tablet, il progetto di uno schema che genera hotspot in una tabella può influire sulla latenza di altre tabelle nella stessa istanza.

Tabelle

Archiviare set di dati con schemi simili nella stessa tabella, anziché in tabelle separate.

In altri sistemi di database, potresti scegliere di archiviare i dati in più tabelle in base all'oggetto e al numero di colonne. In Bigtable, tuttavia, è generalmente meglio archiviare tutti i dati in un'unica tabella. Puoi assegnare un prefisso chiave riga univoco da utilizzare per ogni set di dati, in modo che Bigtable memorizzi i dati correlati in un intervallo contiguo di righe su cui puoi eseguire query per prefisso chiave di riga.

Bigtable ha un limite di 1000 tabelle per istanza, ma di solito dovresti avere molte meno tabelle. La creazione di molte piccole tabelle è un anti-pattern Bigtable per diversi motivi:

  • L'invio di richieste a molte tabelle diverse può aumentare l'overhead della connessione backend, con conseguente maggiore latenza.
  • L'utilizzo di più tabelle di dimensioni diverse può interrompere il bilanciamento del carico dietro le quinte, che migliora il funzionamento di Bigtable.

Puoi giustamente utilizzare una tabella separata per un caso d'uso diverso che richiede uno schema diverso, ma non utilizzare tabelle separate per dati simili. Ad esempio, non devi creare una nuova tabella perché è un nuovo anno o perché hai un nuovo cliente.

Famiglie di colonne

Inserisci le colonne correlate nella stessa famiglia di colonne. Quando una riga contiene più valori che sono correlati tra loro, è buona norma raggruppare le colonne che contengono quei valori nella stessa famiglia di colonne. Raggruppa i dati il più vicino possibile per evitare di dover creare filtri complessi e ottenere solo le informazioni di cui hai bisogno, ma non di più, nelle tue richieste di lettura più frequenti.

Crea fino a circa 100 famiglie di colonne per tabella. La creazione di più di 100 famiglie di colonne può causare un peggioramento delle prestazioni.

Scegli nomi brevi ma significativi per le famiglie di colonne. I nomi sono inclusi nei dati trasferiti per ogni richiesta.

Inserisci colonne che hanno esigenze di conservazione dei dati diverse in famiglie di colonne diverse. Questa pratica è importante se vuoi limitare i costi di archiviazione. I criteri di garbage collection sono impostati a livello di famiglia di colonne, non a livello di colonna. Ad esempio, se devi solo conservare la versione più recente di un dato dato, non archiviarla in una famiglia di colonne in cui è impostata l'archiviazione di 1000 versioni di altro. In caso contrario, stai archiviando 999 celle di dati di cui non hai bisogno.

Colonne

Considera i qualificatori di colonna come dati. Poiché devi archiviare un qualificatore di colonna per ogni colonna, puoi risparmiare spazio assegnando un nome alla colonna. Consideriamo, ad esempio, una tabella in cui sono memorizzati i dati sulle amicizie, in cui ogni riga rappresenta una persona e tutte le sue amicizie. Ogni qualificatore di colonna può essere l'ID di un amico. Il valore di ogni colonna della riga può quindi corrispondere al cerchio social in cui si trova l'amico. In questo esempio, le righe potrebbero avere il seguente aspetto:

Jose Fred:libro-club Gabriel:lavoro Hiroshi:tennis
Sofia Hiroshi:lavoro Seo Yoon:scuola Jakob:club scacchi

Confronta questo schema con uno schema per gli stessi dati che non utilizzano i qualificatori di colonna come dati:

Giuseppe 1 Amico:Fred Cerchio:club-libri
Giuseppe n. 2 Amico:Gabriel Cerchio:lavoro
Giuseppe 3 Amico:Hiroshi Cerchio:tennis
Sofia#1 Amico:Hiroshi Cerchio:lavoro
Sofia#2 Amico:Seo Yoon Cerchia:scuola
Sofia# Amico:Giacomo Cerchio:club di scacchi

La seconda progettazione dello schema fa sì che la tabella cresca molto più velocemente.

Se utilizzi i qualificatori di colonna per archiviare i dati, assegna ai qualificatori di colonna nomi brevi ma significativi. Questo approccio ti consente di ridurre la quantità di dati trasferiti per ogni richiesta. La dimensione massima è 16 kB.

Crea tutte le colonne che vuoi nella tabella. Le tabelle Bigtable sono sparse e non ci sono penalità per lo spazio di una colonna che non viene utilizzata in una riga. In una tabella possono essere presenti milioni di colonne, a condizione che nessuna riga superi il limite massimo di 256 MB per riga.

Evita di utilizzare troppe colonne in una sola riga. Anche se una tabella può avere milioni di colonne, una riga non dovrebbe. Alcuni fattori contribuiscono a questa best practice:

  • L'elaborazione di ogni cella di riga richiede tempo.
  • Ogni cella aggiunge un sovraccarico alla quantità di dati archiviati nella tabella e inviati attraverso la rete. Ad esempio, se stai archiviando 1 KB (1024 byte) di dati, è molto più efficiente in termini di spazio di archiviazione tali dati in una singola cella, anziché distribuire i dati su 1024 celle che contengono ciascuno 1 byte.

Se il set di dati richiede logicamente più colonne per riga di quanto Bigtable possa elaborare in modo efficiente, valuta la possibilità di archiviare i dati come protobuf in una singola colonna.

Righe

Non archiviare più di 100 MB di dati in una singola riga. Le righe che superano questo limite possono comportare una riduzione delle prestazioni di lettura.

Conserva tutte le informazioni per un'entità in una singola riga. Per la maggior parte dei casi d'uso, evita di archiviare dati che devi leggere in modo atomico, o tutti contemporaneamente, in più di una riga per evitare incoerenze. Ad esempio, se aggiorni due righe in una tabella, è possibile che una riga venga aggiornata correttamente e che l'altro non venga eseguito correttamente. Assicurati che lo schema non richieda l'aggiornamento di più di una riga contemporaneamente per garantire l'accuratezza dei dati correlati. Ciò garantisce che, se una parte di una richiesta di scrittura non va a buon fine o deve essere inviata di nuovo, tale dato non è temporaneamente incompleto.

Eccezione: se mantieni un'entità in una singola riga in righe che contengono centinaia di MB, dovresti suddividere i dati su più righe.

Archiviare le entità correlate in righe adiacenti per rendere le letture più efficienti.

Celle

Non archiviare più di 10 MB di dati in una singola cella. Ricorda che una cella corrisponde ai dati archiviati per una determinata riga e colonna con un timestamp univoco e che più celle possono essere memorizzate all'intersezione di quella riga e colonna. Il numero di celle conservate in una colonna è regolato dal criterio di garbage collection impostato per la famiglia di colonne che la contiene.

Chiavi riga

Progetta la chiave di riga in base alle query che utilizzerai per recuperare i dati. Le chiavi di riga ben progettate offrono le migliori prestazioni in Bigtable. Le query Bigtable più efficienti recuperano i dati utilizzando uno dei seguenti elementi:

  • Chiave di riga
  • Prefisso chiave riga
  • Intervallo di righe definito dalle chiavi di riga iniziale e finale

Altri tipi di query attivano una scansione completa della tabella, una modalità molto meno efficace. Scegliendo la chiave riga corretta adesso, puoi evitare un complesso processo di migrazione dei dati in un secondo momento.

Usare le chiavi di riga in modo breve. Le chiavi di riga non devono superare i 4 kB. Le chiavi di riga lunga occupano spazio di archiviazione e memoria aggiuntivi e aumentano il tempo necessario per ricevere risposte dal server Bigtable.

Archivia più valori delimitati in ogni chiave a riga. Poiché il modo migliore per eseguire query su Bigtable in modo efficiente è tramite la chiave di riga, è spesso utile includere più identificatori nella chiave di riga. Quando la chiave di riga include più valori, è molto importante comprendere chiaramente come vengono utilizzati i dati.

I segmenti di chiave di riga sono generalmente separati da un delimitatore, ad esempio i due punti, una barra o un simbolo di hash. Il primo segmento o insieme di segmenti contigui è il prefisso della chiave di riga, mentre l'ultimo segmento o l'insieme di segmenti contigui è il suffisso della chiave di riga.

Chiave di riga di esempio

I prefissi chiave chiave ben pianificati ti consentono di sfruttare l'ordinamento integrato di Bigtable per archiviare i dati correlati in righe contigue. L'archiviazione di dati correlati in righe contigue consente di accedere ai dati correlati come un intervallo di righe, anziché eseguire scansioni di tabelle inefficienti.

Se i dati includono numeri interi che vuoi archiviare o ordinare in modo numerico, inserisci un numero intero con zeri iniziali. Bigtable archivia i dati lessicograficamente. Ad esempio, lexicograficamente, 3 > 20 ma 20 > 03. L'inserimento di tre con uno zero iniziale assicura che i numeri siano ordinati numericamente. Questa tattica è importante per i timestamp in cui vengono utilizzate query basate su intervalli.

È importante creare una chiave di riga che consenta di recuperare un intervallo di righe ben definito. In caso contrario, la query richiede una scansione della tabella, che è molto più lenta del recupero di righe specifiche.

Ad esempio, se la tua applicazione monitora i dati dei dispositivi mobili, puoi avere una chiave di riga composta dal tipo di dispositivo, dall'ID dispositivo e dal giorno in cui i dati vengono registrati. Le chiavi di riga per questi dati potrebbero avere il seguente aspetto:

        phone#4c410523#20200501
        phone#4c410523#20200502
        tablet#a0b81f74#20200501
        tablet#a0b81f74#20200502

La progettazione delle chiavi di riga consente di recuperare i dati con una singola richiesta per:

  • Un tipo di dispositivo
  • Una combinazione di tipo di dispositivo e ID dispositivo

Il design della chiave di riga non è ottimale se vuoi recuperare tutti i dati per un determinato giorno. Poiché il giorno è archiviato nel terzo segmento, o il suffisso chiave riga, non puoi richiedere un intervallo di righe basato sul suffisso o su un segmento centrale della chiave riga. Devi invece inviare una richiesta di lettura con un filtro che scansiona l'intera tabella cercando il valore del giorno.

Se possibile, utilizza valori di stringa leggibili nelle chiavi di riga. Questa procedura semplifica l'utilizzo dello strumento Key Visualizer per risolvere i problemi con Bigtable.

Spesso dovresti progettare chiavi di riga che iniziano con un valore comune e terminano con un valore granulare. Ad esempio, se la chiave riga include un continente, un paese e una città, puoi creare chiavi a riga simili alla seguente per ordinare automaticamente in base ai valori con una cardinalità inferiore:

        asia#india#bangalore
        asia#india#mumbai
        asia#japan#okinawa
        asia#japan#sapporo
        southamerica#bolivia#cochabamba
        southamerica#bolivia#lapaz
        southamerica#chile#santiago
        southamerica#chile#temuco

Chiavi riga per evitare

Alcuni tipi di chiavi di riga possono ostacolare l'esecuzione di query sui dati, mentre altre generano prestazioni scarse. Questa sezione descrive alcuni tipi di chiavi di riga da evitare in Bigtable.

Chiavi delle righe che iniziano con un timestamp. Questo pattern causa il push delle scritture sequenziali su un singolo nodo, creando un hotspot. Se inserisci un timestamp in una chiave di riga, anteponi un valore ad alta cardinalità come ID utente per evitare hotspot.

Chiavi delle righe che impediscono il raggruppamento dei dati correlati. Evita chiavi di riga che causano l'archiviazione di dati correlati in intervalli di righe non contigue, che sono inefficienti da leggere insieme.

ID numerici sequenziali. Supponiamo che il tuo sistema assegni un ID numerico a ogni utente della tua applicazione. Potresti essere tentato di utilizzare un ID numerico dell'utente come chiave di riga per la tabella. Tuttavia, poiché è più probabile che i nuovi utenti siano utenti attivi, è probabile che questo approccio indirizzi la maggior parte del traffico a un numero limitato di nodi.

Un approccio più sicuro consiste nell'utilizzare una versione invertita dell'ID numerico dell'utente, che distribuisce il traffico in modo più uniforme su tutti i nodi per la tabella Bigtable.

Identificatori aggiornati di frequente. Evita di utilizzare una singola chiave di riga per identificare un valore che deve essere aggiornato di frequente. Ad esempio, se memorizzi i dati sull'utilizzo della memoria per un numero di dispositivi una volta al secondo, non utilizzare una sola chiave riga per ogni dispositivo composto dall'ID dispositivo e dalla metrica che viene archiviata, come 4c410523#memusage, e aggiornare la riga ripetutamente. Questo tipo di operazione sovraccarica il tablet che memorizza la riga utilizzata di frequente. Le righe possono anche superare il limite di dimensioni, perché i valori precedenti di una colonna occupano spazio fino a quando le celle non vengono rimosse durante la garbage collection.

Memorizza invece ogni nuova lettura in una nuova riga. Utilizzando l'esempio di utilizzo della memoria, ogni chiave di riga può contenere l'ID dispositivo, il tipo di metrica e un timestamp, quindi le chiavi di riga sono simili a 4c410523#memusage#1423523569918. Questa strategia è efficiente perché in Bigtable la creazione di una nuova riga richiede meno tempo rispetto alla creazione di una nuova cella. Inoltre, questa strategia ti consente di leggere rapidamente i dati relativi a un intervallo di date specifico calcolando le chiavi di inizio e fine appropriate.

Per valori che cambiano di frequente, ad esempio un contatore che viene aggiornato centinaia di volte al minuto, è meglio mantenere i dati in memoria, a livello di applicazione, e scrivere periodicamente nuove righe in Bigtable.

Valori sottoposti ad hashing. L'hashing di una chiave riga elimina la possibilità di sfruttare l'ordinamento naturale di Bigtable, impedendo l'archiviazione delle righe in un modo ottimale per le query. Per lo stesso motivo, l'hashing rende difficile l'utilizzo dello strumento Key Visualizer per la risoluzione dei problemi con Bigtable. Utilizza valori leggibili da persone anziché valori sottoposti ad hashing.

Valori espressi come byte non elaborati anziché stringhe leggibili dalle persone. I byte non elaborati sono appropriati per i valori delle colonne, ma per leggibilità e risoluzione dei problemi utilizza i valori delle stringhe nelle chiavi di riga.

Casi d'uso speciali

Puoi avere un set di dati univoco che richiede un'attenzione particolare durante la progettazione di uno schema per archiviarlo in Bigtable. Questa sezione descrive alcuni, ma non tutti, diversi tipi di dati Bigtable e alcune tattiche suggerite per archiviarli nel modo migliore.

Dati basati sul tempo

Includi un timestamp nella tua chiave di riga se spesso recuperi i dati in base all'ora in cui sono stati registrati.

Ad esempio, l'applicazione potrebbe registrare i dati relativi alle prestazioni, come l'utilizzo di CPU e memoria, una volta al secondo per molte macchine. La chiave di riga di questi dati potrebbe combinare un identificatore per la macchina con un timestamp per i dati (ad esempio, machine_4223421#1425330757685). Tieni presente che le chiavi di riga sono ordinate letteralmente.

Non utilizzare un timestamp in sé o all'inizio di una chiave riga, perché ciò comporterebbe il push di scritture sequenziali su un singolo nodo, creando un hotspot.

Se di solito recuperi prima i record più recenti, puoi utilizzare un timestamp inverso nella chiave di riga sottraendo il timestamp dal valore massimo del linguaggio di programmazione per i numeri interi lunghi (in Java, java.lang.long.MAX_VALUE). Se un timestamp è invertito, i record saranno ordinati dal più recente al meno recente.

Per informazioni specifiche sull'utilizzo dei dati della serie temporale, consulta Progettazione dello schema per i dati della serie temporale.

Multitenancy

I prefissi chiave riga forniscono una soluzione scalabile per un "caso d'uso" multi-tenancy, uno scenario in cui archivi dati simili, utilizzando lo stesso modello dei dati, per conto di più client. L'utilizzo di una sola tabella per tutti i tenant è il modo più efficiente per archiviare e accedere ai dati multi-tenant.

Ad esempio, supponiamo che memorizzi e monitori le cronologie di acquisto per conto di molte aziende. Puoi utilizzare l'ID univoco di ogni azienda come prefisso chiave di riga. Tutti i dati per un tenant sono archiviati in righe contigue nella stessa tabella e puoi eseguire query o applicare filtri utilizzando il prefisso della chiave di riga. Quindi, quando un'azienda non è più il tuo cliente e hai bisogno di eliminare i dati della cronologia acquisti che stavi archiviando per l'azienda, puoi trasmettere l'intervallo di righe che utilizzano il prefisso della chiave di riga del cliente.

Ad esempio, se memorizzi i dati dei dispositivi mobili per i clienti altostrat e examplepetstore, puoi creare chiavi di riga come queste. Quindi, se altostrat non è più il tuo cliente, elimini tutte le righe con il prefisso della chiave di riga altostrat.

        altostrat#phone#4c410523#20190501
        altostrat#phone#4c410523#20190502
        altostrat#tablet#a0b41f74#20190501
        examplepetstore#phone#4c410523#20190502
        examplepetstore#tablet#a6b81f79#20190501
        examplepetstore#tablet#a0b81f79#20190502

Se invece archivi i dati per conto di ciascuna azienda in una propria tabella, potresti riscontrare problemi di prestazioni e scalabilità. Inoltre,è più probabile che raggiungi inavvertitamente il limite di Bigtable di 1000 tabelle per istanza. Quando un'istanza raggiunge questo limite, Bigtable impedisce la creazione di altre tabelle nell'istanza.

Privacy

A meno che il tuo caso d'uso non lo richieda, evita di utilizzare informazioni che consentono l'identificazione personale (PII) o dati utente nelle chiavi di riga o negli ID famiglia di colonne. Le chiavi di riga e le famiglie di colonne sono sia dati che metadati e le applicazioni che le utilizzano come metadati, ad esempio crittografia o logging, possono inavvertitamente esponerle a utenti che non dovrebbero avere accesso a dati privati.

Nomi di dominio

Ampia gamma di nomi di dominio

Se stai archiviando dati su entità che possono essere rappresentate come nomi di dominio, potresti utilizzare un nome di dominio inverso (ad esempio, com.company.product) come chiave di riga. L'utilizzo di un nome di dominio inverso è particolarmente utile se i dati di ogni riga tendono a sovrapporsi alle righe adiacenti. In questo caso, Bigtable può comprimere i tuoi dati in modo più efficiente.

Al contrario, i nomi di dominio standard che non vengono invertiti possono causare l'ordinamento delle righe in modo tale che i dati correlati non vengano raggruppati in un unico posto, il che può comportare una compressione meno efficiente e letture meno efficienti.

Questo approccio funziona al meglio quando i dati sono distribuiti in molti nomi di dominio inversi diversi.

Per illustrare questo punto, considera i seguenti nomi di dominio, ordinati automaticamente in ordine alfabetico da Bigtable:

      drive.google.com
      en.wikipedia.org
      maps.google.com

Non è utile per il caso d'uso in cui vuoi eseguire query su tutte le righe per google.com. Al contrario, considera le stesse righe in cui i nomi di dominio sono stati invertiti:

      com.google.drive
      com.google.maps
      org.wikipedia.en

Nel secondo esempio, le righe correlate vengono ordinate automaticamente in modo da poterle recuperare facilmente come un intervallo di righe.

Pochi nomi di dominio

Se prevedi di archiviare molti dati solo per uno o un numero limitato di nomi di dominio, prendi in considerazione altri valori per la chiave riga. In caso contrario, potresti eseguire il push di scritture su un singolo nodo nel tuo cluster, con il risultato che un hotspot potrebbe avere dimensioni eccessive.

Modifica o incertezza delle query

Se non esegui sempre le stesse query sui dati o non sai esattamente quali saranno le tue query, una soluzione sarà quella di archiviare tutti i dati per una riga in una colonna anziché in più colonne. Con questo approccio, utilizzi un formato che semplifica l'estrazione dei singoli valori in un secondo momento, ad esempio il formato binario buffer buffer o il valore json.

La chiave della riga è comunque progettata con la massima attenzione per garantire che tu possa recuperare i dati necessari, ma ogni riga ha in genere una sola colonna che contiene tutti i dati relativi alla riga in un singolo protobuf.

L'archiviazione dei dati come messaggio protobuf in una colonna invece di distribuirli in più colonne presenta vantaggi e svantaggi. I vantaggi includono:

  • I dati occupano meno spazio, quindi è meno costoso archiviarli.
  • Manterrai un certo grado di flessibilità non impegnandoti nell'utilizzo delle famiglie di colonne e dei qualificatori di colonna.
  • L'applicazione di lettura non deve sapere "quale" è lo schema della tabella.

Alcuni svantaggi sono i seguenti:

  • Devi deserializzare i messaggi protobuf dopo che sono stati letti da Bigtable.
  • Perdi l'opzione di eseguire query sui dati nei messaggi protobuf utilizzando i filtri.
  • Non puoi utilizzare BigQuery per eseguire query federate sui campi all'interno di messaggi protobuf dopo averli letti da Bigtable.

Passaggi successivi