一、背景介紹
日前,Apache Kylin 社區發布了全新架構的 Kylin 4.0。Kylin 4.0 的架構支持存儲和計算分離,這使得 Kylin 用戶可以採取更加靈活、計算資源可以彈性伸縮的雲上部署方式來運行 Kylin 4.0。藉助雲上的基礎設施,用戶可以選擇使用便宜且可靠的對象存儲來儲存 cube 數據,比如 S3 等。不過在存儲與計算分離的架構下,我們需要考慮到,計算節點通過網絡從遠端存儲讀取數據仍然是一個代價較大的操作,往往會帶來性能的損耗。
為了提高 Kylin 4.0 在使用雲上對象存儲作為存儲時的查詢性能,我們嘗試在 Kylin 4.0 的查詢引擎中引入本地緩存(Local Cache)機制,在執行查詢時,將經常使用的數據緩存在本地磁盤,減小從遠程對象存儲中拉取數據帶來的延遲,實現更快的查詢響應;除此之外,為了避免同樣的數據在大量 spark executor 上同時緩存浪費磁盤空間,並且計算節點可以更多的從本地緩存讀取所需數據,我們引入了 軟親和性(Soft Affinity )的調度策略,所謂軟親和性策略,就是通過某種方法在 spark executor 和數據文件之間建立對應關係,使得同樣的數據在大部分情況下能夠總是在同一個 executor 上面讀取,從而提高緩存的命中率。
二、實現原理
01
本地緩存
在 Kylin 4.0 執行查詢時,主要經過以下幾個階段,其中用虛線標註出了可以使用本地緩存來提升性能的階段:

File list cache:在 spark driver 端對 file status 進行緩存。在執行查詢時,spark driver 需要讀取文件列表,獲取一些文件信息進行後續的調度執行,這裡會將 file status 信息緩存到本地避免頻繁讀取遠程文件目錄。
Data cache:在 spark executor 端對數據進行緩存。用戶可以設置將數據緩存到內存或是磁盤,若設置為緩存到內存,則需要適當調大 executor memory,保證 executor 有足夠的內存可以進行數據緩存;若是緩存到磁盤,需要用戶設置數據緩存目錄,最好設置為 SSD 磁盤目錄。除此之外,緩存數據的最大容量、備份數量等均可由用戶配置調整。
基於以上設計,在 Kylin 4.0 的查詢引擎 sparder 的 driver 端和 executor 端分別做不同類型的緩存,基本架構如下:

02
軟親和性調度
在 executor 端做 data cache 時,如果在所有的 executor 上都緩存全部的數據,那麼緩存數據的大小將會非常可觀,極大的浪費磁盤空間,同時也容易導致緩存數據被頻繁清理。為了最大化 spark executor 的緩存命中率,spark driver 需要將同一文件的 task 在資源條件滿足的情況下儘可能調度到同樣的 executor,這樣可以保證相同文件的數據能夠緩存在特定的某個或者某幾個 executor 上,再次讀取時便可以通過緩存讀取數據。
為此,我們採取根據文件名計算 hash 之後再與 executors num 取模的結果來計算目標 executor 列表,在多少個 executor 上面做緩存由用戶配置的緩存備份數量決定,一般情況下,緩存備份數量越大,擊中緩存的概率越高。當目標 executor 均不可達或者沒有資源供調度時,調度程序將回退到 spark 的隨機調度機制上。這種調度方式便稱為軟親和性調度策略,它雖然不能保證 100% 擊中緩存,但能夠有效提高緩存命中率,在儘量不損失性能的前提下避免 full cache 浪費大量磁盤空間。
三、相關配置
根據以上原理,我們在 Kylin 4.0 中實現了本地緩存+軟親和性調度的基礎功能,並分別基於 SSB 數據集和 TPCH 數據集做了查詢性能測試。
這裡列出幾個比較重要的配置項供用戶了解,實際使用的配置將在結尾鏈接中給出:
是否開啟軟親和性調度策略:kylin.query.spark-conf.spark.kylin.soft-affinity.enabled
是否開啟本地緩存:kylin.query.spark-conf.spark.hadoop.spark.kylin.local-cache.enabled
Data cache 的備份數量,即在多少個 executor 上對同一數據文件進行緩存:kylin.query.spark-conf.spark.kylin.soft-affinity.replications.num
緩存到內存中還是本地目錄,緩存到內存設置為 BUFF,緩存到本地設置為 LOCAL:kylin.query.spark-conf.spark.hadoop.alluxio.user.client.cache.store.type
最大緩存容量:kylin.query.spark-conf.spark.hadoop.alluxio.user.client.cache.size
四、性能對比
我們在 AWS EMR 環境下進行了 3 種場景的性能測試,在 scale factor = 10的情況下,對 SSB 數據集進行單並發查詢測試、TPCH 數據集進行單並發查詢以及 4 並發查詢測試,實驗組和對照組均配置 S3 作為存儲,在實驗組中開啟本地緩存和軟親和性調度,對照組則不開啟。除此之外,我們還將實驗組結果與相同環境下 HDFS 作為存儲時的結果進行對比,以便用戶可以直觀的感受到 本地緩存+軟親和性調度 對雲上部署 Kylin 4.0 使用對象存儲作為存儲場景下的優化效果。



從以上結果可以看出:
在 SSB10 數據集單並發場景下,使用 S3 作為存儲時,開啟本地緩存和軟親和性調度能夠獲得3倍左右的性能提升,可以達到與 HDFS 作為存儲時的相同性能甚至還有 5% 左右的提升。
在 TPCH10 數據集下,使用 S3 作為存儲時,無論是單並發查詢還是多並發查詢,開啟本地緩存和軟親和性調度後,基本在所有查詢中都能夠獲得大幅度的性能提升。
不過在 TPCH 10 數據集的 4 並發測試下的 Q21 的對比結果中,我們觀察到,開啟本地緩存和軟親和性調度的結果反而比單獨使用 S3 作為存儲時有所下降,這裡可能是由於某種原因導致沒有通過緩存讀取數據,深層原因在此次測試中沒有進行進一步的分析,在後續的優化過程中我們會逐步改進。由於 TPCH 的查詢比較複雜且 SQL 類型各異,與 HDFS 作為存儲時的結果相比,仍然有部分 SQL 的性能略有不足,不過總體來說已經與 HDFS 的結果比較接近。
本次性能測試的結果是一次對 本地緩存+軟親和性調度 性能提升效果的初步驗證,從總體上來看,本地緩存+軟親和性調度 無論對於簡單查詢還是複雜查詢都能夠獲得明顯的性能提升,但是在高並發查詢下相比單並發查詢存在一定的性能損失。
如果用戶使用雲上對象存儲作為 Kylin 4.0 的存儲,在開啟 本地緩存+ 軟親和性調度的情況下,是可以獲得很好的性能體驗的,這為 Kylin 4.0 在雲上使用計算和存儲分離架構提供了性能保障。
五、代碼實現
由於目前的代碼實現還處於比較基礎的階段,還有許多細節需要完善,比如實現一致性哈希、當 executor 數量發生變化時如何處理已有 cache 等,所以作者還未向社區代碼庫提交 PR,想要提前預覽的開發者可以通過下面的鏈接查看源碼:
https://github.com/apache/kylin/commit/4e75b7fa4059dd2eaed24061fda7797fecaf2e35
相關鏈接
想要了解更多詳情,可通過鏈接查閱性能測試結果數據和 kylin.properties:
https://github.com/Kyligence/kylin-tpch/issues/9