Miglior database e design di tabelle per miliardi di righe di dati [chiuso]


74

Sto scrivendo un'applicazione che deve archiviare e analizzare grandi quantità di dati elettrici e di temperatura.

Fondamentalmente ho bisogno di memorizzare grandi quantità di misurazioni orarie dell'uso di elettricità negli ultimi anni e per molti anni a venire per decine di migliaia di posizioni e quindi analizzare i dati in un modo non molto complesso.

Le informazioni che devo conservare (per ora) sono l'ID posizione, il timestamp (data e ora), l'utilizzo della temperatura e dell'elettricità.

Circa la quantità di dati che devono essere memorizzati, questa è un'approssimazione, ma qualcosa del genere: più di
20.000 posizioni, 720 registrazioni al mese (misurazioni orarie, circa 720 ore al mese), 120 mesi (per 10 anni fa ) e molti anni nel futuro. I calcoli semplici danno i seguenti risultati:

20.000 sedi x 720 registrazioni x 120 mesi (10 anni fa) = 1 728 000 000 registrazioni .

Questi sono i record precedenti, i nuovi record verranno importati mensilmente, quindi circa 20.000 x 720 = 14 400000 nuovi record al mese .

Anche le posizioni totali cresceranno costantemente.

Su tutti questi dati, dovranno essere eseguite le seguenti operazioni:

  1. Recupera i dati per una determinata data E periodo: tutti i record per un determinato ID località tra le date 01.01.2013 e 01.01.2017 e tra le 07:00 e le 13:00.
  2. Semplici operazioni matematiche per una determinata data E intervallo di tempo, ad esempio temperatura MIN, MAX e AVG e consumo di elettricità per un determinato ID località per 5 anni tra le 07:00 e le 13:00.

I dati saranno scritti mensilmente, ma saranno letti da centinaia di utenti (almeno) costantemente, quindi la velocità di lettura è significativamente più importante.

Non ho esperienza con i database NoSQL ma da quello che ho raccolto, sono la soluzione migliore da usare qui. Ho letto i database NoSQL più popolari, ma poiché sono abbastanza diversi e consentono anche un'architettura di tabella molto diversa, non sono stato in grado di decidere quale sia il miglior database da utilizzare.

Le mie scelte principali sono state Cassandra e MongoDB, ma da quando ho una conoscenza molto limitata e nessuna esperienza reale quando si tratta di dati di grandi dimensioni e NoSQL non sono molto sicuro. Ho anche letto che PostreSQL gestisce bene anche tali quantità di dati.

Le mie domande sono le seguenti:

  1. Dovrei usare un database NoSQL per grandi quantità di dati. In caso contrario, posso attenermi a MySQL?
  2. Quale database dovrei usare?
  3. Devo conservare la data e l'ora in colonne separate, indicizzate (se possibile) per recuperare ed elaborare rapidamente i dati per determinati periodi di data e ora, oppure è possibile farlo mantenendo il timestamp in una singola colonna?
  4. Un approccio alla modellazione dei dati di serie temporali è appropriato qui e, in caso contrario, potresti darmi dei suggerimenti per un buon design della tabella?

Grazie.


29
2017. Anche se non piccolo, non si tratta in particolare di una GRANDE quantità di dati per l'hardware corretto. E odio dirtelo, ma finora ciò che hai lì suona come dati relazionali.
TomTom,

6
Ho archiviato tabelle multi-TB con decine di miliardi di righe in MS SQL Server 2008-2014 utilizzando una buona chiave (data dell'epoca), compressione, partizionamento e assicurando che le mie query / indici siano allineati alle partizioni. Ho dovuto passare a NoSQL (Hadoop) quando ho iniziato a ottenere petabyte di dati da analizzare e indicizzare in modo diverso. NoSQL dovrebbe avere altre considerazioni e in questo caso, non sembra adattarsi.
Ali Razeghi,

3
@AliRazeghi Hadoop non ha nulla a che fare con SQL o NoSQL: è solo un motore di archiviazione. Esistono molte interfacce SQL supportate da Hadoop.
Mustaccio,

3
Quali sono i tuoi vincoli riguardo a: soldi da spendere in software / licenze?
user3067860

1
Quando hai soldi infiniti, allora suggerirei di acquistare un'appliance SAP HANA. È ottimo per aggregazioni su set di dati di grandi dimensioni. Ma probabilmente non hai soldi infiniti.
Philipp,

Risposte:


90

Questo è esattamente ciò che faccio ogni giorno, tranne che invece di utilizzare i dati orari, utilizzo i dati di 5 minuti. Scarico circa 200 milioni di dischi ogni giorno, quindi la quantità di cui parli qui non è un problema. I dati di 5 minuti hanno una dimensione di circa 2 TB e ho dati meteorologici che risalgono a 50 anni a livello orario per posizione. Quindi lasciami rispondere alle tue domande in base alla mia esperienza:

  1. Non usare NoSQL per questo. I dati sono altamente strutturati e si adattano perfettamente a un database relazionale.
  2. Personalmente utilizzo SQL Server 2016 e non ho problemi ad applicare calcoli su quel volume di dati. Originariamente era su un'istanza PostgreSQL quando ho iniziato il mio lavoro e non poteva gestire il volume di dati come su una piccola istanza AWS.
  3. Avrei vivamente consiglio estrazione della porzione ora della data e memorizzazione separato da data stessa. Credimi, impara dai miei errori!
  4. Conservo la maggior parte dei dati in elenco (DATE, TIME, DATAPOINT_ID, VALUE) ma non è così che le persone vorranno interpretare i dati. Preparati per alcune domande orribili contro i dati e enormi quantità di pivot. Non abbiate paura di creare una tabella non normalizzata per set di risultati troppo grandi per essere calcolati al volo.

Suggerimento generale: memorizzo la maggior parte dei dati tra due database, il primo è costituito da dati di serie temporali e viene normalizzato. Il mio secondo database è molto de-normalizzato e contiene dati pre-aggregati. Per quanto veloce sia il mio sistema, non sono cieco al fatto che gli utenti non vogliono nemmeno aspettare 30 secondi per caricare un rapporto, anche se penso personalmente che 30 secondi per sgretolare 2 TB di dati siano estremamente veloci.

Per approfondire il motivo per cui consiglio di memorizzare l'ora separatamente dalla data, ecco alcuni motivi per cui lo faccio in questo modo:

  1. Il modo in cui i dati elettrici sono presentati è di End Ending- pertanto, 01:00 è in realtà la media della potenza elettrica per l'ora precedente e 00:00 è Ora che termina 24. (Questo è importante perché devi effettivamente cercare due date per includere il valore di 24 ore - il giorno in cui stanno cercando più il primo segno del giorno seguente.) Tuttavia, i dati meteorologici vengono effettivamente presentati in avanti (effettivi e previsti per l'ora successiva). Nella mia esperienza con questi dati, i consumatori desiderano analizzare l'effetto che il tempo ha sul potere prezzo / domanda. Se dovessi utilizzare un confronto di data semplice, in realtà confronteresti il ​​prezzo medio per l'ora precedente rispetto alla temperatura media per l'ora successiva, anche se i timestamp sono gli stessi.DATETIME colonna.
  2. Prestazione. Direi che almeno il 90% dei rapporti che ho generato sono grafici, che normalmente tracciano il prezzo rispetto all'ora per una singola data o per un intervallo di date. Dover dividere il tempo dalla data può ridurre la velocità della query utilizzata per generare il rapporto in base all'intervallo di date che si desidera visualizzare. Non è raro che i consumatori vogliano vedere una sola data, Anno su Anno negli ultimi 30 anni (in effetti per il clima questo è necessario per generare le normali di 30 anni) - questo può essere lento. Ovviamente puoi ottimizzare la tua query e aggiungere indici e, fidati di me, ho alcuni indici folli che preferirei non avere, ma rendono il sistema veloce.
  3. Produttività. Odio dover scrivere lo stesso codice più di una volta. Conservavo la data e l'ora nella stessa colonna, fino a quando non dovevo scrivere più volte la stessa query per estrarre la porzione di tempo. Dopo un po 'mi sono stancato di doverlo fare e l'ho estratto nella sua stessa colonna. Meno codice devi scrivere, meno possibilità c'è di un errore in esso. Inoltre, dover scrivere meno codice significa che è possibile ottenere i report più velocemente, nessuno vuole aspettare tutto il giorno per i report.
  4. Utenti finali. Non tutti gli utenti finali sono utenti esperti (ovvero sanno come scrivere SQL). Avere i dati già memorizzati in un formato che possono portare in Excel (o altri strumenti simili) con il minimo sforzo ti renderà un eroe in ufficio. Se gli utenti non possono accedere o manipolare facilmente i dati, non useranno il sistema. Credetemi, ho progettato il sistema perfetto un paio di anni fa e nessuno l'ha usato per questo motivo. La progettazione del database non si limita solo all'adesione a un insieme predefinito di regole / linee guida, ma a rendere fruibile il sistema.

Come ho detto sopra, tutto si basa sulla mia esperienza personale, e lascia che te lo dica, sono stati alcuni anni difficili e molte riprogettazioni per arrivare a dove sono ora. Non fare ciò che ho fatto, impara dai miei errori e assicurati di coinvolgere gli utenti finali del tuo sistema (o sviluppatori, autori di report, ecc ...) quando prendi decisioni sul tuo database.


Ho avuto buona fortuna usando solo la data di Epoca ma la tua raccomandazione è interessante per il tuo caso d'uso. Grazie per la condivisione.
Ali Razeghi,

4
Non sono d'accordo con molto di questo. Niente di tutto ciò è una vera preoccupazione per un database moderno, come dimostrato qui con numeri reali . Se gli utenti dei dati sono troppo stupidi per usare sql, allora devi crearli un'interfaccia: non devi modificare lo schema. Estrarre l'ora è una cattiva idea
Evan Carroll,

1
Com'è il tuo hardware?
kennes,

1
@kennes fisico, 16 core, 256 GB di RAM, unità disco da 100 GB, SSD locale da 500 GB con dati TempDB su di esso, SAN ibrido con cache SSD da 8 TB e 40 TB di dischi mandrino in grado di 100.000 iops / sec. L'implementazione del database utilizza ColumnStore, compressione, tabelle in memoria, partizionamento e un'istanza tabulare SSAS.
Mr.Brownstone,

1
Questo è un hardware incredibile a seconda del numero di utenti che servi. Poiché questa è una risposta pseudo-ottimizzata, penso che includere la tua tecnologia sia utile. Sono rimasto completamente scioccato nel sentire che puoi sgretolare 2 TB in 30 secondi - è incredibilmente veloce. A parte il mio giudizio personale, penso che sarebbe utile per le persone future che desiderano ottimizzare i dati delle serie storiche!
kennes,

57

Indici PostgreSQL e BRIN

Provalo tu stesso. Questo non è un problema su un laptop di 5 anni con un SSD.

EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
  SELECT
    x::int AS id,
    (x::int % 20000)::int AS locid,  -- fake location ids in the range of 1-20000
    now() AS tsin,                   -- static timestmap
    97.5::numeric(5,2) AS temp,      -- static temp
    x::int AS usage                  -- usage the same as id not sure what we want here.
  FROM generate_series(1,1728000000) -- for 1.7 billion rows
    AS gs(x);

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series gs  (cost=0.00..15.00 rows=1000 width=4) (actual time=173119.796..750391.668 rows=1728000000 loops=1)
 Planning time: 0.099 ms
 Execution time: 1343954.446 ms
(3 rows)

Quindi ci sono voluti 22 minuti per creare la tabella. In gran parte, perché il tavolo è un modesto 97GB. Quindi creiamo gli indici,

CREATE INDEX ON electrothingy USING brin (tsin);
CREATE INDEX ON electrothingy USING brin (id);    
VACUUM ANALYZE electrothingy;

Anche la creazione degli indici ha richiesto molto tempo. Tuttavia, poiché sono BRIN sono solo 2-3 MB e si archiviano facilmente in RAM. La lettura di 96 GB non è istantanea, ma non è un vero problema per il mio laptop al carico di lavoro.

Ora lo interroghiamo.

explain analyze
SELECT max(temp)
FROM electrothingy
WHERE id BETWEEN 1000000 AND 1001000;
                                                                 QUERY PLAN                                                                  
---------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5245.22..5245.23 rows=1 width=7) (actual time=42.317..42.317 rows=1 loops=1)
   ->  Bitmap Heap Scan on electrothingy  (cost=1282.17..5242.73 rows=993 width=7) (actual time=40.619..42.158 rows=1001 loops=1)
         Recheck Cond: ((id >= 1000000) AND (id <= 1001000))
         Rows Removed by Index Recheck: 16407
         Heap Blocks: lossy=128
         ->  Bitmap Index Scan on electrothingy_id_idx  (cost=0.00..1281.93 rows=993 width=0) (actual time=39.769..39.769 rows=1280 loops=1)
               Index Cond: ((id >= 1000000) AND (id <= 1001000))
 Planning time: 0.238 ms
 Execution time: 42.373 ms
(9 rows)

Aggiornamento con timestamp

Qui generiamo una tabella con diversi timestamp per soddisfare la richiesta di indicizzazione e ricerca in una colonna timestamp, la creazione richiede un po 'più di tempo perché to_timestamp(int)è sostanzialmente più lenta di now()(che viene memorizzata nella cache per la transazione)

EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
  SELECT
    x::int AS id,
    (x::int % 20000)::int AS locid,
    -- here we use to_timestamp rather than now(), we
    -- this calculates seconds since epoch using the gs(x) as the offset
    to_timestamp(x::int) AS tsin,
    97.5::numeric(5,2) AS temp,
    x::int AS usage
  FROM generate_series(1,1728000000)
    AS gs(x);

                                                               QUERY PLAN                                                                
-----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series gs  (cost=0.00..17.50 rows=1000 width=4) (actual time=176163.107..5891430.759 rows=1728000000 loops=1)
 Planning time: 0.607 ms
 Execution time: 7147449.908 ms
(3 rows)

Ora possiamo invece eseguire una query su un valore data / ora ,,

explain analyze
SELECT count(*), min(temp), max(temp)
FROM electrothingy WHERE tsin BETWEEN '1974-01-01' AND '1974-01-02';
                                                                        QUERY PLAN                                                                         
-----------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=296073.83..296073.84 rows=1 width=7) (actual time=83.243..83.243 rows=1 loops=1)
   ->  Bitmap Heap Scan on electrothingy  (cost=2460.86..295490.76 rows=77743 width=7) (actual time=41.466..59.442 rows=86401 loops=1)
         Recheck Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
         Rows Removed by Index Recheck: 18047
         Heap Blocks: lossy=768
         ->  Bitmap Index Scan on electrothingy_tsin_idx  (cost=0.00..2441.43 rows=77743 width=0) (actual time=40.217..40.217 rows=7680 loops=1)
               Index Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
 Planning time: 0.140 ms
 Execution time: 83.321 ms
(9 rows)

Risultato:

 count |  min  |  max  
-------+-------+-------
 86401 | 97.50 | 97.50
(1 row)

Quindi in 83.321 ms possiamo aggregare 86.401 record in una tabella con 1,7 miliardi di righe. Questo dovrebbe essere ragionevole.

Ora che termina

Anche il calcolo della fine dell'ora è abbastanza semplice, abbassa i timestamp e quindi aggiungi semplicemente un'ora.

SELECT date_trunc('hour', tsin) + '1 hour' AS tsin,
  count(*),
  min(temp),
  max(temp)
FROM electrothingy
WHERE tsin >= '1974-01-01'
  AND tsin < '1974-01-02'
GROUP BY date_trunc('hour', tsin)
ORDER BY 1;
          tsin          | count |  min  |  max  
------------------------+-------+-------+-------
 1974-01-01 01:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 02:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 03:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 04:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 05:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 06:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 07:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 08:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 09:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 10:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 11:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 12:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 13:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 14:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 15:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 16:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 17:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 18:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 19:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 20:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 21:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 22:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 23:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-02 00:00:00-06 |  3600 | 97.50 | 97.50
(24 rows)

Time: 116.695 ms

È importante notare che non sta usando un indice sull'aggregazione, anche se potrebbe. Se questa è la tua query in genere, probabilmente vuoi un BRIN in date_trunc('hour', tsin)esso risiede un piccolo problema in quanto date_truncnon è immutabile, quindi dovresti prima avvolgerlo per renderlo tale.

partizionamento

Un altro importante punto di informazione su PostgreSQL è che PG 10 porta il partizionamento DDL . Quindi, ad esempio, puoi creare facilmente partizioni per ogni anno. Suddividere il tuo modesto database in database minori che sono piccoli. In tal modo, dovresti essere in grado di utilizzare e mantenere gli indici btree anziché BRIN, che sarebbe ancora più veloce.

CREATE TABLE electrothingy_y2016 PARTITION OF electrothingy
    FOR VALUES FROM ('2016-01-01') TO ('2017-01-01');

O qualunque cosa.


13

Mi sorprende il fatto che nessuno qui abbia menzionato il benchmarking , fino a quando @EvanCarroll non ha avuto il suo eccellente contributo!

Se fossi in te, passerei un po 'di tempo (e sì, lo so che è un bene prezioso!) Installando sistemi, eseguendo quello che pensi sarà (ottieni l'input dell'utente finale qui!), Diciamo, le tue 10 domande più comuni.

I miei pensieri:

Le soluzioni NoSQL possono funzionare molto bene per casi d'uso particolari, ma spesso non sono flessibili per le query ad hoc. Per una divertente interpretazione di NoSQL di Brian Aker, ex capo architetto di MySQL, guarda qui !

Sono d'accordo con @ Mr.Brownstone che i tuoi dati sono perfettamente adatti a una soluzione relazionale (e questa opinione è stata confermata da Evan Carroll )!

Se dovessi impegnarmi in qualsiasi spesa, sarebbe per la mia tecnologia del disco! Spenderei tutto il denaro che avevo a disposizione su NAS o SAN o forse su alcuni dischi SSD per conservare i miei dati aggregati scritti raramente!

Per prima cosa guarderei ciò che ho disponibile ora . Esegui alcuni test e mostra i risultati ai decisori. Hai già un proxy sotto forma di lavoro di EC ! Ma un test rapido o due montati insieme sul tuo hardware sarebbero più convincenti!

Quindi pensa a spendere soldi! Se hai intenzione di spendere soldi, guarda prima l'hardware anziché il software. AFAIK, puoi noleggiare la tecnologia del disco per un periodo di prova o, meglio ancora, creare un paio di prove di concetto sul cloud.

Il mio primo approdo personale per un progetto come questo sarebbe PostgreSQL. Questo non vuol dire che escluderei una soluzione proprietaria, ma le leggi della fisica e dei dischi sono le stesse per tutti! "Yae cannae barbabietola le leggi della fisica Jim" :-)


6

Se non lo hai già fatto, dai un'occhiata a una serie temporale DBMS, poiché è ottimizzata per l'archiviazione e l'interrogazione dei dati in cui il focus principale è il tipo data / ora. In genere, i database delle serie temporali vengono utilizzati per la registrazione di dati nell'intervallo minuto / secondo / secondo secondo, quindi non sono sicuro che sia ancora appropriato per gli incrementi orari. Ciò detto, vale la pena esaminare questo tipo di DBMS. Attualmente InfluxDB sembra essere il database di serie storiche più consolidato e ampiamente utilizzato.


1
Qual è un esempio di DBMS di una serie temporale?
vescovo,

2
Dai un'occhiata qui .
Vérace,

4

Chiaramente questo non è un problema NoSQL, ma suggerirei che mentre una soluzione RDBMS funzionerebbe, penso che un approccio OLAP si adatterà molto meglio e dati gli intervalli di dati molto limitati, suggerirei vivamente di studiare l'uso di un DB basato su colonna piuttosto che uno basato su riga. Pensaci in questo modo, potresti avere 1,7 miliardi di pezzi di dati, ma hai ancora bisogno di soli 5 bit per indicizzare ogni possibile valore di ora o giorno del mese.

Ho esperienza con un dominio del problema simile in cui Sybase IQ (ora SAP IQ) viene utilizzato per archiviare fino a 300 milioni di contatori l'ora di dati sulla gestione delle prestazioni delle apparecchiature di telecomunicazione, ma dubito che abbiate il budget per quel tipo di soluzione. Nell'arena open source, MariaDB ColumnStore è un candidato molto promettente, ma consiglierei anche di indagare su MonetDB.

Poiché le prestazioni delle query sono un fattore determinante per te, prendi in considerazione il modo in cui verranno formulate le query. È qui che OLAP e RDBMS mostrano le loro maggiori differenze: - con OLAP ti normalizzi per le prestazioni delle query, non per ridurre la ripetizione, ridurre l'archiviazione o addirittura per garantire la coerenza. Quindi, oltre al timestamp originale (ti ricordi di catturare il suo fuso orario, spero?) Avere un campo separato per il timestamp UTC, altri per la data e l'ora, e ancora di più per l'anno, il mese, il giorno, l'ora, il minuto e offset UTC. Se hai ulteriori informazioni sulle posizioni, sentiti libero di conservarle in una tabella delle posizioni separata che può essere consultata su richiesta e sentiti libera di conservare la chiave di quella tabella nel tuo record principale, ma mantieni il nome completo della posizione nella tabella principale come bene, dopo tutto,

Come suggerimento finale, utilizzare tabelle separate per i dati aggregati più diffusi e utilizzare i processi batch per popolarli, in questo modo non è necessario ripetere l'esercizio per ogni report che utilizza un valore aggregato e crea query che confrontano l'attuale con lo storico o da storico a storico molto più semplice e molto, molto più veloce.


Potresti anche considerare Greenplum come un negozio colonnare se li guardi! Come "bonus" - si basa su PostgreSQL!
Vérace,

Ho avuto una buona esperienza con HP Vertica. Avevamo una sola tabella con 9 colonne con 130 miliardi di righe, senza molta ottimizzazione. Ha appena funzionato.
ThatDataGuy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.