KDB+,該公司的產品 是一個在小範圍內廣為人知、速度異常快的列式數據庫,專為存儲時間序列和基於它們的分析計算而設計。 最初,它在金融行業非常流行——所有前 10 大投資銀行和許多知名的對沖基金、交易所和其他組織都在使用它。 最近,KX 決定擴大其客戶群,現在在有大量數據的其他領域提供解決方案,按時間或其他方式排序——電信、生物信息學、製造等。 除此之外,他們還成為了一級方程式阿斯頓馬丁紅牛車隊的合作夥伴,幫助收集和處理來自汽車傳感器的數據,並分析風洞測試。 在這篇文章中,我想談談 KDB+ 的哪些特性使其具有超強的性能,為什麼公司願意在它上面花費大筆資金,最後,為什麼它不是一個真正的數據庫。

在本文中,我將嘗試大體上告訴您 KDB+ 是什麼,它有哪些特性和局限性,以及它對想要處理大量數據的公司有何用途。 我不會詳細介紹 KDB+ 的實現及其 Q 編程語言的細節。這兩個主題都非常廣泛,值得單獨寫文章。 可以在 code.kx.com 上找到有關這些主題的大量信息,包括 Q 書 - Q For Mortals(參見下面的鏈接)。
一些術語
- 內存數據庫。 將數據存儲在 RAM 中以加快訪問速度的數據庫。 這樣的數據庫優點很明顯,缺點是有可能丟失數據,需要在服務器上有大量的內存。
- 列數據庫。 數據按列存儲而不是按記錄存儲的數據庫。 這種數據庫的主要優點是來自一列的數據一起存儲在磁盤和內存中,這大大加快了對它們的訪問速度。 無需加載查詢中未使用的列。 主要缺點是難以修改和刪除記錄。
- 時間序列。 帶有日期或時間列的數據。 通常,及時排序對於此類數據很重要,因此很容易確定哪條記錄在當前記錄之前或之後,或者應用其結果取決於記錄順序的函數。 經典數據庫建立在一個完全不同的原則之上——將記錄集合表示為一個集合,其中記錄的順序原則上沒有定義。
- 向量。 在 KDB+ 的上下文中,這是相同原子類型的元素列表,例如數字。 換句話說,一個元素數組。 與列表不同,數組可以緊湊地存儲並使用處理器向量指令進行處理。
歷史信息
KX 由 Arthur Whitney 於 1993 年創立,他之前曾在摩根士丹利從事 A+ 語言的工作,A+ 語言是 APL 的繼承者,APL 是一種非常原始且曾經在金融界流行的語言。 當然,在 KX 中,Arthur 繼續本著同樣的精神,在激進極簡主義思想的指導下,創建了矢量函數語言 K。 K 程序看起來像一堆標點符號和特殊字符,符號和函數的含義取決於上下文,每個操作都比傳統編程語言中的含義多得多。 正因為如此,K 程序佔用的空間最少——幾行代碼就可以代替 Java 等冗長語言的幾頁文本——並且是該算法的超集中實現。
K 上的一個函數,它根據給定的語法實現大部分 LL1 解析器生成器:
1. pp:{q:{(x;p3(),y)};r:$[-11=@x;$x;11=@x;q[`N;$*x];10=abs@@x;q[`N;x]
2. ($)~*x;(`P;p3 x 1);(1=#x)&11=@*x;pp[{(1#x;$[2=#x;;,:]1_x)}@*x]
3. (?)~*x;(`Q;pp[x 1]);(*)~*x;(`M;pp[x 1]);(+)~*x;(`MP;pp[x 1]);(!)~*x;(`Y;p3 x 1)
4. (2=#x)&(@x 1)in 100 101 107 7 -7h;($[(@x 1)in 100 101 107h;`Ff;`Fi];p3 x 1;pp[*x])
5. (|)~*x;`S,(pp'1_x);2=#x;`C,{@[@[x;-1+#x;{x,")"}];0;"(",]}({$[".s.C"~4#x;6_-2_x;x]}'pp'x);'`pp];
6. $[@r;r;($[1<#r;".s.";""],$*r),$[1<#r;"[",(";"/:1_r),"]";""]]}
亞瑟在 2003 年出現的 KDB+ 中體現了這種以最少的身體動作實現極致效率的理念(我想現在已經很清楚名稱中字母 K 的來源了)並且無非是第四版的解釋者K 語言。在 K K 之上添加了一個更加用戶友好的版本,稱為 Q。Q 還添加了對特定 SQL 方言 - QSQL 的支持,並支持將表作為系統數據類型,用於處理內存中的表的工具並在磁盤等上添加到解釋器中。
因此,從用戶的角度來看,KDB+ 只是一個 Q 解釋器,支持來自 C# 的表和類似 SQL 的 LINQ 樣式表達式。 這是 KDB+ 與其主要競爭優勢之間最重要的區別,而這一點往往被忽視。 這不是數據庫+輔助禁用語言,而是成熟強大的編程語言+對數據庫功能的內置支持。 這種區別將在列舉 KDB+ 的所有優勢時起到決定性作用。 例如…
大小
按照今天的標準,KDB+ 的大小只是微觀。 它實際上是一個亞兆字節的可執行文件和一個具有某些系統功能的小文本文件。 實際上,不到 XNUMX 兆字節,而且公司每年為該程序的服務器上的一個處理器支付數万美元。
- 這個大小使 KDB+ 在任何硬件上都感覺很棒,從 Pi 微型計算機到具有 TB 內存的服務器。 這不會以任何方式影響功能,而且,Q 會立即啟動,這使得它可以用作腳本語言等。
- 有了這個大小,Q 解釋器就完全適合處理器高速緩存,從而加快了程序的執行速度。
- 有了這個大小的可執行文件,Q進程佔用的內存空間可以忽略不計,你可以運行數百個。 同時,如果有必要,Q 可以在單個進程中使用數十或數百 GB 的內存進行操作。
多功能性
Q 適用於各種各樣的任務。 Q 進程可以充當歷史數據庫並提供對數 TB 信息的快速訪問。 例如,我們有幾十個歷史數據庫,其中一些數據庫一天未壓縮的數據超過 100 GB。 然而,在合理的限制下,對數據庫的查詢將在幾十到幾百毫秒內完成。 一般來說,我們有一個通用的用戶請求超時時間——30 秒——而且它很少起作用。
Q 可以很容易地成為一個內存數據庫。 向內存表中添加新數據的速度如此之快,以至於用戶請求成為了限制因素。 表中的數據存儲在列中,這意味著列上的任何操作都將滿負荷使用處理器緩存。 除此之外,KX 還試圖通過矢量處理器指令來實現算術等所有基本操作,從而最大限度地提高速度。 Q 還可以執行數據庫不常見的任務 - 例如,處理流數據並“實時”計算(延遲從幾十毫秒到幾秒,具體取決於任務)不同時間金融工具的各種聚合功能間隔或建立完美交易對市場影響的模型,並在交易完成後幾乎立即進行分析。 在這樣的任務中,最常見的主要時間延遲不是由 Q 引入的,而是由於需要同步來自不同來源的數據。 由於數據和處理它們的函數在一個進程中,並且處理減少到執行幾個不解釋的 QSQL 表達式和連接,而是通過二進制代碼執行,因此實現了高速。
最後,任何服務流程都可以寫在Q上。 例如,自動將用戶請求分發到所需數據庫和服務器的網關進程。 程序員可以完全自由地實施任何算法來平衡、優先級排序、容錯、訪問權限、配額以及您想要的任何其他內容。 這裡的主要問題是你必須自己實現所有這些。
例如,我將列出我們有哪些類型的流程。 它們都被積極使用並協同工作,將數十個不同的數據庫組合成一個整體,處理來自多個來源的數據並為數百個用戶和應用程序提供服務。
- 數據源的連接器(進紙器)。 這些進程通常使用加載到 Q 中的外部庫。Q 中的 C 接口非常簡單,可以輕鬆地為任何 C/C++ 庫創建代理函數。 Q 的速度足以同時處理來自所有歐洲證券交易所的大量 FIX 消息。
- 數據分發器(tickerplant), 它充當連接器和消費者之間的中間鏈接。 同時,他們將傳入的數據寫入特殊的二進制日誌,為消費者提供連接丟失或重新啟動的抵抗力。
- 內存數據庫 (rdb)。 這些數據庫通過將原始數據保存在內存中來提供最快的訪問權限。 通常,他們白天在表中累積數據,晚上將它們重置為零。
- 數據庫持久性 (pdb)。 這些數據庫確保今天的數據存儲在歷史數據庫中。 通常,與rdb不同,它們不將數據存儲在內存中,而是在白天使用磁盤上的特殊緩存,並在午夜將數據複製到歷史數據庫中。
- 歷史基地 (hdb)。 這些數據庫提供對前幾天、幾個月和幾年的數據的訪問。 它們的大小(以天為單位)僅受硬盤驅動器大小的限制。 數據可以位於任何地方,特別是在不同的磁盤上以便更快地訪問。 可以使用多種算法來壓縮數據以供選擇。 數據庫結構有據可查且簡單,數據按列存儲在常規文件中,以便可以對其進行處理,包括通過操作系統進行處理。
- 具有聚合信息的數據庫。 它們存儲各種聚合,通常是 c,按儀器名稱和時間間隔分組。 內存數據庫隨著每條傳入消息更新它們的狀態,而歷史數據庫存儲預先計算的數據以加快對歷史數據的訪問。
- 最後,該 網關進程服務應用程序和用戶。 Q 允許您實現傳入消息的完全異步處理、它們在數據庫之間的分發、檢查訪問權限等。 我注意到消息不受限制,而且大多數情況下不是 SQL 語句,就像其他數據庫中的情況一樣。 大多數情況下,SQL 表達式隱藏在一個特殊的函數中,並根據用戶請求的參數構造 - 執行時間轉換,執行過濾,對數據進行規範化(例如,如果有一個股票價格被拉平)股息支付)等。
一種數據類型的典型架構:

速度
儘管 Q 是一種解釋型語言,但它也是一種矢量語言。 這意味著許多內置函數,尤其是算術函數,接受任何形式的參數——數字、向量、矩陣、列表,並且程序員需要將程序實現為對數組的操作。 在這樣的語言中,如果您將兩個包含一百萬個元素的向量相加,語言被解釋並不重要,相加將由超級優化的二元函數完成。 由於 Q 程序中的大部分時間都花在了使用這些基本矢量化函數對錶進行操作上,因此我們在輸出方面具有非常不錯的性能,這使我們即使在一個進程中也能處理大量數據。 這類似於 python 中的數學庫——儘管 python 語言本身非常慢,但它有許多優秀的庫,如 numpy,可以讓你以編譯語言的速度處理數值數據(順便說一下,numpy 在意識形態上接近於問)。
此外,KX 在設計表格和優化工作方面採取了非常謹慎的方法。 首先,支持多種類型的索引,這些索引由內置函數支持,不僅可以應用於表列,還可以應用於任何向量——分組、排序、唯一屬性和歷史數據庫的特殊分組。 索引以基本方式疊加,並在向列/向量添加元素時自動調整。 索引同樣可以很好地疊加在內存和磁盤上的表列上。 執行 QSQL 查詢時,如果可能,會自動使用索引。 其次,歷史數據的處理是通過操作系統文件顯示機制(內存映射)完成的。 大表永遠不會加載到內存中,相反,必要的列直接顯示到內存中,並且實際上只加載其中需要的那部分(包括在這裡提供幫助的索引)。 對於程序員來說,數據是否在內存中並沒有什麼區別,使用mmap的機製完全隱藏在Q的深處。
KDB+ 不是關係數據庫,表可以包含任意數據,而表中行的順序不會隨著新元素的添加而改變,在編寫查詢時可以而且應該使用。 處理時間序列(來自交換、遙測、事件日誌的數據)時迫切需要此功能,因為如果數據按時間排序,則用戶無需使用任何 SQL 技巧來查找第一行或最後一行或 N表中的行,確定哪一行跟在第 N 行之後,依此類推。 表連接得到了進一步簡化,例如,在包含 16000 個 VOD.L 事務 (Vodafone) 的 500 億個元素的表中查找最後一條報價在磁盤上大約需要 XNUMX 秒,在內存中大約需要 XNUMX 毫秒。
按時間連接的一個例子是引用表被映射到內存,所以不需要在 where 中指定 VOD.L,sym 列上的索引和數據按時間排序的事實被隱式使用。 Q 中幾乎所有的連接都是普通函數,而不是 select 語句的一部分:
1. aj[`sym`time;select from trade where date=2019.03.26, sym=`VOD.L;select from quote where date=2019.03.26]
最後,值得注意的是,來自亞瑟·惠特尼 (Arthur Whitney) 本人的 KX 工程師是真正的效率狂熱者,他們不遺餘力地充分利用 Q 的標準功能並優化最常見的使用模式。
總
KDB+ 之所以受到企業的歡迎,主要是因為其出色的多功能性——它既可以作為內存數據庫,也可以作為存儲 TB 級歷史數據的基礎,以及作為數據分析平台。 由於數據處理直接在數據庫中進行,因此實現了高速和資源節約。 一種與數據庫功能集成的成熟編程語言允許您在一個平台上實現整個堆棧的必要流程——從接收數據到處理用戶請求。
欲了解更多信息,
限制
KDB+/Q 的一個顯著缺點是入門門檻高。 該語言的語法很奇怪,一些函數重載(例如,value 有大約 11 個用例)。 最重要的是,它需要一種截然不同的方法來編寫程序。 在矢量語言中,您必須始終從數組轉換的角度考慮,通過多個版本的 map / reduce 函數(在 Q 中稱為副詞)實現所有循環,永遠不要試圖通過用原子操作替換矢量操作來省錢。 例如,要查找數組中某個元素第 N 次出現的索引,您可以這樣寫:
1. (where element=vector)[N]
儘管按照 C/Java 標準這看起來非常低效(= 創建一個布爾向量,其中返回其中元素的真實索引)。 但是這樣的表示法使表達式的含義更加清晰,並且您可以使用快速向量運算而不是慢速原子運算。 矢量語言與其他語言之間的概念差異類似於命令式編程方法和函數式編程方法之間的差異,您需要為此做好準備。
一些用戶也對 QSQL 不滿意。 關鍵是它只是看起來像真正的 SQL。 實際上,它只是一個不支持查詢優化的類 SQL 表達式解釋器。 用戶必須自己編寫最佳查詢,並且在 Q 上,許多人還沒有準備好。 另一方面,當然,您始終可以自己編寫最優查詢,而不是依賴黑盒優化器。
此外,Q book - Q For Mortals 可免費獲得,網址為 , 也收集了許多其他有用的資料。
另一個很大的缺點是許可證的成本。 一個 CPU 每年要花費數万美元。 只有大公司才能負擔得起這樣的費用。 最近,KX 使許可政策更加靈活,並提供了僅按使用時間付費或在谷歌和亞馬遜雲中租用 KDB+ 的選項。 KX 也提供下載 (根據要求提供 32 位版本或 64 位版本)。
競爭對手
有很多專門的數據庫建立在類似的原則上——柱狀、內存中,專注於非常大量的數據。 問題是這些是專門的數據庫。 一個典型的例子是 Clickhouse。 這個數據庫在磁盤上存儲數據和為 KDB+ 建立索引的原理非常相似,它執行某些查詢的速度比 KDB+ 快,但速度並不快。 但即使 Clickhouse 數據庫比 KDB+ 更專業——網絡分析與任意時間序列(這種差異非常重要——正因為如此,例如,Clickhouse 無法使用記錄排序)。 但是,最重要的是,Clickhouse 沒有 KDB+ 的通用性,KDB+ 是一種允許直接在數據庫中處理數據的語言,而不是首先將其加載到單獨的應用程序中,構建任意 SQL 表達式,在查詢中使用任意函數,創建與歷史數據庫功能的執行無關的進程。 因此,很難將 KDB+ 與其他數據庫進行比較,它們在某些用例中可能更好,或者在涉及經典數據庫任務時更好,但我不知道還有另一種工具在處理臨時數據方面同樣有效和通用。
與 Python 集成
為了讓不熟悉該技術的人更容易使用 KDB+,KX 創建了庫以在單個進程中與 Python 緊密集成。 您可以從 Q 調用任何 python 函數,反之亦然 - 從 Python 調用任何 Q 函數(特別是 QSQL 表達式)。 如有必要(為了提高效率,並非總是如此),圖書館將數據從一種語言的格式轉換為另一種語言的格式。 因此,Q 和 Python 生活在如此緊密的共生關係中,以至於它們之間的界限變得模糊。 因此,程序員一方面可以完全訪問大量有用的 Python 庫,另一方面,他可以快速將基礎集成到 Python 中以處理大數據,這對於那些參與機器學習的人來說尤其有用或建模。
在 Python 中使用 Q:
1. >>> q()
2.q)trade:([]date:();sym:();qty:())
3. q)
4. >>> q.insert('trade', (date(2006,10,6), 'IBM', 200))
5. k(',0')
6. >>> q.insert('trade', (date(2006,10,6), 'MSFT', 100))
7. k(',1')
引用
公司地址——
開發者網站 -
Book Q For Mortals(英文)-
kx 工作人員關於 KDB+/Q 應用程序的文章 —
來源: www.habr.com
