நிகழ்நேர சேவையின் உதாரணத்தைப் பயன்படுத்தி Q மற்றும் KDB+ மொழியின் அம்சங்கள்

KDB+ அடிப்படை, Q நிரலாக்க மொழி என்ன, அவற்றின் பலம் மற்றும் பலவீனம் என்ன என்பதைப் பற்றி எனது முந்தையவற்றில் நீங்கள் படிக்கலாம் கட்டுரை மற்றும் சுருக்கமாக அறிமுகத்தில். கட்டுரையில், உள்வரும் தரவு ஸ்ட்ரீமை செயலாக்கும் மற்றும் பல்வேறு திரட்டல் செயல்பாடுகளை ஒவ்வொரு நிமிடமும் "நிகழ்நேர" பயன்முறையில் கணக்கிடும் ஒரு சேவையை Q இல் செயல்படுத்துவோம் (அதாவது, தரவின் அடுத்த பகுதிக்கு முன் எல்லாவற்றையும் கணக்கிட நேரம் கிடைக்கும்). Q இன் முக்கிய அம்சம் என்னவென்றால், இது ஒரு திசையன் மொழியாகும், இது ஒற்றை பொருள்களுடன் அல்ல, ஆனால் அவற்றின் வரிசைகள், வரிசைகளின் வரிசைகள் மற்றும் பிற சிக்கலான பொருள்களுடன் செயல்பட அனுமதிக்கிறது. கே மற்றும் அதன் உறவினர்களான கே, ஜே, ஏபிஎல் போன்ற மொழிகள் அவற்றின் சுருக்கத்திற்கு பிரபலமானவை. பெரும்பாலும், ஜாவா போன்ற பழக்கமான மொழியில் குறியீட்டின் பல திரைகளை எடுக்கும் ஒரு நிரலை சில வரிகளில் எழுதலாம். இந்தக் கட்டுரையில் நான் நிரூபிக்க விரும்புவது இதுதான்.

நிகழ்நேர சேவையின் உதாரணத்தைப் பயன்படுத்தி Q மற்றும் KDB+ மொழியின் அம்சங்கள்

அறிமுகம்

KDB+ என்பது ஒரு நெடுவரிசை தரவுத்தளமாகும், இது மிகப் பெரிய அளவிலான தரவுகளில் கவனம் செலுத்துகிறது, இது ஒரு குறிப்பிட்ட வழியில் வரிசைப்படுத்தப்படுகிறது (முதன்மையாக நேரம்). இது முதன்மையாக நிதி நிறுவனங்களில் பயன்படுத்தப்படுகிறது - வங்கிகள், முதலீட்டு நிதிகள், காப்பீட்டு நிறுவனங்கள். Q மொழி என்பது KDB+ இன் உள் மொழியாகும், இது இந்தத் தரவுடன் திறம்பட செயல்பட உங்களை அனுமதிக்கிறது. Q சித்தாந்தம் சுருக்கம் மற்றும் செயல்திறன், அதே நேரத்தில் தெளிவு தியாகம் செய்யப்படுகிறது. திசையன் மொழியைப் புரிந்துகொள்வது கடினமாக இருக்கும் என்பதன் மூலம் இது நியாயப்படுத்தப்படுகிறது, மேலும் பதிவின் சுருக்கமும் செழுமையும் நிரலின் மிகப் பெரிய பகுதியை ஒரே திரையில் பார்க்க உங்களை அனுமதிக்கிறது, இது இறுதியில் புரிந்துகொள்வதை எளிதாக்குகிறது.

இந்த கட்டுரையில் நாங்கள் Q இல் ஒரு முழு அளவிலான திட்டத்தை செயல்படுத்துகிறோம், நீங்கள் அதை முயற்சிக்க விரும்பலாம். இதைச் செய்ய, உங்களுக்கு உண்மையான Q தேவைப்படும். kx நிறுவனத்தின் இணையதளத்தில் இலவச 32-பிட் பதிப்பைப் பதிவிறக்கலாம் – www.kx.com. அங்கு, நீங்கள் ஆர்வமாக இருந்தால், Q, புத்தகம் பற்றிய குறிப்புத் தகவலைக் காணலாம் மனிதர்களுக்கான கே மற்றும் இந்த தலைப்பில் பல்வேறு கட்டுரைகள்.

பிரச்சனை அறிக்கை

ஒவ்வொரு 25 மில்லி விநாடிகளுக்கும் தரவுகளுடன் ஒரு அட்டவணையை அனுப்பும் ஆதாரம் உள்ளது. KDB+ முதன்மையாக நிதியில் பயன்படுத்தப்படுவதால், இது பரிவர்த்தனைகளின் (வர்த்தகங்கள்) அட்டவணை என்று கருதுவோம், அதில் பின்வரும் நெடுவரிசைகள் உள்ளன: நேரம் (மில்லி விநாடிகளில் நேரம்), சிம் (பங்குச் சந்தையில் நிறுவனத்தின் பதவி - ஐபிஎம், AAPL,...), விலை (பங்குகள் வாங்கப்பட்ட விலை), அளவு (பரிவர்த்தனையின் அளவு). 25 மில்லி விநாடி இடைவெளி தன்னிச்சையானது, மிகச் சிறியது மற்றும் மிக நீளமானது அல்ல. அதன் இருப்பு என்பது ஏற்கனவே இடையகப்படுத்தப்பட்ட சேவைக்கு தரவு வருகிறது என்பதாகும். தற்போதைய சுமையைப் பொறுத்து டைனமிக் பஃபரிங் உட்பட, சேவைப் பக்கத்தில் இடையகத்தை செயல்படுத்துவது எளிதாக இருக்கும், ஆனால் எளிமைக்காக, நாங்கள் ஒரு நிலையான இடைவெளியில் கவனம் செலுத்துவோம்.

சிம் நெடுவரிசையில் இருந்து உள்வரும் ஒவ்வொரு குறியீட்டிற்கும் ஒவ்வொரு நிமிடத்தையும் சேவையானது ஒருங்கிணைக்கும் செயல்பாடுகளின் தொகுப்பைக் கணக்கிட வேண்டும் - அதிகபட்ச விலை, சராசரி விலை, தொகை அளவு போன்றவை. பயனுள்ள தகவல். எளிமைக்காக, அனைத்து செயல்பாடுகளையும் படிப்படியாக கணக்கிட முடியும் என்று கருதுவோம், அதாவது. ஒரு புதிய மதிப்பைப் பெற, இரண்டு எண்களை அறிந்தால் போதும் - பழைய மற்றும் உள்வரும் மதிப்புகள். எடுத்துக்காட்டாக, செயல்பாடுகள் அதிகபட்சம், சராசரி, தொகை ஆகியவை இந்த பண்புகளைக் கொண்டுள்ளன, ஆனால் சராசரி செயல்பாடு இல்லை.

உள்வரும் தரவு ஸ்ட்ரீம் நேரம் வரிசைப்படுத்தப்பட்டுள்ளது என்றும் நாங்கள் கருதுவோம். இது கடைசி நிமிடத்தில் மட்டுமே வேலை செய்யும் வாய்ப்பை வழங்கும். நடைமுறையில், சில புதுப்பிப்புகள் தாமதமானால் தற்போதைய மற்றும் முந்தைய நிமிடங்களுடன் வேலை செய்ய முடிந்தால் போதும். எளிமைக்காக, இந்த வழக்கை நாங்கள் கருத்தில் கொள்ள மாட்டோம்.

ஒருங்கிணைப்பு செயல்பாடுகள்

தேவையான ஒருங்கிணைப்பு செயல்பாடுகள் கீழே பட்டியலிடப்பட்டுள்ளன. சேவையின் சுமையை அதிகரிக்க நான் முடிந்தவரை பலவற்றை எடுத்தேன்:

  • அதிக - அதிகபட்ச விலை - நிமிடத்திற்கு அதிகபட்ச விலை.
  • குறைந்த - நிமிட விலை - நிமிடத்திற்கு குறைந்தபட்ச விலை.
  • முதல் விலை - முதல் விலை - நிமிடத்திற்கு முதல் விலை.
  • கடைசி விலை - கடைசி விலை - நிமிடத்திற்கு கடைசி விலை.
  • முதல் அளவு - முதல் அளவு - நிமிடத்திற்கு முதல் வர்த்தக அளவு.
  • கடைசி அளவு - கடைசி அளவு - ஒரு நிமிடத்தில் கடைசி வர்த்தக அளவு.
  • numTrades - எண்ணிக்கை i - நிமிடத்திற்கு வர்த்தகங்களின் எண்ணிக்கை.
  • தொகுதி - தொகை அளவு - நிமிடத்திற்கு வர்த்தக அளவுகளின் தொகை.
  • pvolume – sum price – avgPrice க்கு தேவையான நிமிடத்திற்கான விலைகளின் தொகை.
  • - தொகை விற்றுமுதல் விலை* அளவு - நிமிடத்திற்கு பரிவர்த்தனைகளின் மொத்த அளவு.
  • avgPrice - pvolume%numTrades - நிமிடத்திற்கு சராசரி விலை.
  • avgSize – volume%numTrades – நிமிடத்திற்கு சராசரி வர்த்தக அளவு.
  • vwap - விற்றுமுதல்% தொகுதி - பரிவர்த்தனை அளவின் அடிப்படையில் ஒரு நிமிடத்திற்கு சராசரி விலை.
  • cumVolume - தொகை அளவு - முழு நேரத்திலும் பரிவர்த்தனைகளின் திரட்டப்பட்ட அளவு.

இந்த நெடுவரிசைகளை முதல் முறையாக மற்றும் ஒவ்வொரு அடுத்த நிமிடத்திற்கும் எவ்வாறு துவக்குவது என்பதை - ஒரு வெளிப்படையான விஷயத்தை உடனடியாக விவாதிப்போம். முதல் விலை வகையின் சில நெடுவரிசைகள் ஒவ்வொரு முறையும் பூஜ்யமாக துவக்கப்பட வேண்டும்; அவற்றின் மதிப்பு வரையறுக்கப்படவில்லை. மற்ற தொகுதி வகைகளை எப்போதும் 0 க்கு அமைக்க வேண்டும். ஒருங்கிணைந்த அணுகுமுறை தேவைப்படும் நெடுவரிசைகளும் உள்ளன - எடுத்துக்காட்டாக, cumVolume முந்தைய நிமிடத்திலிருந்து நகலெடுக்கப்பட வேண்டும், முதல் நிமிடத்திற்கு 0 ஆக அமைக்கப்பட வேண்டும். இந்த அளவுருக்கள் அனைத்தையும் அகராதி தரவைப் பயன்படுத்தி அமைப்போம் வகை (பதிவுக்கு ஒப்பானது):

// 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 объяснен ниже

நான் வசதிக்காக அகராதியில் சிம் மற்றும் நேரத்தைச் சேர்த்துள்ளேன், இப்போது initWith என்பது இறுதி ஒருங்கிணைந்த அட்டவணையில் இருந்து ஒரு ஆயத்த வரியாகும், அங்கு சரியான சிம் மற்றும் நேரத்தை அமைக்க வேண்டும். அட்டவணையில் புதிய வரிசைகளைச் சேர்க்க இதைப் பயன்படுத்தலாம்.

ஒரு திரட்டல் செயல்பாட்டை உருவாக்கும் போது எங்களுக்கு aggCols தேவைப்படும். Q இல் உள்ள வெளிப்பாடுகள் (வலமிருந்து இடமாக) மதிப்பிடப்படும் வரிசையின் காரணமாக பட்டியல் தலைகீழாக இருக்க வேண்டும். சில நெடுவரிசைகள் முந்தையவற்றைச் சார்ந்திருப்பதால், கணக்கீடு உயர்விலிருந்து கம் வால்யூம் வரை செல்வதை உறுதி செய்வதே இலக்காகும்.

முந்தைய நிமிடத்திலிருந்து புதிய நிமிடத்திற்கு நகலெடுக்க வேண்டிய நெடுவரிசைகள், வசதிக்காக சிம் நெடுவரிசை சேர்க்கப்பட்டது:

rollColumns:`sym`cumVolume;

இப்போது அவை எவ்வாறு புதுப்பிக்கப்பட வேண்டும் என்பதைப் பொறுத்து நிரல்களை குழுக்களாகப் பிரிப்போம். மூன்று வகைகளை வேறுபடுத்தி அறியலாம்:

  1. திரட்டிகள் (தொகுதி, விற்றுமுதல்,..) - உள்வரும் மதிப்பை முந்தைய மதிப்புடன் சேர்க்க வேண்டும்.
  2. ஒரு சிறப்பு புள்ளியுடன் (உயர், குறைந்த, ..) - நிமிடத்தில் முதல் மதிப்பு உள்வரும் தரவிலிருந்து எடுக்கப்படுகிறது, மீதமுள்ளவை செயல்பாட்டைப் பயன்படுத்தி கணக்கிடப்படுகின்றன.
  3. ஓய்வு. எப்போதும் ஒரு செயல்பாட்டைப் பயன்படுத்தி கணக்கிடப்படுகிறது.

இந்த வகுப்புகளுக்கான மாறிகளை வரையறுப்போம்:

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

கணக்கீட்டு வரிசை

ஒருங்கிணைந்த அட்டவணையை இரண்டு நிலைகளில் புதுப்பிப்போம். செயல்திறனுக்காக, உள்வரும் அட்டவணையை முதலில் சுருக்குகிறோம், இதனால் ஒவ்வொரு எழுத்துக்கும் நிமிடத்திற்கும் ஒரு வரிசை மட்டுமே இருக்கும். எங்களின் அனைத்து செயல்பாடுகளும் அதிகரிக்கும் மற்றும் துணையாக இருப்பதால், இந்த கூடுதல் படியின் முடிவு மாறாது என்பதற்கு உத்தரவாதம் அளிக்கிறது. தேர்ந்தெடுக்கப்பட்டதைப் பயன்படுத்தி அட்டவணையைச் சுருக்கலாம்:

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

இந்த முறை ஒரு குறைபாடு உள்ளது - கணக்கிடப்பட்ட நெடுவரிசைகளின் தொகுப்பு முன் வரையறுக்கப்பட்டுள்ளது. அதிர்ஷ்டவசமாக, Q இல், நீங்கள் மாறும் வகையில் உருவாக்கப்பட்ட வாதங்களை மாற்றக்கூடிய ஒரு செயல்பாடாகவும் தேர்ந்தெடு செயல்படுத்தப்படுகிறது:

?[table;whereClause;byClause;selectClause]

வாதங்களின் வடிவமைப்பை நான் விரிவாக விவரிக்க மாட்டேன்; எங்கள் விஷயத்தில், தேர்ந்தெடுக்கப்பட்ட வெளிப்பாடுகள் மட்டுமே அற்பமானதாக இருக்கும், மேலும் அவை படிவ நெடுவரிசைகளின் அகராதிகளாக இருக்க வேண்டும். எனவே, சுருக்க செயல்பாட்டை பின்வருமாறு வரையறுக்கலாம்:

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

தெளிவுக்காக, நான் பாகுபடுத்தும் செயல்பாட்டைப் பயன்படுத்தினேன், இது Q வெளிப்பாடு கொண்ட ஒரு சரத்தை eval செயல்பாட்டிற்கு அனுப்பக்கூடிய மதிப்பாக மாற்றுகிறது மற்றும் இது செயல்பாட்டின் தேர்வில் தேவைப்படுகிறது. முன்செயலானது தேர்ந்தெடுக்கப்பட்ட செயல்பாட்டின் ஒரு ப்ரொஜெக்ஷன் (அதாவது, பகுதி வரையறுக்கப்பட்ட வாதங்களைக் கொண்ட செயல்பாடு) என வரையறுக்கப்படுகிறது, ஒரு வாதம் (அட்டவணை) இல்லை. ஒரு அட்டவணையில் முன்செயல்முறையைப் பயன்படுத்தினால், சுருக்கப்பட்ட அட்டவணையைப் பெறுவோம்.

இரண்டாவது கட்டம் ஒருங்கிணைக்கப்பட்ட அட்டவணையைப் புதுப்பிக்கிறது. முதலில் அல்காரிதத்தை சூடோகோடில் எழுதுவோம்:

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

Q இல், லூப்களுக்குப் பதிலாக வரைபடம்/குறைப்பு செயல்பாடுகளைப் பயன்படுத்துவது பொதுவானது. ஆனால் Q என்பது ஒரு திசையன் மொழி என்பதாலும், எல்லாச் செயல்களையும் ஒரே நேரத்தில் அனைத்துச் சின்னங்களுக்கும் எளிதாகப் பயன்படுத்த முடியும் என்பதாலும், முதல் தோராயமாக, லூப் இல்லாமல், அனைத்து குறியீடுகளிலும் ஒரே நேரத்தில் செயல்பாடுகளைச் செய்யலாம்:

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

ஆனால் நாம் மேலும் செல்லலாம், Q க்கு ஒரு தனித்துவமான மற்றும் மிகவும் சக்திவாய்ந்த ஆபரேட்டர் உள்ளது - பொதுமைப்படுத்தப்பட்ட அசைன்மென்ட் ஆபரேட்டர். குறியீடுகள், செயல்பாடுகள் மற்றும் வாதங்களின் பட்டியலைப் பயன்படுத்தி சிக்கலான தரவு கட்டமைப்பில் மதிப்புகளின் தொகுப்பை மாற்ற இது உங்களை அனுமதிக்கிறது. எங்கள் விஷயத்தில் இது போல் தெரிகிறது:

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

துரதிருஷ்டவசமாக, ஒரு அட்டவணைக்கு ஒதுக்க, உங்களுக்கு நெடுவரிசைகள் அல்ல, வரிசைகளின் பட்டியல் தேவை, மேலும் ஃபிளிப் செயல்பாட்டைப் பயன்படுத்தி மேட்ரிக்ஸை (வரிசைகளின் பட்டியலுக்கான நெடுவரிசைகளின் பட்டியல்) இடமாற்றம் செய்ய வேண்டும். ஒரு பெரிய அட்டவணைக்கு இது விலை உயர்ந்தது, எனவே அதற்குப் பதிலாக ஒவ்வொரு நெடுவரிசைக்கும் தனித்தனியாக ஒரு பொதுவான ஒதுக்கீட்டைப் பயன்படுத்துகிறோம், வரைபடச் செயல்பாட்டைப் பயன்படுத்தி (அப்போஸ்ட்ரோஃபி போல் தெரிகிறது):

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

நாங்கள் மீண்டும் செயல்பாட்டுத் திட்டத்தைப் பயன்படுத்துகிறோம். Q இல், ஒரு பட்டியலை உருவாக்குவதும் ஒரு செயல்பாடு மற்றும் பட்டியல்களின் பட்டியலைப் பெற ஒவ்வொரு(வரைபடம்) செயல்பாட்டைப் பயன்படுத்தி அதை அழைக்கலாம்.

கணக்கிடப்பட்ட நெடுவரிசைகளின் தொகுப்பு நிலையானதாக இல்லை என்பதை உறுதிப்படுத்த, மேலே உள்ள வெளிப்பாட்டை மாறும் வகையில் உருவாக்குவோம். ஒவ்வொரு நெடுவரிசையையும் கணக்கிடுவதற்கான செயல்பாடுகளை முதலில் வரையறுப்போம், ஒருங்கிணைக்கப்பட்ட மற்றும் உள்ளீட்டுத் தரவைக் குறிக்க வரிசை மற்றும் inp மாறிகளைப் பயன்படுத்தி:

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

சில நெடுவரிசைகள் சிறப்பு வாய்ந்தவை; அவற்றின் முதல் மதிப்பு செயல்பாட்டின் மூலம் கணக்கிடப்படக்கூடாது. வரிசை[`numTrades] நெடுவரிசை மூலம் இது முதன்மையானது என்பதை நாம் தீர்மானிக்கலாம் - அதில் 0 இருந்தால், மதிப்பு முதலில் இருக்கும். Q க்கு தேர்ந்தெடுக்கப்பட்ட செயல்பாடு உள்ளது - ?[பூலியன் பட்டியல்; பட்டியல்1; பட்டியல்2] - இது முதல் வாதத்தில் உள்ள நிபந்தனையைப் பொறுத்து பட்டியல் 1 அல்லது 2 இலிருந்து ஒரு மதிப்பைத் தேர்ந்தெடுக்கிறது:

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

இங்கே நான் எனது செயல்பாடு (சுருள் பிரேஸ்களில் ஒரு வெளிப்பாடு) உடன் ஒரு பொதுவான ஒதுக்கீட்டை அழைத்தேன். இது தற்போதைய மதிப்பு (முதல் வாதம்) மற்றும் கூடுதல் வாதத்தைப் பெறுகிறது, நான் 4 வது அளவுருவில் அனுப்புகிறேன்.

பேட்டரி ஸ்பீக்கர்கள் தனித்தனியாகச் சேர்ப்போம், ஏனெனில் அவற்றின் செயல்பாடு ஒன்றுதான்:

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

Q தரநிலைகளின்படி இது ஒரு சாதாரண ஒதுக்கீடு, ஆனால் நான் மதிப்புகளின் பட்டியலை ஒரே நேரத்தில் ஒதுக்குகிறேன். இறுதியாக, முக்கிய செயல்பாட்டை உருவாக்குவோம்:

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

இந்த வெளிப்பாடு மூலம், நான் மேலே கொடுத்த வெளிப்பாட்டைக் கொண்ட ஒரு சரத்திலிருந்து ஒரு செயல்பாட்டை மாறும் வகையில் உருவாக்குகிறேன். முடிவு இப்படி இருக்கும்:

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

Q இல் மதிப்பீட்டு வரிசை வலமிருந்து இடமாக இருப்பதால், நெடுவரிசை மதிப்பீட்டு வரிசை தலைகீழாக மாற்றப்பட்டுள்ளது.

இப்போது கணக்கீடுகளுக்கு தேவையான இரண்டு முக்கிய செயல்பாடுகள் உள்ளன, நாம் ஒரு சிறிய உள்கட்டமைப்பைச் சேர்க்க வேண்டும், சேவை தயாராக உள்ளது.

இறுதி படிகள்

எல்லா வேலைகளையும் செய்யும் முன்செயல்முறை மற்றும் அப்டேட்Agg செயல்பாடுகள் எங்களிடம் உள்ளன. ஆனால் நிமிடங்களில் சரியான மாற்றத்தை உறுதிசெய்து, திரட்டலுக்கான குறியீடுகளை கணக்கிடுவது இன்னும் அவசியம். முதலில், 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
 }

ரோல் செயல்பாட்டையும் வரையறுப்போம், இது தற்போதைய நிமிடத்தை மாற்றும்:

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

புதிய எழுத்துக்களைச் சேர்க்க எங்களுக்கு ஒரு செயல்பாடு தேவைப்படும்:

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

இறுதியாக, மேம்படுத்தல் செயல்பாடு (Q சேவைகளுக்கான இந்த செயல்பாட்டிற்கான பாரம்பரிய பெயர்), இது தரவைச் சேர்க்க கிளையன்ட்டால் அழைக்கப்படுகிறது:

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

அவ்வளவுதான். வாக்குறுதியளித்தபடி, எங்கள் சேவையின் முழுமையான குறியீடு இங்கே உள்ளது, சில வரிகள்:

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

சோதனை

சேவையின் செயல்திறனை சரிபார்க்கலாம். இதைச் செய்ய, அதை ஒரு தனி செயல்பாட்டில் இயக்குவோம் (குறியீட்டை service.q கோப்பில் வைக்கவும்) மற்றும் init செயல்பாட்டை அழைக்கவும்:

q service.q –p 5566

q)init[]

மற்றொரு கன்சோலில், இரண்டாவது Q செயல்முறையைத் தொடங்கி, முதலில் இணைக்கவும்:

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

முதலில், சின்னங்களின் பட்டியலை உருவாக்குவோம் - 10000 துண்டுகள் மற்றும் ஒரு சீரற்ற அட்டவணையை உருவாக்க ஒரு செயல்பாட்டைச் சேர்ப்போம். இரண்டாவது கன்சோலில்:

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

அட்டவணையில் அவற்றைத் தேடுவதை எளிதாக்க, பட்டியலில் மூன்று உண்மையான சின்னங்களைச் சேர்த்துள்ளேன். rnd செயல்பாடு n வரிசைகளுடன் ஒரு சீரற்ற அட்டவணையை உருவாக்குகிறது, அங்கு நேரம் t முதல் t+25 மில்லி விநாடிகள் வரை மாறுபடும்.

இப்போது நீங்கள் சேவைக்கு தரவை அனுப்ப முயற்சி செய்யலாம் (முதல் பத்து மணிநேரத்தைச் சேர்க்கவும்):

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

அட்டவணை புதுப்பிக்கப்பட்டுள்ளதா என்பதை நீங்கள் சேவையில் பார்க்கலாம்:

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

முடிவு:

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

ஒரு நிமிடத்திற்கு எவ்வளவு தரவைச் செயலாக்க முடியும் என்பதைக் கண்டறிய இப்போது சுமை சோதனையை மேற்கொள்வோம். புதுப்பிப்பு இடைவெளியை 25 மில்லி விநாடிகளாக அமைத்துள்ளோம் என்பதை நினைவூட்டுகிறேன். அதன்படி, பயனர்கள் தரவைக் கோருவதற்கு நேரத்தை வழங்க, சேவையானது (சராசரியாக) ஒரு புதுப்பிப்புக்கு குறைந்தது 20 மில்லி விநாடிகளுக்குள் பொருந்த வேண்டும். இரண்டாவது செயல்பாட்டில் பின்வருவனவற்றை உள்ளிடவும்:

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 என்பது இரண்டு நிமிடங்கள். ஒவ்வொரு 1000 மில்லி விநாடிகளிலும் 25 வரிசைகளுக்கு முதலில் இயக்க முயற்சி செய்யலாம்:

start 1000

என்னைப் பொறுத்தவரை, ஒரு புதுப்பிப்புக்கு இரண்டு மில்லி விநாடிகளில் முடிவு கிடைக்கும். எனவே நான் உடனடியாக வரிசைகளின் எண்ணிக்கையை 10.000 ஆக உயர்த்துவேன்:

start 10000

முடிவு:

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

மீண்டும், சிறப்பு எதுவும் இல்லை, ஆனால் இது நிமிடத்திற்கு 24 மில்லியன் வரிகள், வினாடிக்கு 400 ஆயிரம். 25 மில்லி விநாடிகளுக்கு மேல், அப்டேட் 5 முறை மட்டுமே மெதுவாகச் சென்றது, வெளிப்படையாக நிமிடம் மாறும்போது. 100.000 ஆக அதிகரிப்போம்:

start 100000

முடிவு:

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

நீங்கள் பார்க்க முடியும் என, சேவை அரிதாகவே சமாளிக்க முடியும், இருப்பினும் அது மிதக்க நிர்வகிக்கிறது. அத்தகைய தரவு அளவு (நிமிடத்திற்கு 240 மில்லியன் வரிசைகள்) மிகப் பெரியது; இதுபோன்ற சந்தர்ப்பங்களில், சேவையின் பல குளோன்களை (அல்லது டஜன் கணக்கான குளோன்கள் கூட) தொடங்குவது பொதுவானது, ஒவ்வொன்றும் எழுத்துக்களின் ஒரு பகுதியை மட்டுமே செயலாக்குகிறது. இருப்பினும், தரவு சேமிப்பகத்தில் முதன்மையாக கவனம் செலுத்தும் விளக்கப்பட்ட மொழியின் முடிவு சுவாரஸ்யமாக உள்ளது.

ஒவ்வொரு புதுப்பிப்பின் அளவிலும் நேரம் ஏன் நேரியல் அல்லாமல் வளர்கிறது என்ற கேள்வி எழலாம். காரணம், சுருக்க செயல்பாடு உண்மையில் ஒரு C செயல்பாடு ஆகும், இது updateAgg ஐ விட மிகவும் திறமையானது. ஒரு குறிப்பிட்ட புதுப்பிப்பு அளவிலிருந்து (சுமார் 10.000) தொடங்கி, updateAgg அதன் உச்சவரம்பை அடைகிறது, அதன் செயலாக்க நேரம் புதுப்பிப்பு அளவைப் பொறுத்தது அல்ல. பூர்வாங்கப் படியான Q காரணமாகவே, சேவையானது இத்தகைய தரவுகளின் அளவை ஜீரணிக்க முடிகிறது. பெரிய தரவுகளுடன் பணிபுரியும் போது சரியான அல்காரிதத்தைத் தேர்ந்தெடுப்பது எவ்வளவு முக்கியம் என்பதை இது எடுத்துக்காட்டுகிறது. மற்றொரு புள்ளி நினைவகத்தில் தரவின் சரியான சேமிப்பு. தரவு நெடுவரிசையில் சேமிக்கப்படவில்லை அல்லது நேரத்தால் ஆர்டர் செய்யப்படாவிட்டால், TLB கேச் மிஸ் போன்ற ஒரு விஷயத்தை நாம் நன்கு அறிந்திருப்போம் - செயலி முகவரி தற்காலிக சேமிப்பில் நினைவக பக்க முகவரி இல்லாதது. ஒரு முகவரியைத் தேடுவது தோல்வியுற்றால் சுமார் 30 மடங்கு அதிகமாகும், மேலும் தரவு சிதறியிருந்தால், அது சேவையை பல முறை குறைக்கலாம்.

முடிவுக்கு

இந்தக் கட்டுரையில், KDB+ மற்றும் Q தரவுத்தளமானது, பெரிய தரவைச் சேமிப்பதற்கும், தேர்ந்தெடுத்தல் மூலம் எளிதாக அணுகுவதற்கும் மட்டுமல்ல, நூற்றுக்கணக்கான மில்லியன் வரிசைகள்/ஜிகாபைட் தரவுகளை ஜீரணிக்கக்கூடிய தரவு செயலாக்க சேவைகளை உருவாக்குவதற்கும் ஏற்றது என்பதைக் காட்டினேன். ஒரு ஒற்றை Q செயல்முறை. Q மொழியே அதன் திசையன் தன்மை, உள்ளமைக்கப்பட்ட SQL பேச்சு மொழிபெயர்ப்பான் மற்றும் மிகவும் வெற்றிகரமான நூலக செயல்பாடுகளின் காரணமாக தரவு செயலாக்கம் தொடர்பான வழிமுறைகளை மிகவும் சுருக்கமாகவும் திறமையாகவும் செயல்படுத்த அனுமதிக்கிறது.

மேலே உள்ளவை Q என்ன செய்ய முடியும் என்பதன் ஒரு பகுதி என்பதை நான் கவனிக்கிறேன், இது மற்ற தனித்துவமான அம்சங்களையும் கொண்டுள்ளது. எடுத்துக்காட்டாக, தனிப்பட்ட Q செயல்முறைகளுக்கு இடையிலான எல்லையை அழிக்கும் மற்றும் உலகின் பல்வேறு பகுதிகளில் உள்ள டஜன் கணக்கான சர்வர்களில் இருக்கும் ஒரே நெட்வொர்க்கில் நூற்றுக்கணக்கான இந்த செயல்முறைகளை இணைக்க உங்களை அனுமதிக்கும் மிகவும் எளிமையான IPC நெறிமுறை.

ஆதாரம்: www.habr.com

DDoS பாதுகாப்பு, VPS VDS சர்வர்கள் கொண்ட தளங்களுக்கு நம்பகமான ஹோஸ்டிங் வாங்கவும் 🔥 DDoS பாதுகாப்புடன் கூடிய நம்பகமான இணையதள ஹோஸ்டிங், VPS, VDS சர்வர்களை வாங்குங்கள் | ProHoster