Mga bahin sa Q ug KDB+ nga lengguwahe gamit ang ehemplo sa usa ka real-time nga serbisyo

Mahimo nimo mabasa kung unsa ang KDB + base, ang Q programming language, kung unsa ang ilang mga kusog ug kahuyang sa akong nauna. artikulo ug sa mubo sa pasiuna. Sa artikulo, ipatuman namon ang usa ka serbisyo sa Q nga magproseso sa umaabot nga stream sa datos ug makalkulo ang lainlaing mga function sa aggregation matag minuto sa mode nga "tinuod nga oras" (pananglitan, adunay oras sa pagkalkula sa tanan sa wala pa ang sunod nga bahin sa datos). Ang nag-unang bahin sa Q mao nga kini usa ka vector nga pinulongan nga nagtugot kanimo sa pag-operate dili sa usa ka butang, apan sa ilang mga arrays, arrays sa arrays ug uban pang komplikadong mga butang. Ang mga pinulongan sama sa Q ug ang mga paryente niini K, J, APL nabantog sa ilang kamubo. Kasagaran, ang usa ka programa nga adunay daghang mga screen sa code sa usa ka pamilyar nga lengguwahe sama sa Java mahimong isulat sa kanila sa pipila ka linya. Kini ang gusto nakong ipakita niining artikuloha.

Mga bahin sa Q ug KDB+ nga lengguwahe gamit ang ehemplo sa usa ka real-time nga serbisyo

Pasiuna

Ang KDB+ usa ka kolumnar nga database nga naka-focus sa daghan kaayong datos, gi-order sa usa ka espesipikong paagi (panguna sa panahon). Gigamit kini sa panguna sa mga institusyong pinansyal - mga bangko, pondo sa pamuhunan, mga kompanya sa seguro. Ang Q nga pinulongan mao ang internal nga pinulongan sa KDB+ nga nagtugot kanimo sa epektibong pagtrabaho uban niini nga datos. Ang Q ideolohiya kay mub-an ug kahusayan, samtang ang katin-aw gisakripisyo. Gipakamatarung kini sa kamatuoran nga ang vector nga pinulongan mahimong lisud sabton sa bisan unsang kaso, ug ang kamubo ug kadato sa pagrekord nagtugot kanimo nga makita ang usa ka mas dako nga bahin sa programa sa usa ka screen, nga sa katapusan mas sayon ​​​​sabton.

Niini nga artikulo nagpatuman kami usa ka hingpit nga programa sa Q ug mahimo nimong sulayan kini. Aron mahimo kini, kinahanglan nimo ang aktuwal nga Q. Mahimo nimong i-download ang libre nga 32-bit nga bersyon sa website sa kompanya sa kx - www.kx.com. Didto, kung interesado ka, makit-an nimo ang pakisayran nga impormasyon sa Q, ang libro Q Para sa mga Mortal ug lainlaing mga artikulo bahin niini nga hilisgutan.

Pagbuot sa problema

Adunay usa ka tinubdan nga nagpadala usa ka lamesa nga adunay datos matag 25 millisecond. Tungod kay ang KDB+ gigamit sa panguna sa pinansya, atong isipon nga kini usa ka talaan sa mga transaksyon (mga patigayon), nga adunay mosunod nga mga kolum: oras (oras sa milliseconds), sym (pagtudlo sa kompanya sa stock exchange - IBM, AAPL,…), presyo (ang presyo diin gipalit ang mga bahin), gidak-on (gidak-on sa transaksyon). Ang 25 millisecond interval kay arbitraryo, dili kaayo gamay ug dili kaayo taas. Ang presensya niini nagpasabut nga ang datos moabut sa serbisyo nga na-buffer na. Sayon nga ipatuman ang buffering sa service side, lakip ang dinamikong buffering depende sa kasamtangan nga load, apan alang sa kayano, kita mag-focus sa usa ka fixed interval.

Ang serbisyo kinahanglang mag-ihap kada minuto para sa matag umaabot nga simbolo gikan sa sym column usa ka set sa aggregating functions - max price, avg price, sum size, etc. mapuslanon nga impormasyon. Alang sa kayano, atong hunahunaon nga ang tanan nga mga gimbuhaton mahimong kalkulado nga incrementally, i.e. aron makakuha usa ka bag-ong kantidad, igo na nga mahibal-an ang duha nga mga numero - ang daan ug ang umaabot nga mga kantidad. Pananglitan, ang mga function max, average, sum adunay kini nga kabtangan, apan ang median function wala.

Atong hunahunaon usab nga ang umaabot nga data stream gi-order sa oras. Maghatag kini kanamo og higayon nga magtrabaho lamang sa katapusang minuto. Sa praktis, igo na nga makahimo sa pagtrabaho sa karon ug sa miaging mga minuto kung ang pipila nga mga pag-update ulahi na. Alang sa kayano, dili nato tagdon kini nga kaso.

Mga gimbuhaton sa panagsama

Ang gikinahanglan nga aggregation function gilista sa ubos. Gikuha nako ang kadaghanan kanila kutob sa mahimo aron madugangan ang load sa serbisyo:

  • taas – max nga presyo – maximum nga presyo kada minuto.
  • ubos - min nga presyo - minimum nga presyo kada minuto.
  • unang Presyo – unang presyo – unang presyo kada minuto.
  • lastPrice – last price – last price per minute.
  • firstSize - unang gidak-on - unang gidak-on sa trade kada minuto.
  • lastSize – last size β€” last trade size sa usa ka minuto.
  • numTrades – ihap i – gidaghanon sa mga patigayon kada minuto.
  • volume – sum size – sum sa trade size kada minuto.
  • pvolume – sum nga presyo – sum sa mga presyo kada minuto, gikinahanglan para sa avgPrice.
  • - kantidad sa turnover nga presyo * gidak-on - kinatibuk-ang gidaghanon sa mga transaksyon matag minuto.
  • avgPrice – pvolume%numTrades – average nga presyo kada minuto.
  • avgSize – volume%numTrades – kasagarang gidak-on sa trade kada minuto.
  • vwap – turnover%volume – average nga presyo kada minuto nga gitimbang sa gidak-on sa transaksyon.
  • cumVolume – sum volume – natipon nga gidak-on sa mga transaksyon sa tibuok panahon.

Ato dayon nga hisgutan ang usa ka dili klaro nga punto - kung giunsa pagsugod kini nga mga kolum sa unang higayon ug sa matag sunod nga minuto. Ang ubang mga kolum sa una nga tipo sa Presyo kinahanglan nga magsugod sa null matag higayon; ang ilang kantidad dili matino. Ang ubang mga tipo sa volume kinahanglan kanunay nga itakda sa 0. Adunay usab mga kolum nga nanginahanglan usa ka hiniusa nga pamaagi - pananglitan, ang cumVolume kinahanglan nga kopyahon gikan sa miaging minuto, ug alang sa una nga itakda sa 0. Atong ibutang ang tanan niini nga mga parameter gamit ang datos sa diksyonaryo tipo (parehas sa usa ka rekord):

// 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 объяснСн Π½ΠΈΠΆΠ΅

Gidugang nako ang sym ug oras sa diksyonaryo alang sa kasayon, karon ang initWith usa ka andam nga linya gikan sa katapusan nga aggregated nga lamesa, diin kini nagpabilin aron itakda ang husto nga sym ug oras. Mahimo nimo kini gamiton aron makadugang bag-ong mga laray sa usa ka lamesa.

Kinahanglan namon ang aggCols kung maghimo usa ka function sa aggregation. Kinahanglang balit-ad ang lista tungod sa pagkasunod-sunod diin ang mga ekspresyon sa Q gi-evaluate (gikan sa tuo ngadto sa wala). Ang katuyoan mao ang pagsiguro nga ang kalkulasyon gikan sa taas hangtod sa cumVolume, tungod kay ang pipila nga mga kolum nagdepende sa mga nauna.

Ang mga kolum nga kinahanglan kopyahon sa usa ka bag-ong minuto gikan sa miaging usa, ang kolum sa sym gidugang alang sa kasayon:

rollColumns:`sym`cumVolume;

Karon atong bahinon ang mga kolum ngadto sa mga grupo sumala sa unsa nga paagi sila kinahanglan nga updated. Tulo ka matang mahimong mailhan:

  1. Accumulators (volume, turnover,..) - kinahanglan natong idugang ang umaabot nga bili sa nauna.
  2. Uban sa usa ka espesyal nga punto (taas, ubos, ..) - ang unang bili sa minuto gikuha gikan sa umaabot nga datos, ang uban gikalkulo gamit ang function.
  3. Pahulay. Kanunay nga kalkulado gamit ang usa ka function.

Atong ipasabut ang mga variable alang niini nga mga klase:

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

Order sa kalkulasyon

Atong i-update ang aggregated table sa duha ka hugna. Para sa pagkaepisyente, una namong pakunhoran ang umaabot nga lamesa aron adunay usa ra ka laray sa matag karakter ug minuto. Ang kamatuoran nga ang tanan namong mga gimbuhaton incremental ug associative naggarantiya nga ang resulta niining dugang nga lakang dili mausab. Mahimo nimong pakunhuran ang lamesa gamit ang pagpili:

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

Kini nga pamaagi adunay usa ka disbentaha - ang set sa kalkulado nga mga kolum gitakda nang daan. Maayo na lang, sa Q, ang pagpili gipatuman usab isip usa ka function diin mahimo nimong ilisan ang dinamikong gibuhat nga mga argumento:

?[table;whereClause;byClause;selectClause]

Dili nako ihulagway sa detalye ang pormat sa mga argumento; sa among kaso, pinaagi lamang ug pinili nga mga ekspresyon ang dili importante ug kini kinahanglan nga mga diksyonaryo sa porma nga mga kolum! mga ekspresyon. Busa, ang pag-urong nga function mahimong ipasabut ingon sa mosunod:

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];

Alang sa katin-awan, akong gigamit ang parse function, nga nag-ilis sa usa ka string nga adunay Q nga ekspresyon ngadto sa usa ka bili nga mahimong ipasa sa eval function ug nga gikinahanglan sa function select. Timan-i usab nga ang preprocess gihubit ingon usa ka projection (ie, usa ka function nga adunay partially definition nga mga argumento) sa pinili nga function, usa ka argumento (ang lamesa) ang nawala. Kung atong i-apply ang preprocess sa usa ka lamesa, makakuha kita og compressed table.

Ang ikaduhang yugto mao ang pag-update sa aggregated table. Isulat una nato ang algorithm sa 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];
  …

Sa Q, kasagaran ang paggamit sa mapa/pagkunhod sa mga gimbuhaton imbes nga mga galong. Apan tungod kay ang Q usa ka vector nga lengguwahe ug dali namong magamit ang tanan nga mga operasyon sa tanan nga mga simbolo sa usa ka higayon, unya sa una nga pagbanabana mahimo naton nga wala’y usa ka loop, nga nagpahigayon mga operasyon sa tanan nga mga simbolo sa usa ka higayon:

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

Apan makapadayon pa kita, ang Q adunay usa ka talagsaon ug labi ka kusgan nga operator - ang generalized assignment operator. Gitugotan ka nga magbag-o sa usa ka hugpong sa mga kantidad sa usa ka komplikado nga istruktura sa datos gamit ang usa ka lista sa mga indeks, gimbuhaton ug mga argumento. Sa among kaso kini ingon niini:

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;…)];

Ikasubo, aron ma-assign sa usa ka lamesa kinahanglan nimo ang usa ka lista sa mga laray, dili mga kolum, ug kinahanglan nimo nga ibalhin ang matrix (lista sa mga kolum sa lista sa mga linya) gamit ang flip function. Kini mahal alang sa usa ka dako nga lamesa, mao nga sa baylo nag-apply kami usa ka kinatibuk-ang buluhaton sa matag kolum nga gilain, gamit ang function sa mapa (nga morag usa ka apostrophe):

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

Gigamit na usab namo ang function projection. Timan-i usab nga sa Q, ang paghimo og lista usa usab ka function ug mahimo natong tawgon kini gamit ang matag(mapa) function aron makakuha og listahan sa mga lista.

Aron masiguro nga ang hugpong sa mga kalkulado nga mga kolum dili naayo, maghimo kami nga dinamikong ekspresyon sa ibabaw. Atong una nga ipasabut ang mga gimbuhaton aron makalkulo ang matag kolum, gamit ang mga row ug inp nga mga baryable aron magtumong sa giipon ug input nga datos:

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");

Ang pipila ka mga kolum espesyal; ang ilang una nga kantidad kinahanglan dili kalkulado sa function. Atong matino nga kini ang una pinaagi sa row[`numTrades] column - kung kini adunay 0, nan ang bili mao ang una. Ang Q adunay pinili nga function - ?[Boolean list;list1;list2] - nga nagpili ug value gikan sa listahan 1 o 2 depende sa kondisyon sa unang argumento:

// high -> ?[isFirst;inp`high;row[`high]|inp`high]
// @ - Ρ‚ΠΎΠΆΠ΅ ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½Π½ΠΎΠ΅ присваиваниС для случая ΠΊΠΎΠ³Π΄Π° индСкс Π½Π΅Π³Π»ΡƒΠ±ΠΎΠΊΠΈΠΉ
@[`aggExpression;specialCols;{[x;y]"?[isFirst;inp`",y,";",x,"]"};string specialCols];

Dinhi akong gitawag ang usa ka kinatibuk-ang buluhaton sa akong gimbuhaton (usa ka ekspresyon sa mga kulot nga braces). Nakadawat kini sa kasamtangan nga bili (ang unang argumento) ug usa ka dugang nga argumento, nga akong gipasa sa ika-4 nga parameter.

Atong idugang ang mga speaker sa baterya nga gilain, tungod kay ang function parehas alang kanila:

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

Kini usa ka normal nga buluhaton sa mga sumbanan sa Q, apan naghatag ako usa ka lista sa mga kantidad sa usa ka higayon. Sa katapusan, maghimo kita sa panguna nga function:

// ":",/: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),")]}";

Uban niini nga ekspresyon, ako dinamikong naghimo sa usa ka function gikan sa usa ka hilo nga naglangkob sa ekspresyon nga akong gihatag sa ibabaw. Ang resulta mahimong sama niini:

{[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])]}

Ang han-ay sa ebalwasyon sa kolum gibalit-ad tungod kay sa Q ang han-ay sa ebalwasyon gikan sa tuo ngadto sa wala.

Karon kami adunay duha ka panguna nga mga gimbuhaton nga gikinahanglan alang sa mga kalkulasyon, kinahanglan lang namon nga magdugang usa ka gamay nga imprastraktura ug andam na ang serbisyo.

Katapusan nga mga lakang

Kami adunay preprocess ug updateAgg nga mga gimbuhaton nga naghimo sa tanan nga trabaho. Apan kinahanglan gihapon nga masiguro ang husto nga pagbalhin sa mga minuto ug kuwentahon ang mga indeks alang sa pagtipon. Una sa tanan, atong ipasabut ang init function:

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
 }

Atong ipasabut usab ang function sa roll, nga magbag-o sa karon nga minuto:

roll:{[tm]
  if[currTime>tm; :init[]]; // Ссли ΠΏΠ΅Ρ€Π΅Π²Π°Π»ΠΈΠ»ΠΈ Π·Π° ΠΏΠΎΠ»Π½ΠΎΡ‡ΡŒ, Ρ‚ΠΎ просто Π²Ρ‹Π·ΠΎΠ²Π΅ΠΌ init
  rollCache,::offset _ rollColumns#tradeAgg; // ΠΎΠ±Π½ΠΎΠ²ΠΈΠΌ кэш – Π²Π·ΡΡ‚ΡŒ roll ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ ΠΈΠ· aggTable, ΠΎΠ±Ρ€Π΅Π·Π°Ρ‚ΡŒ, Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Π² rollCache
  offset::count tradeAgg;
  currSyms::`u#`$();
 }

Kinahanglan namon ang usa ka function aron makadugang bag-ong mga karakter:

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)];
 }

Ug sa katapusan, ang upd function (ang tradisyonal nga ngalan alang niini nga function alang sa Q nga mga serbisyo), nga gitawag sa kliyente sa pagdugang data:

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]; // ΠΎΠ±Π½ΠΎΠ²ΠΈΠΌ Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Π½ΡƒΡŽ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ. Ѐункция ? ΠΈΡ‰Π΅Ρ‚ индСкс элСмСнтов списка справа Π² спискС слСва.
 };

Mao ra. Ania ang kompleto nga code sa among serbisyo, ingon sa gisaad, pipila lang ka linya:

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];
 };

Pagsulay

Atong susihon ang performance sa serbisyo. Aron mahimo kini, atong padaganon kini sa lain nga proseso (ibutang ang code sa service.q file) ug tawagan ang init function:

q service.q –p 5566

q)init[]

Sa laing console, sugdi ang ikaduhang proseso sa Q ug ikonektar ang una:

h:hopen `:host:5566
h:hopen 5566 // Ссли ΠΎΠ±Π° Π½Π° ΠΎΠ΄Π½ΠΎΠΌ хостС

Una, maghimo kita og lista sa mga simbolo - 10000 ka piraso ug magdugang og function aron makahimo og random nga lamesa. Sa ikaduhang console:

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

Gidugang nako ang tulo ka tinuod nga mga simbolo sa lista aron mas sayon ​​​​ang pagpangita niini sa lamesa. Ang rnd function nagmugna og random table nga adunay n row, diin ang oras magkalahi gikan sa t ngadto sa t+25 milliseconds.

Karon mahimo nimong sulayan ang pagpadala sa datos sa serbisyo (idugang ang una nga napulo ka oras):

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

Mahimo nimong susihon sa serbisyo nga ang lamesa na-update:

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

Resulta:

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

Ipahigayon na nato ang load testing aron masayran kung pila ka data ang maproseso sa serbisyo kada minuto. Pahinumdum ko nimo nga among gitakda ang agwat sa pag-update sa 25 milliseconds. Tungod niini, ang serbisyo kinahanglan (sa aberids) mohaum sa labing menos 20 milliseconds matag update aron mahatagan ang mga tiggamit og panahon sa paghangyo og datos. Pagsulod sa mosunod sa ikaduhang proseso:

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)}

Ang 4800 duha ka minuto. Mahimo nimong sulayan ang pagdagan una alang sa 1000 ka laray matag 25 millisecond:

start 1000

Sa akong kaso, ang resulta hapit sa usa ka magtiayon nga milliseconds matag update. Busa akong dugangan dayon ang gidaghanon sa mga laray ngadto sa 10.000:

start 10000

Resulta:

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

Sa makausa pa, walay espesyal, apan kini mao ang 24 milyon nga linya matag minuto, 400 ka libo matag segundo. Sulod sa labaw sa 25 millisecond, ang pag-update mihinay lamang sa 5 ka beses, dayag sa dihang nausab ang minuto. Atong dugangan ngadto sa 100.000:

start 100000

Resulta:

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

Sama sa imong nakita, ang serbisyo halos dili makasagubang, apan bisan pa niana, kini nagpabilin nga naglutaw. Ang ingon nga gidaghanon sa datos (240 milyon nga mga laray matag minuto) dako kaayo; sa ingon nga mga kaso, kasagaran ang paglansad sa daghang mga clone (o bisan dose-dosenang mga clone) sa serbisyo, nga ang matag usa nagproseso sa bahin lamang sa mga karakter. Bisan pa, ang resulta makapahingangha alang sa usa ka gihubad nga sinultian nga nagpunting sa panguna sa pagtipig sa datos.

Mahimong motungha ang pangutana kung ngano nga ang oras motubo nga dili linya sa gidak-on sa matag update. Ang hinungdan mao nga ang pag-urong nga function sa tinuud usa ka C function, nga labi ka episyente kaysa updateAgg. Sugod gikan sa usa ka piho nga gidak-on sa pag-update (mga 10.000), ang updateAgg moabot sa kisame niini ug unya ang oras sa pagpatuman niini wala magdepende sa gidak-on sa pag-update. Kini tungod sa pasiuna nga lakang Q nga ang serbisyo makahimo sa paghilis sa ingon nga mga volume sa datos. Gipasiugda niini kung unsa ka hinungdanon ang pagpili sa husto nga algorithm kung nagtrabaho uban ang dagkong datos. Ang laing punto mao ang husto nga pagtipig sa datos sa memorya. Kung ang datos wala gitipigan nga kolumnar o wala gimando sa oras, nan pamilyar kami sa usa ka butang sama sa usa ka TLB cache miss - ang pagkawala sa usa ka address sa panid sa panumduman sa cache sa address sa processor. Ang pagpangita alang sa usa ka adres nagkinahanglan ug mga 30 ka pilo nga mas dugay kung dili molampos, ug kung ang datos nagkatag, kini makapahinay sa serbisyo sa daghang mga higayon.

konklusyon

Niini nga artikulo, akong gipakita nga ang KDB+ ug Q database angayan dili lamang sa pagtipig sa dagkong datos ug dali nga ma-access kini pinaagi sa pagpili, kondili alang usab sa paghimo sa mga serbisyo sa pagproseso sa datos nga makahimo sa paghilis sa gatusan ka milyon nga mga laray/gigabytes sa datos bisan sa usa ka proseso sa Q. Ang Q nga lengguwahe mismo nagtugot alang sa hilabihan ka mubo ug episyente nga pagpatuman sa mga algorithm nga may kalabutan sa pagproseso sa datos tungod sa kinaiya niini nga vector, built-in nga SQL dialect interpreter ug usa ka malampuson nga set sa library functions.

Akong timan-an nga ang naa sa ibabaw bahin lang sa mahimo sa Q, kini adunay uban pang talagsaon nga mga bahin usab. Pananglitan, usa ka hilabihan ka yano nga protocol sa IPC nga nagwagtang sa utlanan tali sa indibidwal nga mga proseso sa Q ug nagtugot kanimo sa paghiusa sa gatusan niini nga mga proseso ngadto sa usa ka network, nga mahimong mahimutang sa dosena nga mga server sa lain-laing mga bahin sa kalibutan.

Source: www.habr.com

Idugang sa usa ka comment