Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Qualche tempo fa ci siamo trovati di fronte alla scelta di uno strumento ETL per lavorare con il Big Data. La soluzione precedentemente utilizzata, Informatica BDM, non ci soddisfaceva a causa delle sue funzionalità limitate. Il suo utilizzo si riduceva a un framework per l'esecuzione dei comandi spark-submit. Sul mercato non c'erano molti equivalenti in grado di gestire il volume di dati con cui abbiamo a che fare ogni giorno. Alla fine abbiamo scelto Ab Initio. Durante le dimostrazioni pilota, il prodotto ha mostrato velocità molto elevate nel trattamento dei dati. Ci sono pochissime informazioni su Ab Initio in russo, quindi abbiamo deciso di condividere la nostra esperienza su Habr.

Ab Initio offre molte trasformazioni classiche e insolite, il cui codice può essere esteso utilizzando il proprio linguaggio PDL. Per le piccole imprese, uno strumento così potente potrebbe essere sovrabbondante, e molte delle sue funzionalità potrebbero rivelarsi costose e non richieste. Tuttavia, se le vostre esigenze si avvicinano a quelle delle grandi banche, Ab Initio potrebbe essere interessante per voi.

Aiuta le aziende a raccogliere conoscenze globali e sviluppare un ecosistema, mentre consente agli sviluppatori di migliorare le proprie competenze in ETL, di approfondire le conoscenze nel shell, offre la possibilità di apprendere il linguaggio PDL, fornisce una visione visiva dei processi di caricamento e semplifica lo sviluppo grazie a un'ampia gamma di componenti funzionali.

In questo post parlerò delle possibilità di Ab Initio e fornirò caratteristiche comparative del suo utilizzo con Hive e GreenPlum.

  • Descrizione del framework MDW e dei lavori per la sua configurazione su GreenPlum
  • Caratteristiche comparative delle prestazioni di Ab Initio con Hive e GreenPlum
  • Funzionamento di Ab Initio con GreenPlum in modalità Near Real Time


Le funzionalità di questo prodotto sono molto ampie e richiedono tempo per essere comprese. Tuttavia, con le giuste competenze e impostazioni delle prestazioni, i risultati del trattamento dei dati sono molto impressionanti. L'utilizzo di Ab Initio può fornire agli sviluppatori un'esperienza interessante. È un nuovo approccio allo sviluppo ETL, un ibrido tra un ambiente visivo e lo sviluppo di caricamenti in un linguaggio simile a uno script.

Le aziende stanno sviluppando i propri ecosistemi e questo strumento si rivela estremamente utile. Con Ab Initio è possibile accumulare conoscenze sull’attività attuale e sfruttarle per espandere le attività esistenti e aprirne di nuove. Tra le alternative ad Ab Initio si possono citare gli ambienti di sviluppo visivi come Informatica BDM e gli ambienti non visivi come Apache Spark.

Descrizione di Ab Initio

Ab Initio, come altri strumenti ETL, è un insieme di prodotti.

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Ab Initio GDE (Graphical Development Environment) è l’ambiente per gli sviluppatori, dove si configurano le trasformazioni dei dati e si collegano tramite flussi di dati rappresentati da frecce. Questo insieme di trasformazioni è chiamato grafo:

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Le connessioni in ingresso e in uscita dei componenti funzionali sono porte e contengono campi calcolati all'interno delle trasformazioni. Diversi grafi, connessi tramite flussi di frecce nell'ordine della loro esecuzione, sono chiamati piano.

Ci sono diverse centinaia di componenti funzionali, un numero considerevole. Molti di essi sono altamente specializzati. Le capacità delle trasformazioni classiche in Ab Initio sono più ampie rispetto ad altri strumenti ETL. Ad esempio, Join ha più uscite. Oltre al risultato dell'unione dei dataset, è possibile ottenere come uscita le registrazioni dei dataset in ingresso che non sono riusciti a unirsi. Inoltre, si possono ottenere rejects, errors e log del funzionamento della trasformazione, che possono essere letti come un file di testo in questo stesso grafo e trattati da altre trasformazioni:

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

O, ad esempio, è possibile materializzare il ricevitore di dati come una tabella e leggere i dati da esso nello stesso grafo.

Ci sono trasformazioni originali. Ad esempio, la trasformazione Scan ha funzionalità simili a quelle delle funzioni analitiche. Ci sono trasformazioni con nomi esplicativi: Create Data, Read Excel, Normalize, Sort within Groups, Run Program, Run SQL, Join with DB, ecc. I grafi possono utilizzare parametri di runtime, inclusa la possibilità di trasferire parametri dal sistema operativo o al sistema operativo. I file con un insieme pronto di parametri da trasferire al grafo sono chiamati parameter sets (psets).

Come ci si aspetta, Ab Initio GDE ha il suo repository, chiamato EME (Enterprise Meta Environment). Gli sviluppatori hanno la possibilità di lavorare con versioni locali del codice e di effettuare check in delle proprie sviluppazioni nel repository centrale.

È possibile, durante o dopo l'esecuzione del grafo, fare clic su qualsiasi connessione della trasformazione del flusso e visualizzare i dati passati tra queste trasformazioni:

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

C'è anche la possibilità di fare clic su qualsiasi flusso e visualizzare i dettagli di tracciamento: quante parallelismi ha elaborato la trasformazione, quante righe e byte sono state caricate in ciascuna delle parallelismi:

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

È possibile suddividere l'esecuzione del grafo in fasi e contrassegnare quali trasformazioni devono essere eseguite per prime (nella fase zero), quali nella prima fase, quali nella seconda fase, e così via.

Per ogni trasformazione è possibile scegliere un layout (dove sarà eseguita): senza parallelismi o in flussi paralleli, il cui numero può essere specificato. In questo caso, i file temporanei creati da Ab Initio durante l'elaborazione delle trasformazioni possono essere collocati sia nel file system server, che in HDFS.

In ogni trasformazione basata su un template predefinito è possibile creare il proprio script in linguaggio PDL, che ricorda un po' la shell.

Utilizzando il linguaggio PDL, è possibile estendere le funzionalità delle trasformazioni e, in particolare, è possibile generare dinamicamente (durante l'esecuzione) frammenti di codice arbitrari in base ai parametri di runtime.

Inoltre, Ab Initio ha una buona integrazione con il sistema operativo tramite shell. In particolare, in Sberbank si utilizza linux ksh. È possibile scambiare variabili con la shell e utilizzarle come parametri nei grafic. È possibile chiamare l'esecuzione dei grafici Ab Initio dalla shell e gestire Ab Initio.

Oltre ad Ab Initio GDE, nella fornitura sono inclusi molti altri prodotti. C'è il proprio Co>Operation System che ambisce a essere un sistema operativo. C'è Control>Center, dove è possibile programmare e monitorare i flussi di caricamento. Esistono prodotti per lo sviluppo a un livello più primitivo rispetto a quanto consente Ab Initio GDE.

Descrizione del framework MDW e dei lavori per la sua configurazione su GreenPlum

Insieme ai propri prodotti, il fornitore offre il prodotto MDW (Metadata Driven Warehouse), che è un configuratore di grafi progettato per assistere nelle attività tipiche di popolamento dei data warehouse o dei data vault.

Contiene parser di metadati personalizzati (specifici per il progetto) e generatori di codice pronti all'uso 'out of the box'.

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum
MDW riceve in input un modello di dati, un file di configurazione per la connessione al database (Oracle, Teradata o Hive) e alcune altre impostazioni. La parte specifica del progetto, ad esempio, distribuisce il modello nel database. La parte standard del prodotto genera grafi e file di configurazione corrispondenti in base al caricamento dei dati nelle tabelle del modello. Durante questo processo vengono creati grafi (e psets) per diverse modalità di lavoro iniziale e incrementale per l'aggiornamento delle entità.

Nei casi di Hive e RDBMS, vengono generati grafi differenti per l'aggiornamento iniziale e incrementale dei dati.

Nel caso di Hive, i dati di delta ricevuti vengono uniti tramite Ab Initio Join ai dati che erano presenti nella tabella prima dell'aggiornamento. I caricamenti di dati in MDW (sia in Hive che in RDBMS) non solo inseriscono nuovi dati dalla delta, ma chiudono anche i periodi di validità dei dati in base alle chiavi primarie da cui proviene la delta. Inoltre, è necessario riscrivere da zero la parte di dati non modificata. Questo è necessario, dato che in Hive non ci sono operazioni di delete o update.

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Nel caso di RDBMS, i grafi per l'aggiornamento incrementale dei dati appaiono più ottimali, poiché gli RDBMS hanno reali capacità di aggiornamento.

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

La delta ricevuta viene caricata in una tabella intermedia nel database. Successivamente avviene l'unione della delta con i dati che erano presenti nella tabella prima dell'aggiornamento. Questo avviene tramite SQL utilizzando una query SQL generata. Successivamente, con i comandi SQL delete+insert nella tabella di destinazione, vengono inseriti i nuovi dati dalla delta e chiusi i periodi di validità dei dati in base alle chiavi primarie da cui proviene la delta.
Non è necessario riscrivere i dati non modificati.

Pertanto, siamo giunti alla conclusione che nel caso di Hive, MDW deve procedere a riscrivere l'intera tabella, poiché Hive non ha una funzione di aggiornamento. Non c'è niente di meglio della completa riscrittura dei dati durante l'aggiornamento. Nel caso di RDBMS, al contrario, i creatori del prodotto hanno ritenuto opportuno affidare l'unione e l'aggiornamento delle tabelle all'uso di SQL.

Per un progetto con Sberbank, abbiamo creato una nuova implementazione riutilizzabile del caricatore di basi dati per GreenPlum. Questo è stato realizzato sulla base di una versione generata da MDW per Teradata. Proprio Teradata, e non Oracle, è risultata la più adatta e simile, poiché è anch'essa un sistema MPP. I metodi operativi e la sintassi di Teradata e GreenPlum si sono dimostrati affini.

Ecco alcuni esempi delle differenze critiche per MDW tra diversi RDBMS. In GreenPlum, a differenza di Teradata, è necessario scrivere una clausola durante la creazione delle tabelle.

distributed by

In Teradata si scrive

delete <table> all

, mentre in GreenPlum si scrive

elimina da <table>

In Oracle, per ottimizzare, si scrive

delete from t where rowid in ()

, mentre in Teradata e GreenPlum si scrive

delete from t where exists (select * from delta where delta.pk=t.pk)

Va inoltre notato che per far funzionare Ab Initio con GreenPlum è stato necessario installare il client GreenPlum su tutti i nodi del cluster Ab Initio. Questo perché ci siamo connessi a GreenPlum contemporaneamente da tutti i nodi del nostro cluster. E affinché la lettura da GreenPlum fosse parallela e ogni flusso parallelo di Ab Initio leggesse la propria porzione di dati da GreenPlum, abbiamo dovuto inserire nella sezione "where" delle query SQL una costruzione comprensibile da Ab Initio.

where ABLOCAL()

e definire il valore di questa costruzione, specificando il parametro per la trasformazione che legge dal DB.

ablocal_expr="string_concat("mod(t.", string_filter_out("{$TABLE_KEY}","{}"), ",", (decimal(3))(number_of_partitions()),")=", (decimal(3))(this_partition()))"

, che si compila in qualcosa di simile a

mod(sk,10)=3

, cioè è necessario fornire a GreenPlum un filtro esplicito per ogni partizione. Per altri database (Teradata, Oracle), Ab Initio può eseguire automaticamente questo parallelismo.

Caratteristiche comparative delle prestazioni di Ab Initio con Hive e GreenPlum

In Sberbank è stato condotto un esperimento per confrontare le prestazioni dei grafi generati da MDW in relazione a Hive e a GreenPlum. Nell'ambito dell'esperimento, nel caso di Hive c'erano 5 nodi nello stesso cluster di Ab Initio, mentre nel caso di GreenPlum c'erano 4 nodi in un cluster separato. Cioè, Hive aveva un certo vantaggio su GreenPlum 'in termini di hardware'.

Sono state esaminate due coppie di grafi che eseguono lo stesso compito di aggiornamento dei dati in Hive e in GreenPlum. Nel frattempo, sono stati avviati i grafi generati dal configuratore MDW:

  • caricamento iniziale + caricamento incrementale di dati generati casualmente nella tabella Hive
  • caricamento iniziale + caricamento incrementale di dati generati casualmente nella stessa tabella GreenPlum

In entrambi i casi (Hive e GreenPlum) sono state avviate le operazioni di caricamento in 10 flussi paralleli su uno stesso cluster Ab Initio. I dati intermedi per il calcolo sono stati salvati in HDFS (in termini di Ab Initio è stato utilizzato MFS layout usando HDFS). Una riga di dati generati casualmente occupava in entrambi i casi 200 byte.

Il risultato è stato il seguente:

Hive:

Caricamento iniziale in Hive

Righe inserite
6 000 000
60 000 000
600 000 000

Durata del caricamento iniziale
in secondi
41
203
1 601

Caricamento incrementale in Hive

Numero di righe presenti nella
tabella di destinazione all'inizio dell'esperimento
6 000 000
60 000 000
600 000 000

Numero di righe delta applicate alla
tabella di destinazione durante l'esperimento
6 000 000
6 000 000
6 000 000

Durata del caricamento incrementale
in secondi
88
299
2 541

GreenPlum:

Caricamento iniziale in GreenPlum

Righe inserite
6 000 000
60 000 000
600 000 000

Durata del caricamento iniziale
in secondi
72
360
3 631

Caricamento incrementale in GreenPlum

Numero di righe presenti nella
tabella di destinazione all'inizio dell'esperimento
6 000 000
60 000 000
600 000 000

Numero di righe delta applicate alla
tabella di destinazione durante l'esperimento
6 000 000
6 000 000
6 000 000

Durata del caricamento incrementale
in secondi
159
199
321

Possiamo vedere che la velocità del caricamento iniziale sia in Hive che in GreenPlum dipende linearmente dalla quantità di dati e per motivi di hardware migliore risulta essere leggermente più veloce per Hive rispetto a GreenPlum.

Il caricamento incrementale in Hive dipende anch'esso linearmente dalla quantità di dati precedentemente caricati nella tabella di destinazione e procede piuttosto lentamente con l'aumento della quantità. Questo è dovuto alla necessità di riscrivere l'intera tabella di destinazione. Ciò significa che applicare piccole modifiche a tabelle enormi non è un buon approccio per Hive.

Al contrario, il caricamento incrementale in GreenPlum dipende poco dalla quantità di dati precedentemente caricati nella tabella di destinazione e procede piuttosto rapidamente. Questo è stato possibile grazie alle SQL Joins e all'architettura di GreenPlum, che consente l'operazione di delete.

Quindi, GreenPlum inserisce il delta tramite delete+insert, mentre in Hive non ci sono operazioni delete o update, quindi l'intero set di dati durante l'aggiornamento incrementale ha dovuto essere riscritto completamente. I dati evidenziati in grassetto sono particolarmente significativi poiché corrispondono al caso più comune di utilizzo di carichi di lavoro che richiedono molte risorse. Possiamo vedere che GreenPlum ha superato Hive in questo test di 8 volte.

Funzionamento di Ab Initio con GreenPlum in modalità Near Real Time

In questo esperimento verificheremo la capacità di Ab Initio di aggiornare la tabella GreenPlum con porzioni di dati generate casualmente in un modo vicino al tempo reale. Considereremo la tabella GreenPlum dev42_1_db_usl.TESTING_SUBJ_org_finval, con la quale si lavorerà.

Useremo tre grafi Ab Initio per lavorarci:

1) Grafico Create_test_data.mp – crea file di dati in HDFS con 6.000.000 di righe in 10 flussi paralleli. I dati sono casuali, la loro struttura è organizzata per l'inserimento nella nostra tabella.

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

2) Grafico mdw_load.day_one.current.dev42_1_db_usl_testing_subj_org_finval.pset – grafico MDW generato per l'inserimento iniziale dei dati nella nostra tabella in 10 flussi paralleli (vengono utilizzati dati di test generati dal grafico (1)).

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

3) Grafico mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset – grafico MDW generato per l'aggiornamento incrementale della nostra tabella in 10 flussi paralleli utilizzando un lotto di dati freschi in arrivo (delta), generati dal grafico (1).

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum

Eseguiamo il seguente scenario in modalità NRT:

  • generare 6.000.000 righe di test
  • effettuare un caricamento iniziale, inserire 6.000.000 righe di test in una tabella vuota
  • ripetere 5 volte il caricamento incrementale
    • generare 6.000.000 righe di test
    • effettuare un'inserzione incrementale di 6.000.000 righe di test nella tabella (in questo caso, ai dati esistenti viene assegnata una scadenza valid_to_ts e vengono inseriti dati più recenti con la stessa chiave primaria).

Questo scenario emula il funzionamento reale di un sistema aziendale: una quantità significativa di nuovi dati appare in tempo reale e viene immediatamente inserita in GreenPlum.

Ora diamo un'occhiata al registro dell'esecuzione dello scenario:

Inizio Create_test_data.input.pset alle 2020-06-04 11:49:11
Fine Create_test_data.input.pset alle 2020-06-04 11:49:37
Inizio mdw_load.day_one.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:49:37
Fine mdw_load.day_one.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:50:42
Inizio Create_test_data.input.pset alle 2020-06-04 11:50:42
Fine Create_test_data.input.pset alle 2020-06-04 11:51:06
Inizio mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:51:06
Fine mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:53:41
Inizio Create_test_data.input.pset alle 2020-06-04 11:53:41
Fine Create_test_data.input.pset alle 2020-06-04 11:54:04
Inizio mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:54:04
Fine mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:56:51
Inizio Create_test_data.input.pset alle 2020-06-04 11:56:51
Fine Create_test_data.input.pset alle 2020-06-04 11:57:14
Inizio mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:57:14
Fine mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 11:59:55
Inizio Create_test_data.input.pset alle 2020-06-04 11:59:55
Fine Create_test_data.input.pset alle 2020-06-04 12:00:23
Inizio mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 12:00:23
Fine mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset alle 2020-06-04 12:03:23
Inizio Create_test_data.input.pset alle 2020-06-04 12:03:23
Fine Create_test_data.input.pset alle 2020-06-04 12:03:49
Inizia mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset il 2020-06-04 12:03:49
Fine mdw_load.regular.current.dev42_1_db_usl_testing_subj_org_finval.pset il 2020-06-04 12:06:46

Ecco l'immagine risultante:

Grafico
Start time
Tempo di fine
Lunghezza

Create_test_data.input.pset
04.06.2020 11:49:11
04.06.2020 11:49:37
00:00:26

mdw_load.day_one.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 11:49:37
04.06.2020 11:50:42
00:01:05

Create_test_data.input.pset
04.06.2020 11:50:42
04.06.2020 11:51:06
00:00:24

mdw_load.regular.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 11:51:06
04.06.2020 11:53:41
00:02:35

Create_test_data.input.pset
04.06.2020 11:53:41
04.06.2020 11:54:04
00:00:23

mdw_load.regular.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 11:54:04
04.06.2020 11:56:51
00:02:47

Create_test_data.input.pset
04.06.2020 11:56:51
04.06.2020 11:57:14
00:00:23

mdw_load.regular.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 11:57:14
04.06.2020 11:59:55
00:02:41

Create_test_data.input.pset
04.06.2020 11:59:55
04.06.2020 12:00:23
00:00:28

mdw_load.regular.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 12:00:23
04.06.2020 12:03:23
00:03:00

Create_test_data.input.pset
04.06.2020 12:03:23
04.06.2020 12:03:49
00:00:26

mdw_load.regular.current.
dev42_1_db_usl_testing_subj_org_finval.pset
04.06.2020 12:03:49
04.06.2020 12:06:46
00:02:57

Possiamo vedere che 6.000.000 righe di incremento vengono elaborate in 3 minuti, il che è abbastanza veloce.
I dati nella tabella di destinazione sono stati distribuiti nel seguente modo:

select valid_from_ts, valid_to_ts, count(1), min(sk), max(sk) from dev42_1_db_usl.TESTING_SUBJ_org_finval group by valid_from_ts, valid_to_ts order by 1,2;

Quando hai scale da Sberbank. Utilizzo di Ab Initio per lavorare con Hive e GreenPlum
Si può notare la corrispondenza dei dati inseriti con i momenti di avvio dei grafi.
Quindi, è possibile avviare in Ab Initio il caricamento incrementale dei dati in GreenPlum con una frequenza molto alta e osservare un'alta velocità di inserimento di questi dati in GreenPlum. Naturalmente, non sarà possibile avviarsi ogni secondo, poiché Ab Initio, come qualsiasi strumento ETL, richiede del tempo per l'inizializzazione all'avvio.

Conclusione

Attualmente, Ab Initio è utilizzato da Sberbank per costruire un Unico Livello Semantico dei Dati (ECS). Questo progetto prevede la creazione di una versione unica dello stato di diverse entità di business bancario. Le informazioni provengono da diverse fonti, le cui repliche vengono preparate su Hadoop. Sulla base delle esigenze aziendali, viene preparato un modello di dati e vengono descritte le trasformazioni dei dati. Ab Initio carica l'informazione nell'ECS e i dati caricati non solo sono di interesse per il business, ma servono anche come fonte per costruire vetrine dei dati. Inoltre, le funzionalità del prodotto permettono di utilizzare come destinatario diversi sistemi (Hive, Greenplum, Teradata, Oracle), il che consente di preparare i dati per il business in vari formati richiesti senza eccessivi sforzi.

Le capacità di Ab Initio sono ampie; ad esempio, il framework allegato MDW consente di costruire la storicità tecnica e aziendale dei dati "out of the box". Per gli sviluppatori, Ab Initio offre la possibilità di "non reinventare la ruota" e di utilizzare i molteplici componenti funzionali già disponibili, che essenzialmente fungono da librerie necessarie per lavorare con i dati.

Autore — esperto della comunità professionale di Sberbank SberProfi DWH/BigData. La comunità professionale SberProfi DWH/BigData è responsabile dello sviluppo delle competenze nei settori come l'ecosistema Hadoop, Teradata, Oracle DB, GreenPlum, così come negli strumenti BI Qlik, SAP BO, Tableau e altri.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster