Funzioni di a lingua Q è KDB + usendu l'esempiu di un serviziu in tempu reale

Pudete leghje ciò chì sò a basa KDB +, a lingua di prugrammazione Q, quali sò i so punti di forza è debule in u mo precedente. articulu è brevemente in l'intruduzioni. In l'articulu, implementeremu un serviziu nantu à Q chì prucederà u flussu di dati in entrata è calculate diverse funzioni di aggregazione ogni minutu in u modu "tempu reale" (vale à dì, avarà u tempu di calculà tuttu prima di a prossima parte di dati). A funzione principale di Q hè chì hè una lingua vettoriale chì permette di operà micca cù ughjetti unichi, ma cù i so arrays, arrays of arrays è altri ogetti cumplessi. Lingue cum'è Q è i so parenti K, J, APL sò famosi per a so brevità. Spessu, un prugramma chì piglia parechje schermi di codice in una lingua familiare cum'è Java pò esse scrittu annantu à elli in uni pochi di linii. Questu hè ciò chì vogliu dimustrà in questu articulu.

Funzioni di a lingua Q è KDB + usendu l'esempiu di un serviziu in tempu reale

Introduzione

KDB+ hè una basa di dati di colonna focalizata in quantità assai grande di dati, urdinatu in modu specificu (principalmente per u tempu). Hè utilizatu principarmenti in istituzioni finanziarii - banche, fondi d'investimentu, cumpagnie d'assicuranza. A lingua Q hè a lingua interna di KDB + chì vi permette di travaglià in modu efficace cù queste dati. L'ideulugia Q hè brevità è efficienza, mentre chì a clarità hè sacrificata. Questu hè ghjustificatu da u fattu chì a lingua vettoriali serà difficiuli di capiscenu in ogni casu, è a brevità è a ricchezza di l'arregistramentu permette di vede una parte assai più grande di u prugramma nantu à una sola schermu, chì in fine facilita a capiscenu.

In questu articulu implementemu un prugramma cumpletu in Q è pudete vulete pruvà. Per fà questu, avete bisognu di a Q reale. Pudete scaricà a versione 32-bit gratuitu nantu à u situ web di a cumpagnia kx - www.kx.com. Quì, se site interessatu, truverete infurmazione di riferimentu nantu à Q, u libru Q Per i Mortali è diversi articuli nantu à questu tema.

Formulazione di u prublema

Ci hè una fonte chì manda una tavola cù dati ogni 25 millisecondi. Siccomu KDB + hè utilizatu principarmenti in finanza, assumeremu chì questu hè un tavulinu di transazzione (cummerciali), chì hà e seguenti colonne: tempu (tempu in millisecondi), sym (designazione di a cumpagnia nantu à a borsa - IBM, AAPL,…), prezzu (u prezzu à quale l'azzioni sò stati acquistati), dimensione (taglia di a transazzione). L'intervallu di 25 millisecondi hè arbitrariu, micca troppu chjucu è micca troppu longu. A so prisenza significa chì i dati venenu à u serviziu digià buffered. Saria faciule d'implementazione di u buffering in u latu di serviziu, cumpresu u buffering dinamicu secondu a carica attuale, ma per a simplicità, ci focalizemu nantu à un intervallu fissu.

U serviziu deve cuntà ogni minutu per ogni simbulu entrante da a colonna sym un inseme di funzioni di aggregazione - prezzu max, prezzu mediu, dimensione somma, etc. infurmazione utile. Per simplicità, assumeremu chì tutte e funzioni ponu esse calculate incrementali, i.e. per ottene un novu valore, hè abbastanza per cunnosce dui numeri - i vechji è i valori entranti. Per esempiu, e funzioni max, average, sum anu sta pruprietà, ma a funzione mediana ùn hè micca.

Avemu da assume ancu chì u flussu di dati in entrata hè u tempu urdinatu. Questu ci darà l'uppurtunità di travaglià solu cù l'ultimu minutu. In pratica, hè abbastanza per pudè travaglià cù i minuti attuali è precedenti in casu chì alcune aghjurnamenti sò tardi. Per simplicità, ùn avemu micca cunsideratu stu casu.

Funzioni di aggregazione

E funzioni di aggregazione richieste sò listate quì sottu. Aghju pigliatu quant'è parechji pussibuli per aumentà a carica nantu à u serviziu:

  • altu - prezzu massimu - prezzu massimu per minutu.
  • low - prezzu minimu - prezzu minimu per minutu.
  • firstPrice - primu prezzu - primu prezzu per minutu.
  • lastPrice - ultimu prezzu - ultimu prezzu per minutu.
  • firstSize - prima taglia - prima taglia di cummerciu per minutu.
  • lastSize - last size - l'ultima taglia di cummerciu in un minutu.
  • numTrades - count i - numeru di cummerciu per minutu.
  • volume - sum size - sum of trade sizes per minute.
  • pvolume - sum price - summa di prezzi per minutu, necessariu per avgPrice.
  • - somma u prezzu di fattura * taglia - u voluminu tutale di transazzione per minutu.
  • avgPrice - pvolume%numTrades - prezzu mediu per minutu.
  • avgSize - volume%numTrades - dimensione media di u cummerciu per minutu.
  • vwap - turnover%volume - prezzu mediu per minutu ponderatu da a dimensione di a transazzione.
  • cumVolume - sum volume - dimensioni accumulate di transacciones in tuttu u tempu.

Discutemu immediatamente un puntu micca evidenti - cumu inizializza queste colonne per a prima volta è per ogni minutu dopu. Alcune colonne di u tipu firstPrice deve esse inizializatu à null ogni volta; u so valore ùn hè micca definitu. L'altri tippi di voluminu deve esse sempre stabilitu à 0. Ci sò ancu e culonni chì necessitanu un accostu cumminatu - per esempiu, cumVolume deve esse copiatu da u minutu precedente, è per u primu stabilitu à 0. Fighjemu tutti questi paràmetri cù i dati di u dizziunariu. tipu (analogicu à un record):

// list ! list – создать словарь, 0n – float null, 0N – long null, `sym – тип символ, `sym1`sym2 – список символов
initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time; // список всех вычисляемых колонок, reverse объяснен ниже

Aghju aghjustatu sym è u tempu à u dizziunariu per a cunvenzione, avà initWith hè una linea pronta da a tavola aggregata finale, induve resta per stabilisce u sym è u tempu curretti. Pudete aduprà per aghjunghje novi fila à una tavola.

Avemu bisognu di aggCols quandu crea una funzione di aggregazione. A lista deve esse invertita per l'ordine in quale l'espressioni in Q sò valutate (da diritta à manca). L'obiettivu hè di assicurà chì u calculu passa da altu à cumVolume, postu chì alcune colonne dependenu di e precedente.

Culonni chì deve esse copiatu à un novu minutu da u precedente, a colonna sym hè aghjuntu per comodità:

rollColumns:`sym`cumVolume;

Avà dividemu e culonne in gruppi secondu cumu si deve esse aghjurnatu. Si ponu distingue trè tippi:

  1. Accumulatori (volume, turnover, ..) - avemu da aghjunghje u valore in entrata à u precedente.
  2. Cù un puntu spiciali (altu, bassu, ..) - u primu valore in u minutu hè pigliatu da i dati entranti, u restu hè calculatu cù a funzione.
  3. Restu. Sempre calculatu cù una funzione.

Definimu variabili per queste classi:

accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;

Ordine di calculu

Avemu da aghjurnà a tavola aggregata in duie tappe. Per efficienza, avemu prima riduzzione di a tavola entrata in modu chì ci hè solu una fila per ogni caratteru è minutu. U fattu chì tutte e nostre funzioni sò incrementali è assuciativi guarantisci chì u risultatu di stu passu supplementu ùn cambia micca. Pudete riduzzione di a tavola cù selezziunate:

select high:max price, low:min price … by sym,time.minute from table

Stu metudu hà un svantaghju - u settore di culonni calculati hè predefinitu. Fortunatamente, in Q, a selezzione hè ancu implementata cum'è una funzione induve pudete rimpiazzà l'argumenti creati dinamicamente:

?[table;whereClause;byClause;selectClause]

Ùn descriveraghju micca in dettagliu u formatu di l'argumenti; in u nostru casu, solu per e espressioni selezziunate seranu micca triviali è duveranu esse dizziunari di e forme colonnes! Cusì, a funzione shrinking pò esse definita cusì:

selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size"); // each это функция map в Q для одного списка
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];

Per a clarità, aghju utilizatu a funzione parse, chì trasforma una stringa cù una espressione Q in un valore chì pò esse passatu à a funzione eval è chì hè necessariu in a funzione selezziunata. Innota ancu chì u preprocessu hè definitu cum'è una prughjezzione (vale à dì, una funzione cù argumenti parzialmente definiti) di a funzione di selezzione, un argumentu (a tavula) manca. Se applichemu preprocessu à una tavula, averemu una tavola compressa.

A seconda tappa hè l'aghjurnamentu di a tavola aggregata. Scrivemu prima l'algoritmu in pseudocode:

for each sym in inputTable
  idx: row index in agg table for sym+currentTime;
  aggTable[idx;`high]: aggTable[idx;`high] | inputTable[sym;`high];
  aggTable[idx;`volume]: aggTable[idx;`volume] + inputTable[sym;`volume];
  …

In Q, hè cumuni di utilizà funzioni di mappa / riduzzione invece di loops. Ma cum'è Q hè una lingua vettoriale è pudemu facilmente applicà tutte l'operazioni à tutti i simboli à una volta, allora à una prima apprussimazione pudemu fà senza un ciclu à tutti, eseguendu operazioni nantu à tutti i simboli à una volta:

idx:calcIdx inputTable;
row:aggTable idx;
aggTable[idx;`high]: row[`high] | inputTable`high;
aggTable[idx;`volume]: row[`volume] + inputTable`volume;
…

Ma pudemu andà più in là, Q hà un operatore unicu è estremamente putente - l'operatore di assignazione generalizata. Permette di cambià un set di valori in una struttura cumplessa di dati utilizendu una lista di indici, funzioni è argumenti. In u nostru casu, pare cusì:

idx:calcIdx inputTable;
rows:aggTable idx;
// .[target;(idx0;idx1;..);function;argument] ~ target[idx 0;idx 1;…]: function[target[idx 0;idx 1;…];argument], в нашем случае функция – это присваивание
.[aggTable;(idx;aggCols);:;flip (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];

Sfurtunatamente, per assignà à una tavula avete bisognu di una lista di fila, micca di culonni, è avete da traspone a matrice (lista di colonne à lista di fila) utilizendu a funzione flip. Questu hè caru per una grande tavola, cusì invece applicà una assignazione generalizata à ogni culonna separatamente, utilizendu a funzione di mappa (chì pare un apostrofu):

.[aggTable;;:;]'[(idx;)each aggCols; (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];

Avemu dinò aduprà a prughjezzione di funzione. Innota ancu chì in Q, creà una lista hè ancu una funzione è pudemu chjamà cù a funzione ogni (mappa) per uttene una lista di listi.

Per assicurà chì l'inseme di culonni calculati ùn hè micca fissu, creeremu dinamicamente l'espressione sopra. Fighjemu prima e funzioni per calculà ogni colonna, usendu a fila è e variàbili inp per riferite à i dati aggregati è input:

aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!
 ("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");

Certi culonni sò spiciali; u so primu valore ùn deve esse calculatu da a funzione. Pudemu determinà chì hè u primu da a colonna fila [`numTrades] - se cuntene 0, allora u valore hè primu. Q hà una funzione selezziunata - ? [Boolean list;list1;list2] - chì selezziunate un valore da a lista 1 o 2 secondu a cundizione in u primu argumentu:

// high -> ?[isFirst;inp`high;row[`high]|inp`high]
// @ - тоже обобщенное присваивание для случая когда индекс неглубокий
@[`aggExpression;specialCols;{[x;y]"?[isFirst;inp`",y,";",x,"]"};string specialCols];

Quì aghju chjamatu una assignazione generalizata cù a mo funzione (una espressione in curly braces). Riceve u valore attuale (u primu argumentu) è un argumentu supplementu, chì aghju passatu in u 4 paràmetru.

Aghjunghjemu i parlanti di a batteria separatamente, postu chì a funzione hè a stessa per elli:

// volume -> row[`volume]+inp`volume
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;

Questa hè una assignazione normale da i standard Q, ma aghju assignatu una lista di valori in una volta. Infine, creemu a funzione principale:

// ":",/:aggExprs ~ map[{":",x};aggExpr] => ":row[`high]|inp`high" присвоим вычисленное значение переменной, потому что некоторые колонки зависят от уже вычисленных значений
// string[cols],'exprs ~ map[,;string[cols];exprs] => "high:row[`high]|inp`high" завершим создание присваивания. ,’ расшифровывается как map[concat]
// ";" sv exprs – String from Vector (sv), соединяет список строк вставляя “;” посредине
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}";

Cù sta espressione, aghju criatu dinamicamente una funzione da una stringa chì cuntene l'espressione chì aghju datu sopra. U risultatu sarà cusì:

{[aggTable;idx;inp] rows:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols ;(cumVolume:row[`cumVolume]+inp`cumVolume;… ; high:?[isFirst;inp`high;row[`high]|inp`high])]}

L'ordine di valutazione di a colonna hè invertitu perchè in Q l'ordine di valutazione hè da diritta à manca.

Avà avemu duie funzioni principali necessarie per i calculi, solu bisognu di aghjunghje un pocu infrastruttura è u serviziu hè prestu.

Passi finali

Avemu funzioni preprocess è updateAgg chì facenu tuttu u travagliu. Ma hè sempre necessariu di assicurà a transizione curretta attraversu minuti è calculà l'indici per l'agregazione. Prima di tuttu, definiscemu a funzione init:

init:{
  tradeAgg:: 0#enlist[initWith]; // создаем пустую типизированную таблицу, enlist превращает словарь в таблицу, а 0# означает взять 0 элементов из нее
  currTime::00:00; // начнем с 0, :: означает, что присваивание в глобальную переменную
  currSyms::`u#`symbol$(); // `u# - превращает список в дерево, для ускорения поиска элементов
  offset::0; // индекс в tradeAgg, где начинается текущая минута 
  rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg; // кэш для последних значений roll колонок, таблица с ключом sym
 }

Definiremu ancu a funzione roll, chì cambierà u minutu attuale:

roll:{[tm]
  if[currTime>tm; :init[]]; // если перевалили за полночь, то просто вызовем init
  rollCache,::offset _ rollColumns#tradeAgg; // обновим кэш – взять roll колонки из aggTable, обрезать, вставить в rollCache
  offset::count tradeAgg;
  currSyms::`u#`$();
 }

Avemu bisognu di una funzione per aghjunghje novi caratteri:

addSyms:{[syms]
  currSyms,::syms; // добавим в список известных
  // добавим в таблицу sym, time и rollColumns воспользовавшись обобщенным присваиванием.
  // Функция ^ подставляет значения по умолчанию для roll колонок, если символа нет в кэше. value flip table возвращает список колонок в таблице.
  `tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime), (initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
 }

È infine, a funzione upd (u nome tradiziunale per sta funzione per i servizii Q), chì hè chjamatu da u cliente per aghjunghje dati:

upd:{[tblName;data] // tblName нам не нужно, но обычно сервис обрабатывает несколько таблиц 
  tm:exec distinct time from data:() xkey preprocess data; // preprocess & calc time
  updMinute[data] each tm; // добавим данные для каждой минуты
};
updMinute:{[data;tm]
  if[tm<>currTime; roll tm; currTime::tm]; // поменяем минуту, если необходимо
  data:select from data where time=tm; // фильтрация
  if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms]; // новые символы
  updateAgg[`tradeAgg;offset+currSyms?syms;data]; // обновим агрегированную таблицу. Функция ? ищет индекс элементов списка справа в списке слева.
 };

Eccu tuttu. Eccu u codice cumpletu di u nostru serviziu, cum'è prumessu, solu uni pochi di linii:

initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time;
rollColumns:`sym`cumVolume;

accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;

selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size");
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];

aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");
@[`aggExpression;specialCols;{"?[isFirst;inp`",y,";",x,"]"};string specialCols];
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}"; / '

init:{
  tradeAgg::0#enlist[initWith];
  currTime::00:00;
  currSyms::`u#`symbol$();
  offset::0;
  rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg;
 };
roll:{[tm]
  if[currTime>tm; :init[]];
  rollCache,::offset _ rollColumns#tradeAgg;
  offset::count tradeAgg;
  currSyms::`u#`$();
 };
addSyms:{[syms]
  currSyms,::syms;
  `tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime),(initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
 };

upd:{[tblName;data] updMinute[data] each exec distinct time from data:() xkey preprocess data};
updMinute:{[data;tm]
  if[tm<>currTime; roll tm; currTime::tm];
  data:select from data where time=tm;
  if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms];
  updateAgg[`tradeAgg;offset+currSyms?syms;data];
 };

Prucessioni

Cuntrollamu a prestazione di u serviziu. Per fà questu, eseguimu in un prucessu separatu (mette u codice in u schedariu service.q) è chjamate a funzione init:

q service.q –p 5566

q)init[]

In una altra cunsola, inizià u sicondu prucessu Q è cunnette à u primu:

h:hopen `:host:5566
h:hopen 5566 // если оба на одном хосте

Prima, creamu una lista di simboli - 10000 XNUMX pezzi è aghjunghje una funzione per creà una tavola aleatoria. In a seconda cunsola:

syms:`IBM`AAPL`GOOG,-9997?`8
rnd:{[n;t] ([] sym:n?syms; time:t+asc n#til 25; price:n?10f; size:n?10)}

Aghju aghjustatu trè simboli veri à a lista per fà più faciule per circà in a tavula. A funzione rnd crea una tabella aleatoria cù n fila, induve u tempu varieghja da t à t + 25 millisecondi.

Avà pudete pruvà à mandà dati à u serviziu (aghjunghje e prime dece ore):

{h (`upd;`trade;rnd[10000;x])} each `time$00:00 + til 60*10

Pudete verificà in u serviziu chì a tavola hè stata aghjurnata:

c 25 200
select from tradeAgg where sym=`AAPL
-20#select from tradeAgg where sym=`AAPL

Risultatu:

sym|time|high|low|firstPrice|lastPrice|firstSize|lastSize|numTrades|volume|pvolume|turnover|avgPrice|avgSize|vwap|cumVolume
--|--|--|--|--|--------------------------------
AAPL|09:27|9.258904|9.258904|9.258904|9.258904|8|8|1|8|9.258904|74.07123|9.258904|8|9.258904|2888
AAPL|09:28|9.068162|9.068162|9.068162|9.068162|7|7|1|7|9.068162|63.47713|9.068162|7|9.068162|2895
AAPL|09:31|4.680449|0.2011121|1.620827|0.2011121|1|5|4|14|9.569556|36.84342|2.392389|3.5|2.631673|2909
AAPL|09:33|2.812535|2.812535|2.812535|2.812535|6|6|1|6|2.812535|16.87521|2.812535|6|2.812535|2915
AAPL|09:34|5.099025|5.099025|5.099025|5.099025|4|4|1|4|5.099025|20.3961|5.099025|4|5.099025|2919

Facemu avà a prova di carica per sapè quantu dati u serviziu pò processà per minutu. Lasciami ricurdà chì avemu stabilitu l'intervallu di aghjurnamentu à 25 millisecondi. In cunsiquenza, u serviziu deve (in media) si mette in almenu 20 millisecondi per aghjurnamentu per dà à l'utilizatori tempu per dumandà dati. Inserite i seguenti in u sicondu prucessu:

tm:10:00:00.000
stressTest:{[n] 1 string[tm]," "; times,::h ({st:.z.T; upd[`trade;x]; .z.T-st};rnd[n;tm]); tm+:25}
start:{[n] times::(); do[4800;stressTest[n]]; -1 " "; `min`avg`med`max!(min times;avg times;med times;max times)}

4800 hè dui minuti. Pudete pruvà à correre prima per 1000 fila ogni 25 millisecondi:

start 1000

In u mo casu, u risultatu hè di circa un paru di millisecondi per aghjurnamentu. Allora aumenteraghju immediatamente u numeru di fila à 10.000 XNUMX:

start 10000

Risultatu:

min| 00:00:00.004
avg| 9.191458
med| 9f
max| 00:00:00.030

À novu, nunda di speciale, ma questu hè 24 milioni di linee per minutu, 400 mila per seconda. Per più di 25 millisecondi, l'aghjurnamentu hà rallentatu solu 5 volte, apparentemente quandu u minutu cambiò. Aumentemu à 100.000:

start 100000

Risultatu:

min| 00:00:00.013
avg| 25.11083
med| 24f
max| 00:00:00.108
q)sum times
00:02:00.532

Comu si pò vede, u serviziu ùn pò à pena affruntà, ma, quantunque, riesci à stà à flottu. Un tali volumi di dati (240 milioni di fila per minutu) hè assai grande; in tali casi, hè cumunu per lancià parechji cloni (o ancu decine di cloni) di u serviziu, ognunu di quali processa solu una parte di i caratteri. Eppuru, u risultatu hè impressiunanti per una lingua interpretata chì si cuncintra principarmenti in u almacenamentu di dati.

A quistione pò esse per quessa chì u tempu cresce non-linearly cù a dimensione di ogni aghjurnamentu. U mutivu hè chì a funzione shrink hè in realtà una funzione C, chì hè assai più efficaci di updateAgg. Partendu da una certa dimensione d'aghjurnamentu (circa 10.000 30), updateAgg righjunghji u so tettu è dopu u so tempu d'esekzione ùn dipende micca da a dimensione di l'aghjurnamentu. Hè duvuta à u passu preliminariu Q chì u serviziu hè capaci di digerisce tali volumi di dati. Questu mette in risaltu quantu hè impurtante di sceglie l'algoritmu ghjustu quandu travaglia cù big data. Un altru puntu hè u almacenamentu currettu di dati in memoria. Se i dati ùn sò micca stati cullucati in colonna o ùn sò micca urdinati per u tempu, allora avemu da esse familiarizatu cù una cosa cum'è un TLB cache miss - l'absenza di una pagina di memoria in a cache di l'indirizzu di u processatore. A ricerca di un indirizzu pigghia circa XNUMX volte più longu s'ellu ùn hè micca successu, è se i dati sò spargugliati, pò rallentà u serviziu parechje volte.

cunchiusioni

In questu articulu, aghju dimustratu chì a basa di dati KDB + è Q sò adattati micca solu per almacenà grandi dati è accede facilmente à traversu selezziunà, ma ancu per creà servizii di trattamentu di dati chì sò capaci di digerisce centinaie di milioni di file / gigabyte di dati ancu in . un unicu prucessu Q. A lingua Q stessu permette una implementazione estremamente concisa è efficiente di algoritmi ligati à u processu di dati per via di a so natura vettoriale, di un interprete di dialettu SQL integratu è di un inseme assai successu di funzioni di biblioteca.

Aghju nutatu chì quì sopra hè solu una parte di ciò chì Q pò fà, hà ancu altre caratteristiche uniche. Per esempiu, un protokollu IPC estremamente simplice chì sguassate u cunfini trà i prucessi Q individuali è vi permette di cumminà centinaie di sti prucessi in una sola reta, chì pò esse situatu nantu à decine di servitori in diverse parti di u mondu.

Source: www.habr.com

Add a comment