Database KDB+: dalla finanza alla Formula 1

KDB+, prodotto aziendale KX è un database colonnare ampiamente conosciuto in ambienti ristretti, estremamente veloce, progettato per archiviare serie temporali e calcoli analitici basati su di esse. Inizialmente era (ed è) molto popolare nel settore finanziario: lo utilizzano tutte le 10 principali banche di investimento e molti noti hedge fund, borse valori e altre organizzazioni. Recentemente, KX ha deciso di espandere la propria base di clienti e ora offre soluzioni in altre aree in cui è presente una grande quantità di dati, organizzati in base al tempo o in altro modo: telecomunicazioni, bioinformatica, produzione, ecc. Sono diventati anche partner del team Aston Martin Red Bull Racing in Formula 1, dove aiutano a raccogliere ed elaborare i dati dai sensori delle auto e ad analizzare i test nella galleria del vento. In questo articolo voglio raccontarti quali caratteristiche di KDB+ lo rendono super performante, perché le aziende sono disposte a spendere molti soldi per esso e, infine, perché non è un vero e proprio database.
 
Database KDB+: dalla finanza alla Formula 1
 
In questo articolo cercherò di raccontarti in generale cos'è KDB+, quali funzionalità e limitazioni ha e quali sono i vantaggi per le aziende che vogliono elaborare grandi quantità di dati. Non entrerò nei dettagli dell'implementazione di KDB+ o nei dettagli del suo linguaggio di programmazione Q. Entrambi questi argomenti sono molto ampi e meritano articoli separati. Molte informazioni su questi argomenti possono essere trovate su code.kx.com, incluso un libro su Q - Q For Mortals (vedi link sotto).

Alcuni termini

  • Database in memoria. Un database che memorizza i dati nella RAM per un accesso più rapido. I vantaggi di un database di questo tipo sono evidenti, ma gli svantaggi sono la possibilità di perdita di dati e la necessità di avere molta memoria sul server.
  • Base di dati colonnare. Un database in cui i dati vengono archiviati colonna per colonna anziché record per record. Il vantaggio principale di un tale database è che i dati di una colonna vengono archiviati insieme su disco e in memoria, il che accelera notevolmente l'accesso ad esso. Non è necessario caricare le colonne non utilizzate nella query. Lo svantaggio principale è che è difficile modificare ed eliminare i record.
  • Serie temporali. Dati con una colonna di data o ora. In genere, l'ordinamento temporale è importante per tali dati, in modo da poter determinare facilmente quale record precede o segue quello corrente o per applicare funzioni i cui risultati dipendono dall'ordine dei record. I database classici sono costruiti secondo un principio completamente diverso: rappresentano una raccolta di record come un insieme, dove l'ordine dei record, in linea di principio, non è definito.
  • Vettore. Nel contesto di KDB+, questo è un elenco di elementi dello stesso tipo atomico, ad esempio i numeri. In altre parole, un array di elementi. Gli array, a differenza degli elenchi, possono essere archiviati in modo compatto ed elaborati utilizzando le istruzioni del processore vettoriale.

 

storia

KX è stata fondata nel 1993 da Arthur Whitney, che in precedenza aveva lavorato presso la Morgan Stanley Bank sul linguaggio A+, il successore di APL, un linguaggio molto originale e un tempo popolare nel mondo finanziario. Naturalmente, in KX, Arthur ha continuato con lo stesso spirito e ha creato il linguaggio funzionale vettoriale K, guidato dalle idee del minimalismo radicale. I programmi K sembrano un miscuglio di punteggiatura e caratteri speciali, il significato di segni e funzioni dipende dal contesto e ogni operazione ha molto più significato di quanto non ne abbia nei linguaggi di programmazione convenzionali. Per questo motivo, un programma K occupa uno spazio minimo (poche righe possono sostituire pagine di testo in un linguaggio dettagliato come Java) ed è un'implementazione super concentrata dell'algoritmo.
 
Una funzione in K che implementa la maggior parte del generatore del parser LL1 secondo una data grammatica:

1. pp:{q:{(x;p3(),y)};r:$[-11=@x;$x;11=@x;q[`N;$*x];10=abs@@x;q[`N;x]  
2.   ($)~*x;(`P;p3 x 1);(1=#x)&11=@*x;pp[{(1#x;$[2=#x;;,:]1_x)}@*x]  
3.      (?)~*x;(`Q;pp[x 1]);(*)~*x;(`M;pp[x 1]);(+)~*x;(`MP;pp[x 1]);(!)~*x;(`Y;p3 x 1)  
4.      (2=#x)&(@x 1)in 100 101 107 7 -7h;($[(@x 1)in 100 101 107h;`Ff;`Fi];p3 x 1;pp[*x])  
5.      (|)~*x;`S,(pp'1_x);2=#x;`C,{@[@[x;-1+#x;{x,")"}];0;"(",]}({$[".s.C"~4#x;6_-2_x;x]}'pp'x);'`pp];  
6.   $[@r;r;($[1<#r;".s.";""],$*r),$[1<#r;"[",(";"/:1_r),"]";""]]}  

 Arthur ha incarnato questa filosofia di estrema efficienza con il minimo movimento del corpo in KDB+, apparso nel 2003 (credo sia ormai chiaro da dove deriva la lettera K nel nome) e non è altro che un interprete della quarta versione della K Una versione più user-friendly è stata aggiunta oltre a K K chiamata Q. Q ha anche aggiunto il supporto per un dialetto specifico di SQL - QSQL, e l'interprete - supporto per le tabelle come tipo di dati di sistema, strumenti per lavorare con le tabelle in memoria e su disco, ecc.
 
Quindi, dal punto di vista dell'utente, KDB+ è semplicemente un interprete del linguaggio Q con supporto per tabelle ed espressioni in stile LINQ simili a SQL da C#. Questa è la differenza più importante tra KDB+ e altri database e il suo principale vantaggio competitivo, che spesso viene trascurato. Questo non è un database + un linguaggio ausiliario disabilitato, ma un potente linguaggio di programmazione a tutti gli effetti + supporto integrato per le funzioni del database. Questa distinzione giocherà un ruolo decisivo nell’elencare tutti i vantaggi di KDB+. Per esempio…
 

dimensione

Secondo gli standard moderni, KDB+ ha dimensioni semplicemente microscopiche. È letteralmente un file eseguibile di dimensioni inferiori al megabyte e un piccolo file di testo con alcune funzioni di sistema. In realtà, meno di un megabyte, e per questo programma le aziende pagano decine di migliaia di dollari all'anno per un processore sul server.

  • Questa dimensione consente a KDB+ di funzionare perfettamente su qualsiasi hardware, da un microcomputer Pi ai server con terabyte di memoria. Ciò non influisce in alcun modo sulla funzionalità; inoltre, Q si avvia immediatamente, il che ne consente l'utilizzo, tra le altre cose, come linguaggio di scripting.
  • A queste dimensioni, l'interprete Q si inserisce interamente nella cache del processore, accelerando l'esecuzione del programma.
  • Con questa dimensione del file eseguibile, il processo Q occupa uno spazio trascurabile in memoria; puoi eseguirne centinaia. Inoltre, se necessario, Q può operare con decine o centinaia di gigabyte di memoria all'interno di un singolo processo.

Universalismo

Q è ottimo per un'ampia gamma di applicazioni. Process Q può fungere da database storico e fornire un rapido accesso a terabyte di informazioni. Ad esempio, disponiamo di dozzine di database storici, in alcuni dei quali un giorno di dati non compressi occupa più di 100 gigabyte. Tuttavia, con ragionevoli restrizioni, una query al database verrà completata in decine o centinaia di millisecondi. In generale, abbiamo un timeout universale per le richieste degli utenti - 30 secondi - e funziona molto raramente.
 
Q potrebbe facilmente essere un database in memoria. I nuovi dati vengono aggiunti alle tabelle in memoria così rapidamente che le richieste degli utenti rappresentano il fattore limitante. I dati nelle tabelle vengono archiviati in colonne, il che significa che qualsiasi operazione su una colonna utilizzerà la cache del processore a piena capacità. Oltre a questo, KX ha cercato di implementare tutte le operazioni di base come l'aritmetica tramite istruzioni vettoriali del processore, massimizzandone la velocità. Q può anche eseguire attività non tipiche dei database, ad esempio elaborare dati in streaming e calcolare in "tempo reale" (con un ritardo da decine di millisecondi a diversi secondi a seconda dell'attività) varie funzioni di aggregazione per strumenti finanziari per tempi diversi intervalli o costruire un modello dell'influenza delle transazioni perfette sul mercato ed effettuare la sua profilazione quasi immediatamente dopo il suo completamento. In tali attività, molto spesso il ritardo temporale principale non è Q, ma la necessità di sincronizzare dati da fonti diverse. L'elevata velocità si ottiene grazie al fatto che i dati e le funzioni che li elaborano si trovano in un unico processo e l'elaborazione si riduce all'esecuzione di diverse espressioni e join QSQL, che non vengono interpretati, ma vengono eseguiti tramite codice binario.
 
Infine, puoi scrivere qualsiasi processo di servizio in Q. Ad esempio, i processi Gateway che distribuiscono automaticamente le richieste degli utenti ai database e ai server necessari. Il programmatore ha completa libertà di implementare qualsiasi algoritmo per il bilanciamento, la definizione delle priorità, la tolleranza agli errori, i diritti di accesso, le quote e praticamente qualsiasi altra cosa desideri. Il problema principale qui è che dovrai implementare tutto questo da solo.
 
Ad esempio, elencherò i tipi di processi che abbiamo. Tutti vengono utilizzati attivamente e lavorano insieme, combinando dozzine di database diversi in uno solo, elaborando dati da più fonti e servendo centinaia di utenti e applicazioni.

  • Connettori (feedhandler) alle origini dati. Questi processi in genere utilizzano librerie esterne caricate in Q. L'interfaccia C in Q è estremamente semplice e consente di creare facilmente funzioni proxy per qualsiasi libreria C/C++. Q è abbastanza veloce da gestire, ad esempio, l'elaborazione simultanea di un flusso di messaggi FIX provenienti da tutte le borse europee.
  • Distributori di dati (tickerplant), che fungono da collegamento intermedio tra connettori e consumatori. Allo stesso tempo, scrivono i dati in entrata in uno speciale registro binario, fornendo robustezza ai consumatori contro perdite di connessione o riavvii.
  • Database in memoria (rdb). Questi database forniscono l'accesso più rapido possibile a dati grezzi e aggiornati archiviandoli in memoria. In genere, accumulano dati nelle tabelle durante il giorno e li reimpostano di notte.
  • Database persistente (PDB). Questi database garantiscono che i dati odierni siano archiviati in un database cronologico. Di norma, a differenza di rdb, non memorizzano i dati in memoria, ma durante il giorno utilizzano una cache speciale su disco e copiano i dati a mezzanotte nel database storico.
  • Database storici (hdb). Questi database forniscono l'accesso ai dati dei giorni, dei mesi e degli anni precedenti. La loro dimensione (in giorni) è limitata solo dalla dimensione dei dischi rigidi. I dati possono essere localizzati ovunque, in particolare su dischi diversi per velocizzare l'accesso. È possibile comprimere i dati utilizzando diversi algoritmi tra cui scegliere. La struttura del database è ben documentata e semplice, i dati sono archiviati colonna per colonna in file regolari, quindi possono essere elaborati anche tramite il sistema operativo.
  • Database con informazioni aggregate. Memorizzano varie aggregazioni, solitamente con, raggruppate per nome dello strumento e intervallo di tempo. I database in memoria aggiornano il proprio stato con ogni messaggio in arrivo, mentre i database storici archiviano dati precalcolati per accelerare l'accesso ai dati storici.
  • Infine, l' processi di passaggioservire applicazioni e utenti. Q consente di implementare un'elaborazione completamente asincrona dei messaggi in arrivo, distribuendoli su database, controllando i diritti di accesso, ecc. Tieni presente che i messaggi non sono limitati e molto spesso non sono espressioni SQL, come nel caso di altri database. Molto spesso, l'espressione SQL è nascosta in una funzione speciale ed è costruita in base ai parametri richiesti dall'utente: il tempo viene convertito, filtrato, i dati vengono normalizzati (ad esempio, il prezzo delle azioni viene equalizzato se vengono pagati i dividendi), ecc.

Architettura tipica per un tipo di dati:

Database KDB+: dalla finanza alla Formula 1

velocità

Sebbene Q sia un linguaggio interpretato, è anche un linguaggio vettoriale. Ciò significa che molte funzioni integrate, in particolare quelle aritmetiche, accettano argomenti di qualsiasi forma - numeri, vettori, matrici, liste - e ci si aspetta che il programmatore implementi il ​​programma come operazioni su array. In un linguaggio del genere, se si sommano due vettori da un milione di elementi, non ha più importanza che il linguaggio venga interpretato; l'addizione verrà eseguita da una funzione binaria super ottimizzata. Poiché la maggior parte del tempo nei programmi Q viene dedicato alle operazioni con tabelle che utilizzano queste funzioni vettoriali di base, l'output è una velocità operativa molto decente, che ci consente di elaborare un'enorme quantità di dati anche in un unico processo. Questo è simile alle librerie matematiche in Python: sebbene Python stesso sia un linguaggio molto lento, ha molte librerie eccellenti come numpy che ti consentono di elaborare dati numerici alla velocità di un linguaggio compilato (a proposito, numpy è ideologicamente vicino a Q ).
 
Inoltre, KX ha adottato un approccio molto attento alla progettazione dei tavoli e all'ottimizzazione del lavoro con essi. Innanzitutto, sono supportati diversi tipi di indici, che sono supportati da funzioni integrate e possono essere applicati non solo alle colonne della tabella, ma anche a qualsiasi vettore: raggruppamento, ordinamento, attributo di unicità e raggruppamento speciale per database storici. L'indice viene applicato in modo semplice e viene regolato automaticamente quando si aggiungono elementi alla colonna/vettore. Gli indici possono essere applicati con successo anche alle colonne delle tabelle sia in memoria che su disco. Quando si esegue una query QSQL, gli indici vengono utilizzati automaticamente, se possibile. In secondo luogo, il lavoro con i dati storici viene eseguito tramite il meccanismo di visualizzazione dei file del sistema operativo (mappa della memoria). Le tabelle di grandi dimensioni non vengono mai caricate in memoria; invece, le colonne necessarie vengono mappate direttamente in memoria e viene effettivamente caricata solo la parte di esse (anche gli indici aiutano in questo caso) che sono necessarie. Per il programmatore non fa differenza se i dati sono in memoria o meno; il meccanismo per lavorare con mmap è completamente nascosto nelle profondità di Q.
 
KDB+ non è un database relazionale; le tabelle possono contenere dati arbitrari, mentre l'ordine delle righe nella tabella non cambia quando vengono aggiunti nuovi elementi e può e deve essere utilizzato durante la scrittura delle query. Questa funzionalità è urgentemente necessaria per lavorare con le serie temporali (dati da scambi, telemetria, registri eventi), perché se i dati sono ordinati in base al tempo, l'utente non ha bisogno di utilizzare alcun trucco SQL per trovare la prima o l'ultima riga o N righe nella tabella, determinare quale riga segue l'ennesima riga, ecc. Le unioni di tabelle sono ulteriormente semplificate, ad esempio, trovare l'ultima quotazione per 16000 transazioni VOD.L (Vodafone) in una tabella di 500 milioni di elementi richiede circa un secondo su disco e decine di millisecondi in memoria.
 
Un esempio di time join: la tabella delle virgolette è mappata in memoria, quindi non è necessario specificare VOD.L in cui vengono utilizzati implicitamente l'indice sulla colonna sym e il fatto che i dati sono ordinati in base all'ora. Quasi tutti i join in Q sono funzioni regolari, non fanno parte di un'espressione select:

1. aj[`sym`time;select from trade where date=2019.03.26, sym=`VOD.L;select from quote where date=2019.03.26]  

Infine, vale la pena notare che gli ingegneri di KX, a cominciare dallo stesso Arthur Whitney, sono davvero ossessionati dall'efficienza e fanno di tutto per ottenere il massimo dalle caratteristiche standard della Q e ottimizzare i modelli di utilizzo più comuni.
 

risultato

KDB+ è popolare tra le aziende principalmente per la sua eccezionale versatilità: funziona altrettanto bene come database in memoria, come database per archiviare terabyte di dati storici e come piattaforma per l'analisi dei dati. Poiché l'elaborazione dei dati avviene direttamente nel database, si ottengono un'elevata velocità di lavoro e un risparmio di risorse. Un linguaggio di programmazione completo integrato con le funzioni del database consente di implementare l'intero stack di processi necessari su un'unica piattaforma, dalla ricezione dei dati all'elaborazione delle richieste degli utenti.
 

Per ulteriori informazioni,

Limitazioni

Uno svantaggio significativo di KDB+/Q è l'elevata soglia di accesso. Il linguaggio ha una sintassi strana, alcune funzioni sono fortemente sovraccaricate (value, ad esempio, ha circa 11 casi d'uso). Ancora più importante, richiede un approccio radicalmente diverso alla scrittura dei programmi. In un linguaggio vettoriale, devi sempre pensare in termini di trasformazioni di array, implementare tutti i cicli attraverso diverse varianti delle funzioni map/reduce (che sono chiamate avverbi in Q) e non cercare mai di risparmiare denaro sostituendo le operazioni vettoriali con quelle atomiche. Ad esempio, per trovare l'indice dell'ennesima occorrenza di un elemento in un array, dovresti scrivere:

1. (where element=vector)[N]  

sebbene questo sembri terribilmente inefficiente per gli standard C/Java (= crea un vettore booleano, dove restituisce i veri indici degli elementi in esso contenuti). Ma questa notazione rende più chiaro il significato dell'espressione e si utilizzano operazioni vettoriali veloci anziché quelle atomiche lente. La differenza concettuale tra un linguaggio vettoriale e altri è paragonabile alla differenza tra l'approccio imperativo e quello funzionale alla programmazione, e per questo devi essere preparato.
 
Anche alcuni utenti non sono soddisfatti di QSQL. Il punto è che sembra solo un vero SQL. In realtà, è solo un interprete di espressioni di tipo SQL che non supporta l'ottimizzazione delle query. L'utente deve scrivere da solo le query ottimali e in Q, per le quali molti non sono pronti. D'altra parte, ovviamente, puoi sempre scrivere tu stesso la query ottimale, piuttosto che affidarti a un ottimizzatore a scatola nera.
 
Inoltre, un libro su Q - Q For Mortals è disponibile gratuitamente su sito web aziendale, lì sono raccolti anche molti altri materiali utili.
 
Un altro grosso svantaggio è il costo della licenza. Si tratta di decine di migliaia di dollari all'anno per CPU. Solo le grandi aziende possono permettersi tali spese. Recentemente KX ha reso la sua politica di licenza più flessibile e offre la possibilità di pagare solo per il tempo di utilizzo o di noleggiare KDB+ nei cloud di Google e Amazon. KX offre anche il download versione gratuita per scopi non commerciali (Versione a 32 bit o 64 bit su richiesta).
 

concorrenti

Esistono numerosi database specializzati costruiti su principi simili: colonnari, in memoria, focalizzati su quantità molto grandi di dati. Il problema è che si tratta di database specializzati. Un esempio lampante è Clickhouse. Questo database ha un principio molto simile a KDB+ per la memorizzazione dei dati su disco e la creazione di un indice; esegue alcune query più velocemente di KDB+, anche se non in modo significativo. Ma anche come database, Clickhouse è più specializzato di KDB+ - analisi web rispetto a serie temporali arbitrarie (questa differenza è molto importante - per questo motivo, ad esempio, in Clickhouse non è possibile utilizzare l'ordinamento dei record). Ma, cosa più importante, Clickhouse non ha la versatilità di KDB+, un linguaggio che consentirebbe di elaborare i dati direttamente nel database, invece di caricarli prima in un'applicazione separata, costruendo espressioni SQL arbitrarie, applicando funzioni arbitrarie in una query, creando processi non correlato all'esecuzione delle funzioni del database storico. Pertanto è difficile confrontare KDB+ con altri database; potrebbero essere migliori in determinati casi d'uso o semplicemente migliori quando si tratta di attività di database classiche, ma non conosco un altro strumento altrettanto efficace e versatile per l'elaborazione di dati temporanei.
 

Integrazione con Python

Per rendere KDB+ più facile da usare per le persone che non hanno familiarità con la tecnologia, KX ha creato librerie per integrarsi strettamente con Python all'interno di un unico processo. Puoi chiamare qualsiasi funzione Python da Q o viceversa: chiamare qualsiasi funzione Q da Python (in particolare, espressioni QSQL). Le biblioteche convertono, se necessario (non sempre per ragioni di efficienza), i dati dal formato di una lingua al formato di un'altra. Di conseguenza, Q e Python vivono in una simbiosi così stretta che i confini tra loro sono sfumati. Di conseguenza, il programmatore, da un lato, ha pieno accesso a numerose utili librerie Python, dall'altro riceve una base veloce per lavorare con i Big Data integrati in Python, che è particolarmente utile per chi è coinvolto nell'apprendimento automatico o modellazione.
 
Lavorare con Q in Python:

1. >>> q()  
2.q)trade:([]date:();sym:();qty:())  
3. q)  
4. >>> q.insert('trade', (date(2006,10,6), 'IBM', 200))  
5. k(',0')  
6. >>> q.insert('trade', (date(2006,10,6), 'MSFT', 100))  
7. k(',1')  

riferimenti

Il sito dell'azienda - https://kx.com/
Sito web per sviluppatori - https://code.kx.com/v2/
Libro Q Per Mortali (in inglese) - https://code.kx.com/q4m3/
Articoli sulle applicazioni KDB+/Q dei dipendenti kx - https://code.kx.com/v2/wp/

Fonte: habr.com

Aggiungi un commento