Karatteristiċi tal-lingwa Q u KDB+ bl-użu ta 'eżempju ta' servizz f'ħin reali

Tista' taqra dwar x'inhuma l-bażi KDB+, il-lingwa ta' programmar Q, x'inhuma s-saħħiet u d-dgħufijiet tagħhom fil-preċedenti tiegħi artikolu u fil-qosor fl-introduzzjoni. Fl-artiklu, se nimplimentaw servizz fuq Q li se jipproċessa n-nixxiegħa tad-dejta li dieħla u jikkalkula diversi funzjonijiet ta 'aggregazzjoni kull minuta fil-modalità "ħin reali" (jiġifieri, se jkollu ħin biex jikkalkula kollox qabel il-porzjon ta' dejta li jmiss). Il-karatteristika ewlenija ta 'Q hija li hija lingwa vettur li tippermettilek li topera mhux ma' oġġetti singoli, iżda bl-arrays tagħhom, arrays ta 'arrays u oġġetti kumplessi oħra. Lingwi bħal Q u l-qraba tiegħu K, J, APL huma famużi għall-qosor tagħhom. Ħafna drabi, programm li jieħu diversi skrins ta 'kodiċi f'lingwa familjari bħal Java jista' jinkiteb fuqhom fi ftit linji. Dan huwa dak li rrid nuri f'dan l-artikolu.

Karatteristiċi tal-lingwa Q u KDB+ bl-użu ta 'eżempju ta' servizz f'ħin reali

Introduzzjoni

KDB+ hija database ta' kolonni ffukata fuq ammonti kbar ħafna ta' dejta, ordnata b'mod speċifiku (primarjament skond iż-żmien). Jintuża primarjament f'istituzzjonijiet finanzjarji - banek, fondi ta 'investiment, kumpaniji tal-assigurazzjoni. Il-lingwa Q hija l-lingwa interna tal-KDB+ li tippermettilek taħdem b'mod effettiv b'din id-dejta. L-ideoloġija Q hija qosor u effiċjenza, filwaqt li ċ-ċarezza hija sagrifikata. Dan huwa ġġustifikat mill-fatt li l-lingwa tal-vettur se tkun diffiċli biex tinftiehem fi kwalunkwe każ, u l-qosor u r-rikkezza tar-reġistrazzjoni jippermettulek tara parti ħafna akbar tal-programm fuq skrin wieħed, li fl-aħħar mill-aħħar jagħmilha aktar faċli biex tifhem.

F'dan l-artikolu nimplimentaw programm sħiħ f'Q u tista' tkun trid tipprovah. Biex tagħmel dan, ser ikollok bżonn il-Q attwali. Tista' tniżżel il-verżjoni b'xejn 32-bit fuq il-websajt tal-kumpanija kx – www.kx.com. Hemmhekk, jekk inti interessat, issib informazzjoni ta’ referenza fuq Q, il-ktieb Q Għall Mortals u diversi artikli dwar dan is-suġġett.

Dikjarazzjoni tal-problema

Hemm sors li jibgħat tabella bid-dejta kull 25 millisekondi. Peress li KDB+ jintuża primarjament fil-finanzi, se nassumu li din hija tabella ta 'tranżazzjonijiet (kummerċ), li għandha l-kolonni li ġejjin: ħin (ħin f'millisekondi), sym (denominazzjoni tal-kumpanija fil-borża - IBM, AAPL,…), il-prezz (il-prezz li bih inxtraw l-ishma), id-daqs (id-daqs tat-tranżazzjoni). L-intervall ta '25 millisekonda huwa arbitrarju, mhux żgħir wisq u mhux twil wisq. Il-preżenza tagħha tfisser li d-data tasal għas-servizz diġà buffered. Ikun faċli li timplimenta buffering fuq in-naħa tas-servizz, inkluż buffering dinamiku skont it-tagħbija attwali, iżda għas-sempliċità, aħna se niffukaw fuq intervall fiss.

Is-servizz għandu jgħodd kull minuta għal kull simbolu li jidħol mill-kolonna sym sett ta 'funzjonijiet aggregati - prezz massimu, prezz medju, daqs tas-somma, eċċ. informazzjoni utli. Għas-sempliċità, se nassumu li l-funzjonijiet kollha jistgħu jiġu kkalkulati b'mod inkrementali, i.e. biex tikseb valur ġdid, huwa biżżejjed li tkun taf żewġ numri - il-valuri qodma u l-valuri deħlin. Pereżempju, il-funzjonijiet max, medja, somma għandhom din il-proprjetà, iżda l-funzjoni medjana le.

Se nassumu wkoll li n-nixxiegħa tad-dejta li tidħol hija ordnata fil-ħin. Dan jagħtina l-opportunità li naħdmu biss fl-aħħar minuta. Fil-prattika, huwa biżżejjed li tkun tista 'taħdem mal-minuti kurrenti u ta' qabel f'każ li xi aġġornamenti jkunu tard. Għal sempliċità, mhux se nikkunsidraw dan il-każ.

Funzjonijiet ta' aggregazzjoni

Il-funzjonijiet ta' aggregazzjoni meħtieġa huma elenkati hawn taħt. Ħadt kemm jista 'jkun biex inżid it-tagħbija fuq is-servizz:

  • għoli – prezz massimu – prezz massimu għal kull minuta.
  • baxx – prezz minimu – prezz minimu kull minuta.
  • firstPrice – l-ewwel prezz – l-ewwel prezz kull minuta.
  • lastPrice – l-aħħar prezz – l-aħħar prezz kull minuta.
  • firstSize – l-ewwel daqs – l-ewwel daqs tal-kummerċ kull minuta.
  • lastSize - l-aħħar daqs - l-aħħar daqs tal-kummerċ f'minuta.
  • numTrades - għadd i - numru ta 'snajja' kull minuta.
  • volum – daqs tas-somma – somma tad-daqsijiet tal-kummerċ kull minuta.
  • pvolume – sum price – somma tal-prezzijiet kull minuta, meħtieġa għal avgPrice.
  • – somma tal-prezz tal-bejgħ* daqs – volum totali ta’ tranżazzjonijiet kull minuta.
  • avgPrice – pvolume%numTrades – prezz medju kull minuta.
  • avgSize – volum%numTrades – daqs medju tal-kummerċ kull minuta.
  • vwap – fatturat%volum – prezz medju kull minuta peżat skond id-daqs tat-tranżazzjoni.
  • cumVolume – volum tas-somma – daqs akkumulat tat-tranżazzjonijiet matul iż-żmien kollu.

Ejja niddiskutu immedjatament punt wieħed mhux ovvju - kif inizjalizza dawn il-kolonni għall-ewwel darba u għal kull minuta sussegwenti. Xi kolonni tat-tip firstPrice għandhom jiġu inizjalizzati għal null kull darba; il-valur tagħhom mhuwiex definit. Tipi ta' volum oħra għandhom dejjem ikunu ssettjati għal 0. Hemm ukoll kolonni li jeħtieġu approċċ kombinat - pereżempju, cumVolume għandu jiġi kkupjat mill-minuta ta 'qabel, u għall-ewwel wieħed issettjat għal 0. Ejja nissettjaw dawn il-parametri kollha billi tuża d-dejta tad-dizzjunarju tip (analogu għal 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 объяснен ниже

Żidt is-sym u l-ħin mad-dizzjunarju għall-konvenjenza, issa initWith hija linja lesta mit-tabella aggregata finali, fejn fadal biex tissettja s-sym u l-ħin korretti. Tista' tużaha biex iżżid ringieli ġodda ma' tabella.

Ikollna bżonn aggCols meta noħolqu funzjoni ta 'aggregazzjoni. Il-lista trid tiġi maqluba minħabba l-ordni li fiha l-espressjonijiet f'Q huma evalwati (mill-lemin għax-xellug). L-għan huwa li jiġi żgurat li l-kalkolu jmur minn għoli għal cumVolume, peress li xi kolonni jiddependu minn dawk preċedenti.

Kolonni li jeħtieġ li jiġu kkupjati għal minuta ġdida minn dik preċedenti, il-kolonna sym hija miżjuda għall-konvenjenza:

rollColumns:`sym`cumVolume;

Issa ejja naqsmu l-kolonni fi gruppi skond kif għandhom jiġu aġġornati. Jistgħu jiġu distinti tliet tipi:

  1. Akkumulaturi (volum, fatturat, ..) - irridu nżidu l-valur li jidħol ma 'dak ta' qabel.
  2. B'punt speċjali (għoli, baxx, ..) - l-ewwel valur fil-minuta jittieħed mid-dejta li tidħol, il-bqija huma kkalkulati bl-użu tal-funzjoni.
  3. Mistrieħ. Dejjem ikkalkulat billi tuża funzjoni.

Ejja niddefinixxu varjabbli għal dawn il-klassijiet:

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

Ordni ta' kalkolu

Se naġġornaw it-tabella aggregata f'żewġ stadji. Għall-effiċjenza, aħna l-ewwel iċċekken it-tabella li tidħol sabiex ikun hemm biss ringiela waħda għal kull karattru u minuta. Il-fatt li l-funzjonijiet kollha tagħna huma inkrementali u assoċjati jiggarantixxi li r-riżultat ta 'dan il-pass addizzjonali mhux se jinbidel. Tista 'tiċkien it-tabella billi tuża tagħżel:

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

Dan il-metodu għandu żvantaġġ - is-sett ta 'kolonni kkalkulati huwa predefinit. Fortunatament, f'Q, select hija implimentata wkoll bħala funzjoni fejn tista' tissostitwixxi argumenti maħluqa dinamikament:

?[table;whereClause;byClause;selectClause]

Mhux se niddeskrivi fid-dettall il-format tal-argumenti; fil-każ tagħna, biss minn u espressjonijiet magħżula se jkunu mhux trivjali u għandhom ikunu dizzjunarji tal-forma kolonni!espressjonijiet. Għalhekk, il-funzjoni li tiċkien tista 'tiġi definita kif ġej:

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

Għaċ-ċarezza, użajt il-funzjoni parse, li ddawwar string b'espressjoni Q f'valur li jista 'jiġi mgħoddi lill-funzjoni eval u li hija meħtieġa fil-funzjoni tagħżel. Innota wkoll li l-preproċess huwa definit bħala projezzjoni (jiġifieri, funzjoni b'argumenti parzjalment definiti) tal-funzjoni tal-għażla, argument wieħed (it-tabella) huwa nieqes. Jekk napplikaw preproċess għal tabella, se nġibu tabella kkompressata.

It-tieni stadju huwa l-aġġornament tat-tabella aggregata. Ejja l-ewwel niktbu l-algoritmu fil-psewdocode:

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

F'Q, huwa komuni li tuża funzjonijiet ta 'mappa/jnaqqas minflok loops. Iżda peress li Q hija lingwa vettur u nistgħu faċilment napplikaw l-operazzjonijiet kollha għas-simboli kollha f'daqqa, allura għall-ewwel approssimazzjoni nistgħu nagħmlu mingħajr loop, billi nwettqu operazzjonijiet fuq is-simboli kollha f'daqqa:

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

Imma nistgħu mmorru aktar 'il quddiem, Q għandha operatur uniku u estremament qawwi - l-operatur ta' assenjazzjoni ġeneralizzat. Jippermettilek tibdel sett ta 'valuri fi struttura ta' data kumplessa billi tuża lista ta 'indiċi, funzjonijiet u argumenti. Fil-każ tagħna jidher bħal dan:

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

Sfortunatament, biex tassenja lil tabella għandek bżonn lista ta 'ringieli, mhux kolonni, u trid tittrasponi l-matriċi (lista ta' kolonni għal lista ta 'ringieli) billi tuża l-funzjoni flip. Dan huwa għali għal tabella kbira, għalhekk minflok napplikaw assenjazzjoni ġeneralizzata għal kull kolonna separatament, bl-użu tal-funzjoni tal-mappa (li tidher qisha appostrofu):

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

Aħna għal darb'oħra nużaw il-projezzjoni tal-funzjoni. Innota wkoll li f'Q, il-ħolqien ta 'lista hija wkoll funzjoni u nistgħu nsejħulha billi tuża l-funzjoni kull (mappa) biex tikseb lista ta' listi.

Biex niżguraw li s-sett ta 'kolonni kkalkulati ma jkunx iffissat, aħna se noħolqu l-espressjoni ta' hawn fuq b'mod dinamiku. Ejja l-ewwel niddefinixxu funzjonijiet biex nikkalkulaw kull kolonna, billi tuża r-ringiela u l-varjabbli inp biex tirreferi għad-dejta aggregata u tal-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");

Xi kolonni huma speċjali; l-ewwel valur tagħhom m'għandux jiġi kkalkulat mill-funzjoni. Nistgħu niddeterminaw li hija l-ewwel bir-ringiela [`numTrades] kolonna - jekk fiha 0, allura l-valur huwa l-ewwel. Q għandha funzjoni ta' għażla - ?[Boolean list;list1;list2] - li tagħżel valur mil-lista 1 jew 2 skont il-kundizzjoni fl-ewwel argument:

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

Hawnhekk sejjaħt assenjazzjoni ġeneralizzata bil-funzjoni tiegħi (espressjoni fi ċineg kaboċċi). Jirċievi l-valur kurrenti (l-ewwel argument) u argument addizzjonali, li ngħaddi fir-4 parametru.

Ejja nżidu l-kelliema tal-batteriji separatament, peress li l-funzjoni hija l-istess għalihom:

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

Din hija assenjazzjoni normali mill-istandards Q, iżda qed nassenja lista ta 'valuri f'daqqa. Fl-aħħarnett, ejja noħolqu l-funzjoni ewlenija:

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

B'din l-espressjoni, noħloq dinamikament funzjoni minn string li fiha l-espressjoni li tajt hawn fuq. Ir-riżultat se jidher bħal dan:

{[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-ordni tal-valutazzjoni tal-kolonna hija maqluba minħabba li f'Q l-ordni tal-evalwazzjoni hija mil-lemin għax-xellug.

Issa għandna żewġ funzjonijiet ewlenin meħtieġa għall-kalkoli, irridu biss inżidu ftit infrastruttura u s-servizz ikun lest.

Passi finali

Għandna funzjonijiet preprocess u updateAgg li jagħmlu x-xogħol kollu. Iżda xorta huwa meħtieġ li tiġi żgurata t-tranżizzjoni korretta permezz ta 'minuti u tikkalkula l-indiċi għall-aggregazzjoni. L-ewwelnett, ejja niddefinixxu l-funzjoni 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
 }

Se niddefinixxu wkoll il-funzjoni tar-roll, li se tbiddel il-minuta kurrenti:

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

Ikollna bżonn funzjoni biex inżidu karattri ġodda:

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

U fl-aħħarnett, il-funzjoni upd (l-isem tradizzjonali għal din il-funzjoni għas-servizzi Q), li tissejjaħ mill-klijent biex iżżid id-dejta:

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]; // обновим агрегированную таблицу. Функция ? ищет индекс элементов списка справа в списке слева.
 };

Dak kollox. Hawn hu l-kodiċi sħiħ tas-servizz tagħna, kif imwiegħed, ftit linji biss:

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

Ittestjar

Ejja niċċekkjaw il-prestazzjoni tas-servizz. Biex tagħmel dan, ejja nħaddmuh fi proċess separat (poġġi l-kodiċi fil-fajl service.q) u sejjaħ il-funzjoni init:

q service.q –p 5566

q)init[]

F'console ieħor, ibda t-tieni proċess Q u qabbad mal-ewwel:

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

L-ewwel, ejja noħolqu lista ta 'simboli - 10000 biċċa u żid funzjoni biex toħloq tabella każwali. Fit-tieni 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)}

Żidt tliet simboli reali mal-lista biex tagħmilha aktar faċli li tfittexhom fit-tabella. Il-funzjoni rnd toħloq tabella każwali b'n ringieli, fejn il-ħin ivarja minn t sa t + 25 millisekondi.

Issa tista' tipprova tibgħat data lis-servizz (żid l-ewwel għaxar sigħat):

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

Tista' tiċċekkja fis-servizz li t-tabella ġiet aġġornata:

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

Riżultat:

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

Ejja issa nwettqu ttestjar tat-tagħbija biex insiru nafu kemm jista' jipproċessa s-servizz kull minuta. Ħa nfakkarkom li aħna nissettjaw l-intervall tal-aġġornament għal 25 millisekonda. Għaldaqstant, is-servizz għandu (bħala medja) jidħol f'mill-inqas 20 millisekondi għal kull aġġornament biex jagħti lill-utenti ħin biex jitolbu d-dejta. Daħħal dan li ġej fit-tieni proċess:

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 huma żewġ minuti. Tista' tipprova taħdem l-ewwel għal 1000 ringiela kull 25 millisekonda:

start 1000

Fil-każ tiegħi, ir-riżultat huwa madwar ftit millisekondi għal kull aġġornament. Allura immedjatament inżid in-numru ta 'ringieli għal 10.000:

start 10000

Riżultat:

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

Għal darb'oħra, xejn speċjali, iżda dan huwa 24 miljun linja kull minuta, 400 elf kull sekonda. Għal aktar minn 25 millisekondi, l-aġġornament naqas biss 5 darbiet, apparentement meta nbidlet il-minuta. Ejja nżidu għal 100.000:

start 100000

Riżultat:

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

Kif tistgħu taraw, is-servizz bilkemm jista’ jlaħħaq, iżda madankollu jirnexxielu jibqa’ għaddej. Tali volum ta 'dejta (240 miljun ringieli kull minuta) huwa estremament kbir; f'każijiet bħal dawn, huwa komuni li jitniedu diversi kloni (jew anke għexieren ta 'kloni) tas-servizz, li kull wieħed minnhom jipproċessa biss parti mill-karattri. Xorta waħda, ir-riżultat huwa impressjonanti għal lingwa interpretata li tiffoka primarjament fuq il-ħażna tad-dejta.

Tista' tqum il-mistoqsija għaliex iż-żmien jikber b'mod mhux lineari mad-daqs ta' kull aġġornament. Ir-raġuni hija li l-funzjoni shrink hija fil-fatt funzjoni C, li hija ħafna aktar effiċjenti minn updateAgg. Jibda minn ċertu daqs ta 'aġġornament (madwar 10.000), updateAgg jilħaq il-limitu massimu tiegħu u mbagħad il-ħin ta' eżekuzzjoni tiegħu ma jiddependix fuq id-daqs tal-aġġornament. Huwa minħabba l-pass preliminari Q li s-servizz huwa kapaċi jiddiġerixxi tali volumi ta 'data. Dan jenfasizza kemm huwa importanti li tagħżel l-algoritmu t-tajjeb meta taħdem ma 'big data. Punt ieħor huwa l-ħażna korretta tad-data fil-memorja. Jekk id-dejta ma kinitx maħżuna b'mod kolonni jew ma kinitx ordnata biż-żmien, allura nsiru familjari ma 'ħaġa bħal TLB cache miss - in-nuqqas ta' indirizz tal-paġna tal-memorja fil-cache tal-indirizz tal-proċessur. It-tfittxija għal indirizz tieħu madwar 30 darba itwal jekk ma tirnexxix, u jekk id-dejta tkun imxerrda, tista 'tnaqqas is-servizz diversi drabi.

Konklużjoni

F'dan l-artikolu, wrejt li d-database KDB+ u Q huma adattati mhux biss biex jaħżnu dejta kbira u jaċċessawha faċilment permezz ta' selezzjoni, iżda wkoll biex joħolqu servizzi ta' pproċessar ta 'dejta li huma kapaċi jiddiġerixxu mijiet ta' miljuni ta 'ringieli / gigabytes ta' dejta anke f'dan l-artikolu. proċess wieħed Q wieħed. Il-lingwa Q nnifisha tippermetti implimentazzjoni estremament konċiża u effiċjenti ta 'algoritmi relatati mal-ipproċessar tad-dejta minħabba n-natura vector tagħha, interpretu djalett SQL integrat u sett ta' funzjonijiet ta 'librerija ta' suċċess kbir.

Se ninnota li dan ta 'hawn fuq huwa biss parti minn dak li jista' jagħmel Q, għandu karatteristiċi uniċi oħra wkoll. Pereżempju, protokoll IPC estremament sempliċi li jħassar il-konfini bejn proċessi Q individwali u jippermettilek tgħaqqad mijiet ta 'dawn il-proċessi f'netwerk wieħed, li jista' jkun jinsab fuq għexieren ta 'servers f'partijiet differenti tad-dinja.

Sors: www.habr.com

Żid kumment