Apache Arrow原始碼分析(一)——簡介和框架
背景
列儲存在資料庫領域中早已被提出,列儲存資料結構在分析型事務上表現優異,大資料分析引擎,諸如Spark-SQL,Impala 均採用列儲存作為其中間資料表示形式,那麼Apache Arrow就是這樣一種記憶體列式資料結構。
在眾多分散式系統中,每個系統都有自己內部的記憶體格式,70-80%的CPU浪費在序列化和反序列化過程,類似功能在多個專案中實現,沒有一個標準。那麼Arrow就是為解決這一問題而提出了。Arrow基於列儲存的資料組織方式,能夠很好的適應CPU的Cache,以及現代CPU SIMD (Single input multiple data) 技術,並且提供良好的序列化和反序列化效能。更值得一提的是,Arrow提供良好的擴充套件性,既有C++版的實現,又有java版的實現,是一個很值得學習的資料結構庫。
基本思想
Arrow 基本思想就是把向量在記憶體中的佈局給緊湊話,說白了,就是把之前按行表示的資料按列進行組織。
舉一個很簡單的例子,對於一個字串向量{“a”, “bb”, “”, “”, “ccc”},通常在c++中我們採用vector< string >進行儲存vector<string> strings = {"a", "bb", "", "", "ccc"};
, 採用這種儲存方式,其記憶體佈局大致如下圖所示:
由於STL容器string中儲存字串的空間在堆上分配,那麼每一個string物件會單獨在堆上申請記憶體空間,儲存字串,這就可能導致在記憶體中,vector< string > 儲存空間並非連續的。這樣的儲存會帶來兩個問題,其一在於,不能很好的適應CPU cache,其二在於序列化和反序列化代價較大。
那麼Apache Arrow將vector< string >進行變形,轉換成如下的儲存方式:
引入一個Offset向量,以及連續記憶體空間的buffer,對外提供和vector類似的介面。其中,對於string 的訪問介面實現如下所示:
const uint8_t* GetValue(int64_t i, int64_t* out_length) const {
int32_t pos = offsets_[i];
*out_length = offsets_[i + 1 ] - pos;
return raw_bytes_ + pos;
}
std::string GetString(int64_t i) const {
const uint8_t* str = GetValue(i, &nchars);
return std::string(reinterpret_cast<const char*>(str), nchars);
}
引入偏移量向量來對連續的buffer空間進行索引,從而對外提供和vector< string >類似的取值介面。
框架和類圖
Apache Arrow類的設計採用建造者模式,封裝了基礎類array,buffer,以及arraybuilder,具體類圖如下圖所示: