前言
由于項目組里的老大工程能力實在太強碴萧,各種技術棧玩得6到飛起,于是我們也跟著開闊了不少視野末购。業(yè)務的一些特性也導致允許我們嘗試一些最新的技術來完成工作內容破喻。剛進來的時候接觸clojure,這種lisp方言盟榴,開始我是抗拒的曹质。具有一定的入門門檻,開始時常城娉。看幾個函數(shù)處理邏輯羽德,要花個半天的時間。再加上逆天的可擴展性極強的宏特性迅办,老實說我是崩潰的玩般。(不過后期熟練后用起來,寫業(yè)務接口真的是好簡潔的說礼饱,,究驴,基于jvm的好處是要啥有啥镊绪,,洒忧,這里按下不表蝴韭。)
倘若要談起編程功底,編碼能力啥的熙侍,語言這種東西實在是浮于表面榄鉴,大佬眼中對于“世界上最好的語言”這種爭論常常是不屑一顧的,因為什么東西都能在他們手里玩出花兒來蛉抓。(為了不顯得過于低端庆尘,當然不能討論語言啊)
但是,最近開始用kdb+,搭配的Q 語言巷送,實在是把三觀毀得天崩地裂驶忌,以前還覺得clojure非常難懂,現(xiàn)在看來笑跛,真是年輕人啊付魔,又傻又胖又稚嫩。飞蹂。几苍。
說下業(yè)務場景:
現(xiàn)在有一張業(yè)務明細表,字段若干(100左右),量級百萬左右陈哑,經(jīng)常需要查看某條ID的明細數(shù)據(jù)妻坝,某個列的透視情況伸眶,多個列的透視情況等等,頁面上需要多個列的組合group顯示等等(其實就是excel中的各種透視操作)惠勒。這種數(shù)據(jù)量級上事實上mysql就能夠解決赚抡,但是因為頁面上可視化拖拽會導致拼出的sql比較復雜,且對字段優(yōu)化不佳的情況下也會導致查詢時間偏長纠屋。這種對于列的頻繁操作的場景涂臣,再加上量級不大,老大拍板說售担,那就玩下kdb+吧~
折騰這東西期間各種顛覆三觀赁遗,,覺得還是有必要撕破偽裝的假面族铆,誠實的回到一個低端程序員岩四,來討論下語言。哥攘。剖煌。
KDB+ 簡介
kdb+ 號稱 最強的內存數(shù)據(jù)庫之一。列式存儲的特性逝淹,使得對于某個列的統(tǒng)計分析操作異常方便耕姊。全球頂尖的投行,高盛栅葡,摩根茉兰,國內的國信等證券公司也開始使用,在延遲性上有著苛刻要求的金融領域欣簇,kdb+可謂一家獨大规脸。當然在優(yōu)秀的性能背后,超高的費用也是必不可少的熊咽,貌似單核價格在3w+美刀以上?...
kdb+:
- 單體架構莫鸭,輕松支持 billion以上數(shù)據(jù)
- 分布式擴展,無性能損耗
- 超低延遲+高并發(fā)支持
- 列式存儲+內存數(shù)據(jù)庫
- 靈活的Q語言横殴,內置非常多的統(tǒng)計計算方法黔龟。
等等等。滥玷。氏身。
當然官方也提供了32位免費版,單進程支持最大4G內存惑畴。實用中80萬數(shù)據(jù)蛋欣,100+列,在無任何優(yōu)化的情況下如贷,內存占用大概是200+M陷虎。(kdb+還會對數(shù)據(jù)進行壓縮等)所以單進程支持500~1000萬數(shù)據(jù)應該不是大問題到踏。
在個人研究一些歷史股票數(shù)據(jù)時,利用q/kdb+來跑一些簡單的量化策略尚猿,還是非常方便的窝稿。
Q語言 基礎
Q 數(shù)據(jù)類型
Q語言有的數(shù)據(jù)類型,跟其他語言不相上下凿掂,也就是基本的int long,double,string伴榔。但是在表現(xiàn)形式上,兩者有著巨大的生殖隔離...
基本數(shù)據(jù)類型
Q語言 使用 數(shù)字+字符 來表示一個數(shù)據(jù)類型庄萎】耍基礎的有如下幾個數(shù)據(jù)類型(只列出一些基礎的數(shù)據(jù)類型瓷马,詳細可查官方reference)
數(shù)字 | 字符 | 占用字節(jié)大小 | q數(shù)據(jù)類型 | 對應java數(shù)據(jù)類型 | ||
---|---|---|---|---|---|---|
0 | l | list | ||||
1 | b | 1 | boolean | Boolean | ||
4 | x | 1 | byte | Byte | ||
5 | h | 2 | short | Short | ||
6 | i | 4 | int | Integer | ||
7 | j | 8 | long | Long | ||
8 | e | 4 | real | Float | ||
9 | f | 8 | float | Double | ||
10 | c | 1 | char | Charater | ||
11 | s | symbol | String | |||
14 | d | 4 | date | Date | ||
15 | z | 8 | datetime | TimeStamp | ||
16 | n | 8 | timespan | TimeSpan | ||
19 | t | 4 | time | Time |
a:6 /定義 a = 6 (q中數(shù)字默認定義為Long類型)
type a
\表示注釋開始
輸出 -7h 链烈。負號表示這是a 變量是一個atom值憎茂,表示只有一個值,跟 list類型相反忍捡。
查表 集漾,7表示為long類型,h 表示type的輸出類型 是short,查表砸脊,h對應類型為short帆竹。
注釋結束/
b:1.2
type b / 輸出 -9h 。即b是一個atom值脓规,值類型為我們通常用的Double形
想要定義特定類型的數(shù)據(jù),也非常的簡單险领,只需在 值前面加上 對應的 類型字符+"$"侨舆。
int2:`i$2
type int2 /輸出 -6h。
char 與symbol:
q語言中symbol就是我們常理解 string類型绢陌,用 ` 開始挨下。
char類型則用 雙引號 "" 表示。(有點反常理的樣子....)
c:"h"
cs:"hello world"
type c / 輸出 -10h脐湾。 表示這個是單個的值臭笆,值的類型為char
type cs / 輸出 10h,正號表示這是個list,且是個char類型的list秤掌。
假如想要定義一個有多個字符的字符串(而不是char list)愁铺,就得使用 symbol
s:`hello /type s 輸出 -11h。 表示為單個的string闻鉴。
space: `$("hello world") / string類型中包含空格會解析錯誤茵乱,得使用類型轉換將 char list轉為 symbol....
date 日期時間等
q語言中針對日期時間操作非常簡單方便且強大。(kdb+本意也是為股票證券等時序數(shù)據(jù)而準備的孟岛,畢竟每毫秒鐘就幾千萬上下...)
日期類型一般限制與 1709.01.01 ~ 2290.12.31 任何日期操作的結果值則不會有這個限制...
日期格式在kdb+存儲為 yyyy.mm.dd形式瓶竭,日期值以2000.01.01為相對"坐標"督勺。即任何一個date形式的值,都可以轉為int類型斤贰,其值的結果就是相對于 2000.01.01的相對天數(shù)
dt:2000.01.02
`int $ dt / 輸出 1智哀,表示與2000.01.01相差為1天。在此日期之前荧恍,則值為負數(shù)
time 類型就是一天中的時分秒毫秒瓷叫。time類型在kdb+表示為32位的int,表示相對于今天0點0分0毫秒的相對值块饺。最小單位為毫秒赞辩。因此,int 和time類型也能夠輕松轉換授艰,時間操作上也輕松方便辨嗽。
mtime: `time $ 60000
/以毫秒為最小單位,則mtime表示 第一個分鐘淮腾。mtime = 00:01:00.000
mtime - 30000 / 00:00:30.000 時間可以直接 + - * %
mtime * 2 / 00:02:00.000
當然datetime 形式就是 date+time合并在一起糟需。
q語言中還提供了 分鐘秒等類型
list
q中的list 與python的list概念上并沒有太大的差別,都可以認為是多個元素的集合體谷朝。支持多重嵌套
q語言中生成 list主要由幾種方法
ali: 1 2 3 4 /直接賦值,ali 為 list洲押,list中元素為1,2,3,4
ali:(1 2 3 4)/ 同上
ali: enlist 1 2 3 4 / 同上
/其他的如 # take操作符等等。
dict 字典
dict 字典也是一種常見的數(shù)據(jù)類型圆凰,key杈帐,value格式存儲,支持多重嵌套
dict:`a`b`c!1 2 3 / 使用感嘆號!定義一個字典 ,即{"a":1,"b":2,"c":3}
不太相同的是专钉,q中允許一個字典包含相同的key值挑童,但使用key 查找時,只會命中第一個出現(xiàn)該key的值跃须。
dict:`a`b`a`c!10 20 30 20
[圖片上傳失敗...(image-ddad66-1536989081986)]
查找 key為a 的值時站叼,只會命中第一個key出現(xiàn)的值,更新也只會更新第一個key的值菇民。
刪除則會把所有相同的key都刪掉.
table
table 是q中的"一等"數(shù)據(jù)結構尽楔。
table 可以認為是字典的集合。將字典轉置一下第练,就可以當成table阔馋。
dict:`a`b`c!(1 2 3; 1 2 3;1 2 3)
a| 1 2 3
b| 1 2 3
c| 1 2 3
/dict 的key,a b c 的值都為 1 2 3。
/將dict 的key value 九十度轉換一下娇掏,就變成了table垦缅。dict 的key 變成了table 的header,dict 的value變成了table每行的值驹碍。
table:flip dict
a b c
-----
1 1 1
2 2 2
3 3 3
table 的每一列就是一個key value格式的字典壁涎。因此也認為table是列式的凡恍。
table `a /取 a 列的所有值.
1 2 3
table[0] /取table的第一行。返回一個字典怔球。
a| 1
b| 1
c| 1
keyed /unkeyed
table 除了可以通過 轉置一下字典獲得外嚼酝,還能使用table的標準定義
unkey:([]name:`a`b`c; val:1 2 3)
name val
--------
a 1
b 2
c 3
keyed:([name:`a`b`c] val:1 2 3)
name| val
----| ---
a | 1
b | 2
c | 3
/keyed 與unkeyed的展示上區(qū)別是 name 和val 之間多了一個豎條分隔。
table 分為 keyed 和unkeyed竟坛。在定義上的區(qū)別是闽巩,中括號[] 中是否有定義列。
unkeyd table 就是 list,list里面的值為為一個字典担汤。如 (d1 d2 d3 d4)涎跨。d1 到d4 是四個不同的字典,字典的key充當table的列名崭歧,字典的每個值分別對應table同一行的值隅很。
unkey table 可以通過 對列名獲取整個列的值。也可以利用index下標(因為是一個list)率碾,如叔营,table[0],返回一個字典所宰,也即{"d1":d1_val0,"d2",d2_val0,"d3":d3_val0,"d4":d4_val0}
keyed table與unkey table 不同绒尊,keyed table 其實是存成了一個字典,字典的key 為中括號[] 定義的值仔粥。因此也與字典一樣婴谱,允許擁有相同的key值,但只會針對第一個出現(xiàn)的key值做操作躯泰。
因為 keyed table存成了字典谭羔,只能通過[]中的key進行獲取,無法通過列名稱獲取整列的值斟冕。這里的[]中定義的值有點像mysql中的主鍵,每次獲取一行的信息通過主鍵獲取缅阳。(不過這里允許主鍵重復)
unkey `val /操作合法磕蛇,取val 這一列的值。返回一個list
unkey[0] /操作合法十办。取表的第一行數(shù)據(jù),返回一個dict
name| `a
val | 1
unkey `val /操作不合法秀撇。
unkey[0] /操作不合法
keyed `a /操作合法,取表中主鍵為 a的行記錄 返回 val| 1
針對 table的操作向族,更多時候是使用類sql的方式進行操作呵燕,后面會找個時間記錄下這部分
數(shù)據(jù)基本操作
Q 語言基本操作符
operator | desc |
---|---|
+ - * % | 加 減 乘 除 |
& | | &(最大) |(最小) |
= <> | 相等/不等 件相,一個一個比較再扭,若操作數(shù)是list氧苍,則每個atom一一比較 |
~ | 比較,與 = 不同,將操作數(shù)看成一個整體比較 |
:: | 別名泛范,可看是是一個引用让虐。 |
Q 執(zhí)行順序 與操作符優(yōu)先級
Q語言在執(zhí)行順序是從右到左,剛接觸的時候,這一點可能會非常的不習慣罢荡,再加上本身的是 一種vector language,當參數(shù)是標量和參數(shù)是向量時赡突,常常會有不一樣的效果,特別是操作符有各種前綴区赵,中綴惭缰,后綴用法,這也常被人詬病笼才,天書一樣的表達方式漱受。
Q語言不存在操作符優(yōu)先級
重溫下三種表達式:前綴表達式(prefix),中綴表達式(infix),后綴表達式(postfix)
下面簡單列舉兩個常見運算,三種式子 是等價的患整。
中綴表達式 | 前綴表達式 | 后綴表達式 | desc |
---|---|---|---|
A * B + C / D | A B * C D / + | + * A B / C D | A 乘B拜效,C除以D,結果相加 |
A * (B + C) / D | A B C + * D / | / * A + B C D | B加C各谚,乘A紧憾,除以D |
其中 中綴表達式(infix),是最符合人類閱讀習慣的昌渤,操作符之間也是有優(yōu)先級的赴穗。Q語言中為了追求解析速度,拋棄了操作符之間的優(yōu)先級膀息,這種情況下般眉,解釋器不用等到完整得讀到整個語句,便可以執(zhí)行潜支。
例如 A * B + C
由于 Q 語言是從右往左執(zhí)行的甸赃,所以 先讀到 B + C ,馬上就eval求值冗酿,假如要加上操作符優(yōu)先級的話埠对,得整行讀完,
A * B + C裁替,發(fā)現(xiàn) * 優(yōu)先級 比 + 高项玛,先執(zhí)行 A * B,再加上C。
去掉操作符優(yōu)先級的最大好處就是提高了解析速度弱判。金融領域每一個小的速度提升都是非常重要的襟沮。
2 * 3 + 4
/輸出值為 14. 從右到左執(zhí)行。無操作符優(yōu)先級
原子操作符的擴展性
+ - * % mod 等操作時,當操作數(shù)為list時开伏,默認會自動將操作符一個一個作用于list中的每個元素膀跌。
100+ 1 2 3
/ 1 2 3,為list, 加號 為list每一個元素都加上100硅则,返回 101 102 103
l:(1 2 3; 4 5)
100+l
/l 是一個 嵌套的list淹父,list中第一個list為 1 2 3,第二個list為 4 5怎虫。加法嵌套的為每一個list中的元素都加上100.返回結果為(101 102 103;104 105)
比較符 = 與 ~ 的區(qū)別.
= 與 ~ 都可以用來 比較兩個操作數(shù)是否相等暑认。不同的是,當 = 的兩邊操作數(shù)為list時大审,會輸出 一對一的比較結果蘸际。而~則將 兩邊操作數(shù)當成一個整體比較。
= | ~ |
---|---|
atomic,原子的單個的進行比較徒扶,當操作數(shù)為list時粮彤,輸出list中對應是否相等 | non-atomic,將操作數(shù)當成一個整體進行比較 |
操作數(shù)兩邊可以為整數(shù)姜骡,字符导坟,日期等,兩兩相互兼容圈澈,symbol值則只能與symbol值對比 | 對操作數(shù)為類型要求 |
"abc" = "abd" /輸出 110b惫周。 1代表比較結果為相等,0則反之康栈。
0 = 2000.01.01 /輸出 1b 前面提到過递递,日期格式以 2000.01.01 為 坐標。因為 2000.01.01 可以與 0 直接比較
1 = 00:00:00.001 /輸出 1b啥么, 同理登舞,time類型存成語每天0點的offset值,因此也能與整數(shù)進行直接悬荣。
42="*" /輸出1b菠秒, 字符 * 在ascii中存儲值為 42,因此與整數(shù) 42相等
`b = "b" /執(zhí)行異常氯迂,symbol值也就是string 在使用 = 比較時践叠,兩邊值類型必須都是symbol類型。
"abc" ~ "abd" /輸出 0b囚戚, ~ 將兩邊參數(shù) 看成一個整體酵熙,此語句將 兩個char list進行比較轧简。內容不相同驰坊,則返回 0b。
(1 2;3 4)~(1;2 3 4) /輸出 0b,兩個list 結構不同哮独,
(1; 2 3 4)~(1; (2; 3; 4)) /輸出 1b,兩個list 相同
相對于 ~ 操作符的不同拳芙,下面有幾個 atomic的操作符察藐,在list 情況下,同樣是一對一的比較舟扎。
> 大于
< 小于
>= 大于等于
<= 小于等于
| 取最大值
& 取最小值分飞。
"abc" <= "aef" /輸出 100b
12 32 | 3 44 / 輸出 12 44
"accg" & "abed" /輸出 "abcd",對于兩個 char list中睹限,一一比較譬猫,取最小值。
Q語言中簡單的數(shù)據(jù)類型和操作規(guī)則就先介紹到這羡疗,下次介紹下 list染服,dict 與 function的一些用法與特性~~