您的位置:首頁>科技>正文

鏈家大資料多維分析引擎實踐

前言:大資料背景下, 傳統關聯式多維分析 ROLAP 引擎遇到極大挑戰, 因而鏈家轉向基於 Hadoop 生態的 MOLAP(Kylin)及 HOLAP (多引擎)。 在由七牛雲和鏈家聯合主辦的架構師實踐日北京站中, 鏈家大資料集群架構組負責人鄧鈁元進行演講, 分享了鏈家在多維分析引擎方面的一些實踐經驗, 主要從 OLAP 的背景和簡介、鏈家多維分析架構演進和展望、OLAP 平臺鏈路優化這三部分來介紹。

鄧鈁元, 鏈家網大數據資深研發工程師。 2015 年開始負責鏈家大資料集群建設, 專注於 Hadoop 生態組的定制開發及應用, 深入 Hadoop/HBase 等源碼, 成為 contributor 回饋社區。 擅長底層性能調優,

打造公司級計算存儲平臺。

一、OLAP 的背景和簡介

1. OLAP vs OLTP

OLAP 翻譯成中文叫連線分析處理, OLTP 叫連線交易處理。 OLTP 它的核心是事務, 實際上就是我們常見的資料庫。 我們業務資料庫就是面向於事務。 它的併發量會比較高, 但是操作的資料量會比較小。 它是即時更新的。 資料庫的設計會按照 3NF 範式, 更高的話可能會按照 BC 範式之類的來做。 而 OLAP 的核心是分析, 面向應用是分析決策, 需要分析的資料級會非常大, 可能 TB, 甚至 PB 都會有。 它的資料更新會稍微慢一些, 它的設計一般是反範式的, 因為面向分析。 常見的是雪花模型和星型模型。

剛才說的比較空洞, 實際上 OLAP 是什麼呢?非常簡單, 就是一個 SQL, 這裡按照兩個維度, 一個 returnflag, 一個 orderstatus 來做 Group By, 然後做一下 Sum, Group By 這段就叫維度,

From 這段叫做指標, 非常簡單。

2. OLAP 引擎分類

OLAP 引擎的一些常見分類大概有這幾種。

第一種叫 ROLAP, 叫關聯式 OLAP, 它的特點就是它是基於關係性模型, 計算的時候, 根據原始資料去做聚合運算。 常見的實現, 小資料量可以利用 MySQL、Oracle 這種傳統資料庫, 而大資料量可以利用 Spark SQL、Presto 這些項目。

第二種類型叫 MOLAP, 叫多維 OLAP, 它的特點就是它會基於一個預定義的模型, 我需要知道, 要根據什麼維度, 要去算哪些指標, 我提前就把這些結果弄好, 存儲在引擎上。 當查詢的時候, 根據結果簡單地做一做匯總就可以得出來。 今天主要會講到 Kylin 引擎, 螞蜂窩的汪老師後續會講到 Druid 引擎。 大家也可以做一下對比。

3. HOLAP

最後一種叫 HOLAP, 叫混合 OLAP, 這個就非常簡單了, 就是兩個雜交一下。 根據業務場景路由不同的引擎。

圖一這是簡單的 ROLAP 模型, 具體操作流程剛才已經提到了, 它的優勢就是, 它其實就是個資料庫, 所以任何的 SQL 都可以在裡面執行。 資料是沒有冗餘的。 缺點就是資料量很大的時候, 計算速度會下降很多, 所以併發會比較差。 它的場景就是不知道要查什麼資料, 靈活性非常高時, 一般會選 ROLAP。

4. MOLAP

第二種 MOLAP, 剛才提到 MOLAP 主要是要定義一個模型, 比如說圖2樣例裡面定義了三個維度:Time, Web page, Action。 從這三個維度把所有的組合提前預計算好, 存在一個存儲引擎中, 需要的時候裡面取出來做一下聚合就可以, 所以它的原始資料支援非常大, 查詢速度, 因為直接算好了, 可以很快返回。 它的缺點就是因為聚合了以後, 就查不到明細資料。 它的靈活性稍微差一點, 因為需要預先去定義維度, 還有指標。 所以說是需要能夠知道查詢的模式, 才能用這個 MOLAP。

1. 早期分析資料實現

剛才呂毅已經講到, 我們剛開始做大資料的時候, Hadoop 組建基本上都是基於開源的。 我們會從日誌、Kafka 去導數據, MySQL 資料會用 Sqoop 同步到 Hadoop 中。 當時是用一個開源的調度引擎 Ooize, 我們根據業務需求去建表, 然後把資料導到 MySQL 中, 因為要做呈現, 必須要在一個比較快的返回的資料庫中。 所以放在 MySQL。

它的缺點是什麼?當它的資料量越來越大, MySQL 做存儲, 它的擴展性是非常不好的。 資料量大以後, 第二個問題速度會比較慢。第三個,面臨分析的維度非常多,每個維度的分析,都要一個資料開發做需求,平均做一個需求時間可能要兩周。

2. 技術選型

能不能用一些 OLAP 引擎來簡化操作?我們總結了對 OLAP 引擎的一些需求。

第一,回應要快。因為業務分析人員等不了太久。需要一定併發。最好有一個 SQL 介面。支援資料級要非常大。當然離線方面,我們目前是 T+1 的模式,所以說綜合考慮選擇了 Kylin。Kylin 就是基於 Hadoop 之上的提供 SQL 查詢和多維分析的能力,它能支援超大規模資料級。它能在亞秒返回巨大的一個查詢。實際上它就是標準的 MOLAP 方案,我們需要預先定義維度和指標,提前預計算它的 Cube,把結果存在 HBase,查詢時解析 SQL,根據路由把它到 HBase 到對應的表中去拿取資料。

圖4是 Kylin 的架構圖,最下麵是構建引擎,左邊是 Hadoop 資料倉庫,右邊是 HBase。最下面會根據 Hadoop 不同的原始資料進行構建,然後把資料存在 HBase。查詢的時候,在 Query Engine 做解析,解析完了以後,下面路由選擇,去 HBase 中查對應的資料。左邊大家看到有根虛線,這個是什麼意思呢?剛才提到 MOLAP 只能支援預聚合的資料,要查原始的明細資料應該怎麼辦,Kylin 是不支持的。這條虛線劃出來,官方說會在後續某個版本支持,但實際上一直沒做。後面會講到我們是怎麼解決這個問題的。

再介紹一下鏈家 Kylin 使用統計,定位是離線的 OLAP 引擎。線上有 100 多個 Cube,公司 8 個業務現在使用,總共存儲容量應該也到 30T。總的資料量應該 800 億行左右,單個 Cube 最大已經到 40 億行。每天的查詢量大概有 10 萬多,查詢性能還是非常好的,95% 能在 500ms 以內,99% 都在 1s 中返回。除開那種超大的,可能會在 10s 左右,還是非常滿足我們需求的。

3. 鏈家 OLAP 平臺架構

圖5是鏈家整個 OLAP 平臺的架構。首先核心還是基於 Kylin 這樣一個 MOLAP 引擎,我們 Kylin 做了讀寫分離的部署,也做了負載均衡的高可用。Build 機器是構建任務的機器,下面對接的是主要的 Hadoop 集群,負責所有的資料倉庫存儲和所有的計算。我們有自研的調度系統,每天會去調度 Kylin 的構建任務。關於調度系統,其實跟剛才党老師的調度非常像,調度提供了很多功能,比如說分散式調度,會根據機器的負載情況去分發任務,會做任務的依賴,會做任務的監控等等。當然今天的重點不是它。

第二塊就是 HBase 集群,是個單獨的集群。為什麼這麼做呢?如果 HBase 跟 Hadoop 在一起,Hadoop 一旦運行大的任務,記憶體壓力大的時候,HBase 就會性能非常差,所以把查詢服務獨立出來。所有的查詢 Query 機器都會去查 HBase。上面指標分析平臺就是鏈家視覺化的分析平臺,它底層的引擎主要就是 Kylin,它所有的預建模的查詢都會走 Kylin,當然會做緩存,把那些常用的 SQL,重複的 SQL 緩存住。

第二塊就是剛才提到的,如果有查明細資料該怎麼辦?鏈家這邊集群組做了另一個引擎叫 Query Engine,這是我們自己的。它主要是給另外一個業務提供服務,提供即時查詢服務。它底層是兩個引擎,Presto 和 Spark SQL。在指標上會把明細的查詢和靈活的查詢,如果 Kylin 不支持,就會把它轉到 Query Engine,Query Engine 會根據它的 Cost來做優化,來選擇 Presto 或者 Spark SQL 來查詢。這樣就解決了明細查詢的問題。

最後一塊就是預警監控,我們所有模組都有 Supervisior 存活監控,保證它掛了以後能啟動,整個鏈路都會上報到 OP 部門 維護的基於 Falcon 的監控平臺。

圖6是報表查詢頁面,這裡會顯示我有許可權的報表,在裡面搜索一下,我想看的一些報表。這是一張,首先選擇一個時間的維度,然後這裡可以篩選很多條件,比如說根據分公司,根據店組去查。比如想看東北區的詳情,點進去就可以顯示到這個區裡面所有店主。如果需要看這個店主再點進去,就可以到這個店主裡面的每個人。這個在 OLAP 領域,這個過程叫做下鑽的過程。如果往回這個過程叫上卷,相當於把那個結果再往回聚合。我們感覺 UI 設計的還可以。

然後圖7這是我們自研的 Cube 管理。如果大家有用過 Kylin 的話,應該覺得主要的功能都是差不多的,因為都是根據 Kylin 來做定制的。比如說選擇維度,選擇指標。然後可以設置一些參數,可以在這裡面做查詢。它的可能就是它對接了我們許可權平臺,能夠自己對它做管理。

總結一下鏈家 OLAP 的一些特色。一是自研的視覺化平臺,支持上卷下鑽,維度對比,視覺化報表創建,指標管理。開源的時候,其實有些開源產品比如說叫 Saiku,有些公司有應用。相比於它的話,個人感覺 UI 還是美觀一些,更切近業務,方便靈活定制。我們的引擎能力,其實並不是簡單的 MOLAP,而是一個混合的 OLAP 模型,既支援明細查詢,又支援彙總查詢。針對需求實現了跨 Cube 查詢的功能。就是 Kylin 中,一個 Cube 對應一張即時表。一個 Cube 裡面只能查一張即時表的資料。假如說有多個即時表,但是我查的維度是類似的,我想把結果做聚合運算,Kylin 是不支持的,我們平臺裡面支持了跨 Cube 查詢。然後在這裡面完整監控,有高可用的架構。Cube 管理剛才也提到,前端會做一些簡化,對接了許可權,簡化配置,提升我們的管理效率。

5. OLAP 展望

下面是我們 OLAP 下半年的展望。

一是擴展能力。剛才提到已經有跨 Cube 查詢,但是隨著業務增長,可能有更多的需求。比如說想把 Kylin 的資料去跟 Hive 裡的資料做計算,或者業務那邊接入他們自己的 Oracle,裡面放了一些他們不想給我們的資料,但是想跟我們的資料做一些混合運算,我們這邊做一個多中繼資料查詢。

第二塊會做一些更多的路由優化,優化查詢效率。第三就是 Kylin 目前它的來源資料同步,Build 機器相當於 Master,Query的機器相當於 Slave,目前是 Push 的模式,Build 機器 build 好了,把一些中繼資料同步過去。但是缺點是有時候會出現失敗的情況。我們目前做得非常 low,就是用 Crontab,定期讓 Slave 去重新刷一下緩存。

後期我們正在做的就是讓 Slave 用心跳機制,Master 那裡用一個狀態機模型,把每一個要下發的任務放在狀態機裡,Slave 心跳了之後來取這個狀態。當我更新完緩存以後,我在下次心跳的時候,把這個狀態再置為已經更新。其實這個非常類似於 Hadoop 裡面的 YARN 架構,都是基於狀態機的。這樣的話,擴展性會非常好,減少 Master 的一些壓力。我們正在做 Kylin 2.0 調研,比如它支援雪花模型,不用轉化資料倉庫。支援 Spark 構建,提升速度。

第三塊,我們目前的 OLAP 還是一個離線服務,隨著業務的發展,我們有更多即時需求。主要是 MOLAP 裡面的一些即時的方向,一個是 Kylin 自身支持 Streaing Cubing,類似於 Spark Streaming,實際上是小批量近即時的,做到分鐘級。另外我們也考慮 Druid 或者 Palo 這些架構,它們是 Lambda 的架構。就是記憶體中會維護最新的狀態,這樣可以做到純即時的更新。

三、OLAP 平臺鏈路優化實踐

1. Kylin 全鏈路架構

最後講一些比較接地氣的,我們在 OLAP 平臺全鏈路的一些優化經驗。首先看一下 Kylin 的整個鏈路。最上頭的 Kylin,它的存儲引擎是 HBase,HBase 又基於 HDFS。所有程式都跑到了JVM 上。首先是 Kylin 本身有一些優化,我們知道假如我們定義了 N 個維度,我查詢的時候,每一個維度可選擇或者不選擇,其實有 2 的 N 次方種選擇。比如說 A、B、C、D 四個維度,這裡頭一共有 16 種維度選擇。這個 Kylin 裡面會提供很多方式來避免這種維度的膨脹。我這兒簡要介紹一下。見圖8。

第一個是聚合組,用戶查詢的時候不會所有條件都查,而是同時只會出現某幾個維度,我們就把這幾個維度定義為一個聚合組。比如說這裡就用了 A、B、C 和 B、C、D。於是只有 A、B、C 內部的這些維度和 B、C、D 內部維度會進行計算,而且如果兩個有交叉,就會計算一份。其他的各種衍生維度、強制維度、層次維度,大家如果做 Kylin 引擎,後續可以在這塊著重關注一下,我這邊就不詳細解釋了。反正原理都是儘量根據查詢的一些條件或者業務的一些條件,儘量減少要計算的維度搭配。

2. Kylin 改造以及優化

我們在 Kylin 裡面做了很多改造和優化,第一個是 Kylin 默認只能在 HBase 中,放在預設庫裡面,首碼是 Kylin 加底線。我們如果要在上面部署多套環境就會有衝突,我們這邊修改了一下,讓它支援配置一個首碼,可以在一套 HBase 中部署多套 Kylin。我們之前講漏了一件事兒,剛才架構圖裡面有一個預上線的 Kylin,我們所有的,因為上線前都會在預上線的 Kylin 裡面進行驗證,然後只有它的資料 OK 了以後才會上到線上,預上線的環境。還有第二個功能就是雙寫。假如一些重要的業務,我會在兩個集群裡邊同時構建。如果主集群先出現問題,立馬切到備用集群。我們修復了它裡面的一個鎖死的問題,主要是更新緩存部分,這個引擎加了社區。

然後接下來是一些,比如說構建的時候,MR 引擎的一些優化,比如開啟壓縮等等。Kylin 也是記憶體型的查詢服務,它是 Java 寫的,所以說自然 GC 的問題,等一下會詳細講一下。如果你使用全域字典,你是要調大一些 MAP 記憶體。定期會做 Segment 的合併,清理一些過期資料,減少 HBase Region 個數。因為 HBase 如果 Region Server 太多,壓力非常大。

最後還有一個默認使用的授權的時候,有個 Byct 加密,這個非常影響性能。後續我會講如何定位這個問題。

3. 系統性能調優

關於系統調優給大家推薦一個工具叫火焰圖,見圖9。火焰圖是什麼呢?它就是一個視覺化的去展示 CPU 佔用情況的圖。每一個小方框裡面都是一個函數,水準方向看它的寬度就是 CPU 的時間佔用,垂直方向看就是一個函數的堆疊。下面的函數就會調用上面的函數。所以自然上面函數的時間會累加到下面函數中。

如何定位性能的問題呢?從下往上看,可以理解成,如果一個山峰一直沒有縮短,到最頂層的時候,調用堆疊最上面的一個函數,那是最終調用的函數,如果它佔用非常多,一定是它的性能問題。

我們之前發現 Kylin 在高併發的時候,CPU 會滿載,非常高,但是查詢非常簡單。利用火焰圖去分析發現,它有 80% 的時間在做一個 Spring 的 Bcybt 加密的驗證。然後右邊那一小塊占 4% 的,這個才是 Kylin 的查詢。我們查了一下,其實 Bcybt 加密是一個比較好,相當於是比較不容易破解的加密。它的方式就是讓它每次計算非常耗性能,降低它的速度。我們 將 Bcybt 加密換成了其他加密的演算法。大家可以看到,這段(Kylin 查詢部分)就是之前 4% 的時間,因為我把其他時間消掉了,所以它的占比就大幅度提升,提升到 40%。提升了大概 1 倍 QPS。

第二塊就是 Java GC。其實 Java 大家應該還是比較熟悉的。像傳統的 CMS 非常經典,但是它的缺點就是對於大記憶體回收會有問題,而我們現在線上基本開到 80G,甚至 90G,CMS 在這種情況下基本上是 STW 非常長,可能要達到幾分鐘。G1 是 Java 最新的垃圾回收演算法。它的核心還是保留了 CMS 的分代概念,但是它把每一代分成了很多 Region,打散。G1 定位是暫停時間可控的 GC,它會根據你設定暫停時間,在暫停時間內盡可能多地回收記憶體。因為打散成了很多 Region,我可以不全部回收,我可以回收部分,保證我每次回收的時間可控。因為新生代的 GC 是發生非常頻繁的,所以我們要控制新生代的大小,保證每次回收時間可控。

圖9是 GC 調優後的對比圖,上面是 CMS,下面是 Java。可以看到,GC 頻率和時間都有大幅地縮短。

第二塊就是我們 Java 的存儲是放在 Hbase 中,HBase 又放在 HDFS 中,HDFS 的底層就是物理上的磁片。我想提升 Java 的查詢性能,從硬體上我就想,現在主流的磁片 IOPS 可能就幾百,但是現在的 SSD,普通的 SSD 能達到幾萬,像 PCI 組建的 SSD 現在已經達到 30、40 萬 IOPS。我們今年年初做了一件事情,我們把 Hadoop 集群從 2.4 升到了 2.7,2.7 裡面引入了一個非常重要的特性,叫異構存儲,或叫混合存儲。就是 Hadoop 支援我把我的磁片標記一個標籤,支援四種標籤。第一種叫記憶體磁片,第二種叫 SSD,第三個是普通磁片,最後一種是歸檔,歸檔是一種性能非常差的磁片,可以認為。定義了存儲策略。比如說 ALLSSD 是指我的所有副本全放在 SSD 中,ONE-SSD 就是放一份,放在 SSD 中。預設是 Hot 全部放磁片。我們這邊會把 HBase 的核心日誌,還有核心業務都會放在 ALLSSD 中,對重要業務會使用 ONE-SSD,如果是普通業務放在磁片上就行了。

剛才提到,我們其實為了節省成本,如果用 SSD 也是主要用 ONE-SSD。ONE-SSD 有一個問題,假如說這個用戶端讀取資料,我有三個備份。其中一個是 SSD,但是我本地的副本是一個機械磁片,預設的社區版會優先讀取本地,因為它的想法就是網路延遲是比較高的。但實際上我們鏈家大資料已經全部是萬兆的架構,所以說網路瓶頸已經是不存在的。我們這裡給它定制了一個 First 選擇策略,叫 SSD-FIRST。就是如果我遠端有 SSD,我還是優先使用遠端的。後面會給一個對比資料,這個已經提交給社區。

圖10是一個 HBase 就是讀寫分離。HBase 預設所有佇列和處理執行緒實際上是不區分的,我查詢請求分 Scan、Get、Write。三種請求都會打到相同的佇列裡面。如果出現了 Scan,把所有佇列和執行緒都堵滿了以後,簡單的小查詢都會卡住。這個是 HBase 在 1.2,還是 1.3 的時候已經實現了,我們可以把佇列和執行緒按照比例去分配給 Scan、Get、Write 三種請求,避免 Scan 請求阻塞小請求。

圖11是我們 HBase 的測試資料。大概是三台機器,使用 ONESSD 測試工具。我們就關注綠色這條線,這條線就是輸送量,或者叫 QPS 。最右邊是 HDD,當我們使用 ONE-SSD 以後,大概能提升 4 倍左右的吞吐。我們再作用,上線 SSD-FIRST 策略以後,又能提升一倍的吞吐。最後在上線讀寫分離以後又能提升一倍,整體提升的大概 10 多倍。而且只有第一步是需要硬體成本的,後面的兩個策略都是純策略上的調整。

圖12還有很多常見的一些優化項,我這邊就不詳細解釋了。主要是針對 Hadoop 的一些讀寫優化。

下面講一些作業系統常見的一些調優。首先給大家講一個最常見的命令 Free,運行出來能顯示當前記憶體的情況。我這裡寫了三種境界,第一種是剛接觸 Linux 同學,Free 是 0,是不是記憶體不夠了,是不是感覺要掛。經過一段時間學習,發現 Cache,Buffer 都是可以拿來用的。看第二行, Free 是 4G,記憶體什麼問題都沒有。真正老司機是什麼樣的呢?我們要知道 Cache 是用來幹什麼的,使用的命中率如何,Cache 是幹什麼的,Cache 預設就是 Page Cache,就是頁面緩存,主要是為了快取檔案系統,它在 VFS 裡面會用到的。然後一般的 Cache 都是可以釋放出來給程式使用的。但是有一種情況 Cache 不能釋放。就是有一種檔案系統叫 tempfs,相當於記憶體的一個檔案系統,利用了 Page Cache 的空間來做存儲,這種資料是沒法被清掉的。所以 Cache 並不是所有的可以用。如果拿來做檔緩存,本身作業系統這麼設計一定是有它的作用,我們想知道這個緩存命中率如何。

我看了一下網上各種工具,實際上沒有一個對緩存命中率計算的方法。還是剛才提到做火焰圖那哥們,在他的博客上公佈了一個利用 ftrace 的方案,原理其實非常簡單。命中率我們現在算 100%-Miss 的概率,Miss 是什麼情況呢?Miss 的分母就是 Page 的訪問次數,分子是,如果發生了 Misses,我就會把它從磁片上讀出來,再放到緩存中。所以說分子就是添加到 Page 緩存的一個次數。我們這邊因為 ftrace 會在系統上有問題,我用 System Tap 把腳本改寫了一下,著重是四個系統的調用,System Tap 就是一個動態跟蹤技術,它可以在內核任何一個函 數上動態去注入一段代碼,然後可以規定在函數運行前或者是運行後,我們這裡只是給了四個篩檢程式,就是在四個條件上面做了過濾。

下面大家看一下 Total,裡面第一項叫 Mark Page Accessed,就是頁面訪問次數,Miss 的第一項就是添加到 LRU 緩存的一個次數。後面減掉的一部分是什麼呢?大家知道,頁面緩存除了去讀的時候用到,寫的時候也會用到。任何一次寫入都會寫入到 Page 中。有時候我們要從分子分母中把寫入的那部分資料給減掉。我們會把 Cache 的命中率定期同步到我們裡面做監控。我們線上現在應該能達到 70%-90% 的命中率。據我們的試驗,一般在 60% 以上,HBase 會比較穩定,因為 HBase 對於 IO 性能要求非常高。如果 Cache 命中率非常高,實際上每次讀寫都會非常快。當然這個也跟業務有關係。如果你的查詢是非常隨機的話,這個緩存也很容易被弄髒。

下面講一個比較大的坑:NUMA。現在大家的伺服器基本上都是多個 CPU,常見的有兩種架構,一種叫 SMP,對稱多處理。記憶體其實是跟 CPU 之間連著一根匯流排。它的問題就是隨著CPU 越來越多,記憶體越來越大,所有的瓶頸都壓在匯流排上。接下來英特爾公司就想到另一個方案,就是我每個 CPU 給它自己專屬的記憶體,這個 CPU 的專屬記憶體存取速度非常快。現在記憶體非常大,每個地方的專屬記憶體應該是足夠的,如果我實在是需要遠端記憶體,我再走主線。預設是一個親和模式,什麼意思呢?就是我這個 CPU 分配的記憶體和淘汰的記憶體都會優先從我的專屬記憶體中來找,比如說我專屬記憶體不夠用了,我就會優先從我專屬記憶體裡去淘汰。這樣會導致一個問題,我系統看系統非常充足,但是我發現有 SWAP 佔用,我的 Java 在做 GC。這個就非常奇怪。其實就是因為預設的親和模式造成的。這個親和模式實際上對小記憶體應用非常好的,因為我一看 CPU 記憶體其實也很大了,至少 64G、128 個 G 都有,因為總共可能 256、512。單個小記憶體應用記憶體是足夠的,但是像 HBase 或者說資料庫,我們現在可以到 80G,甚至 100G,實際上單個 CPU 的專屬記憶體是不夠用的。我們這兒主要做了一個調整策略,就是把它的分配參數調整了一下。如果專屬記憶體不夠的時候,我允許從其他地方獲取記憶體,而不是去做本地的回收。

最後一個也是比較大的一個坑,就是透明大頁。什麼是透明大頁呢?大家知道,現在作業系統都是分頁的,每一個程式有一個頁表。程式分配都是邏輯位址,它要真的訪記憶體要通過一次邏輯位址到物理位址映射,然後 TLB 是用來做一個加速的,現在預設的大小是 4K,隨著記憶體越來越大就會有問題,頁表會越來越長,TLB 緩存命中率也會降低。作業系統想到一個方式,我就把配置調大,就給了兩個選項,一個是 2M,最大可以開到 1G。這樣看起來非常完美,但是在 6.5 的 centOS 中,這個大頁是默認開啟的,但是我們會發現 HBase 在使用過程中,CPU 佔用會非常異常,System Tap CPU 會非常高。大家可以看,JVM 的使用才 24%,但是我內核達到 32%,我們用 Perf 工具來做這個即時採樣。會發現 Kernel 會發生在一個自選鎖上,自選鎖裡面是什麼呢?就能看到,會做一個異名的一個大頁記憶體分配。這個就會造成在系統非常繁忙的時候,內核的 CPU 佔用非常高。然後我們做了一個測試,在開啟透明大頁和關閉透明大頁,看藍色和紅色的線,在開啟透明大頁的功能以後,就是 Always 這個選項下,它的性能有很多毛刺,性能總體會下降 30%- 40%。所以說我們推薦把透明大頁關閉。當然作業系統還有很多其他的一些選項,這邊就不詳細說了,比如說檔數限制,關閉 SWAP ,TCP 這一塊。

以上就是我今天的分享,謝謝大家。

快來與國內頂尖技術領導者一起“遊學”矽谷:

國內製造,金融,互聯網等領域頂級技術領導者同行 啟發無刻不在

量身定制斯坦福明星教授講座,觸摸無限接近的未來

深入十家矽谷最具科技感的巨頭公司和最先鋒獨角獸公司,汲取養分,碰撞思想

訪問全美知名孵化器和非營利科研組織,感受創新源泉

約伯斯同款禪修院冥想之旅,解放被禁錮認知

戳“閱讀原文”瞭解活動詳情,關注高可用架構公眾號更可享8折優惠!

最後五個席位,等你前來~

第二個問題速度會比較慢。第三個,面臨分析的維度非常多,每個維度的分析,都要一個資料開發做需求,平均做一個需求時間可能要兩周。

2. 技術選型

能不能用一些 OLAP 引擎來簡化操作?我們總結了對 OLAP 引擎的一些需求。

第一,回應要快。因為業務分析人員等不了太久。需要一定併發。最好有一個 SQL 介面。支援資料級要非常大。當然離線方面,我們目前是 T+1 的模式,所以說綜合考慮選擇了 Kylin。Kylin 就是基於 Hadoop 之上的提供 SQL 查詢和多維分析的能力,它能支援超大規模資料級。它能在亞秒返回巨大的一個查詢。實際上它就是標準的 MOLAP 方案,我們需要預先定義維度和指標,提前預計算它的 Cube,把結果存在 HBase,查詢時解析 SQL,根據路由把它到 HBase 到對應的表中去拿取資料。

圖4是 Kylin 的架構圖,最下麵是構建引擎,左邊是 Hadoop 資料倉庫,右邊是 HBase。最下面會根據 Hadoop 不同的原始資料進行構建,然後把資料存在 HBase。查詢的時候,在 Query Engine 做解析,解析完了以後,下面路由選擇,去 HBase 中查對應的資料。左邊大家看到有根虛線,這個是什麼意思呢?剛才提到 MOLAP 只能支援預聚合的資料,要查原始的明細資料應該怎麼辦,Kylin 是不支持的。這條虛線劃出來,官方說會在後續某個版本支持,但實際上一直沒做。後面會講到我們是怎麼解決這個問題的。

再介紹一下鏈家 Kylin 使用統計,定位是離線的 OLAP 引擎。線上有 100 多個 Cube,公司 8 個業務現在使用,總共存儲容量應該也到 30T。總的資料量應該 800 億行左右,單個 Cube 最大已經到 40 億行。每天的查詢量大概有 10 萬多,查詢性能還是非常好的,95% 能在 500ms 以內,99% 都在 1s 中返回。除開那種超大的,可能會在 10s 左右,還是非常滿足我們需求的。

3. 鏈家 OLAP 平臺架構

圖5是鏈家整個 OLAP 平臺的架構。首先核心還是基於 Kylin 這樣一個 MOLAP 引擎,我們 Kylin 做了讀寫分離的部署,也做了負載均衡的高可用。Build 機器是構建任務的機器,下面對接的是主要的 Hadoop 集群,負責所有的資料倉庫存儲和所有的計算。我們有自研的調度系統,每天會去調度 Kylin 的構建任務。關於調度系統,其實跟剛才党老師的調度非常像,調度提供了很多功能,比如說分散式調度,會根據機器的負載情況去分發任務,會做任務的依賴,會做任務的監控等等。當然今天的重點不是它。

第二塊就是 HBase 集群,是個單獨的集群。為什麼這麼做呢?如果 HBase 跟 Hadoop 在一起,Hadoop 一旦運行大的任務,記憶體壓力大的時候,HBase 就會性能非常差,所以把查詢服務獨立出來。所有的查詢 Query 機器都會去查 HBase。上面指標分析平臺就是鏈家視覺化的分析平臺,它底層的引擎主要就是 Kylin,它所有的預建模的查詢都會走 Kylin,當然會做緩存,把那些常用的 SQL,重複的 SQL 緩存住。

第二塊就是剛才提到的,如果有查明細資料該怎麼辦?鏈家這邊集群組做了另一個引擎叫 Query Engine,這是我們自己的。它主要是給另外一個業務提供服務,提供即時查詢服務。它底層是兩個引擎,Presto 和 Spark SQL。在指標上會把明細的查詢和靈活的查詢,如果 Kylin 不支持,就會把它轉到 Query Engine,Query Engine 會根據它的 Cost來做優化,來選擇 Presto 或者 Spark SQL 來查詢。這樣就解決了明細查詢的問題。

最後一塊就是預警監控,我們所有模組都有 Supervisior 存活監控,保證它掛了以後能啟動,整個鏈路都會上報到 OP 部門 維護的基於 Falcon 的監控平臺。

圖6是報表查詢頁面,這裡會顯示我有許可權的報表,在裡面搜索一下,我想看的一些報表。這是一張,首先選擇一個時間的維度,然後這裡可以篩選很多條件,比如說根據分公司,根據店組去查。比如想看東北區的詳情,點進去就可以顯示到這個區裡面所有店主。如果需要看這個店主再點進去,就可以到這個店主裡面的每個人。這個在 OLAP 領域,這個過程叫做下鑽的過程。如果往回這個過程叫上卷,相當於把那個結果再往回聚合。我們感覺 UI 設計的還可以。

然後圖7這是我們自研的 Cube 管理。如果大家有用過 Kylin 的話,應該覺得主要的功能都是差不多的,因為都是根據 Kylin 來做定制的。比如說選擇維度,選擇指標。然後可以設置一些參數,可以在這裡面做查詢。它的可能就是它對接了我們許可權平臺,能夠自己對它做管理。

總結一下鏈家 OLAP 的一些特色。一是自研的視覺化平臺,支持上卷下鑽,維度對比,視覺化報表創建,指標管理。開源的時候,其實有些開源產品比如說叫 Saiku,有些公司有應用。相比於它的話,個人感覺 UI 還是美觀一些,更切近業務,方便靈活定制。我們的引擎能力,其實並不是簡單的 MOLAP,而是一個混合的 OLAP 模型,既支援明細查詢,又支援彙總查詢。針對需求實現了跨 Cube 查詢的功能。就是 Kylin 中,一個 Cube 對應一張即時表。一個 Cube 裡面只能查一張即時表的資料。假如說有多個即時表,但是我查的維度是類似的,我想把結果做聚合運算,Kylin 是不支持的,我們平臺裡面支持了跨 Cube 查詢。然後在這裡面完整監控,有高可用的架構。Cube 管理剛才也提到,前端會做一些簡化,對接了許可權,簡化配置,提升我們的管理效率。

5. OLAP 展望

下面是我們 OLAP 下半年的展望。

一是擴展能力。剛才提到已經有跨 Cube 查詢,但是隨著業務增長,可能有更多的需求。比如說想把 Kylin 的資料去跟 Hive 裡的資料做計算,或者業務那邊接入他們自己的 Oracle,裡面放了一些他們不想給我們的資料,但是想跟我們的資料做一些混合運算,我們這邊做一個多中繼資料查詢。

第二塊會做一些更多的路由優化,優化查詢效率。第三就是 Kylin 目前它的來源資料同步,Build 機器相當於 Master,Query的機器相當於 Slave,目前是 Push 的模式,Build 機器 build 好了,把一些中繼資料同步過去。但是缺點是有時候會出現失敗的情況。我們目前做得非常 low,就是用 Crontab,定期讓 Slave 去重新刷一下緩存。

後期我們正在做的就是讓 Slave 用心跳機制,Master 那裡用一個狀態機模型,把每一個要下發的任務放在狀態機裡,Slave 心跳了之後來取這個狀態。當我更新完緩存以後,我在下次心跳的時候,把這個狀態再置為已經更新。其實這個非常類似於 Hadoop 裡面的 YARN 架構,都是基於狀態機的。這樣的話,擴展性會非常好,減少 Master 的一些壓力。我們正在做 Kylin 2.0 調研,比如它支援雪花模型,不用轉化資料倉庫。支援 Spark 構建,提升速度。

第三塊,我們目前的 OLAP 還是一個離線服務,隨著業務的發展,我們有更多即時需求。主要是 MOLAP 裡面的一些即時的方向,一個是 Kylin 自身支持 Streaing Cubing,類似於 Spark Streaming,實際上是小批量近即時的,做到分鐘級。另外我們也考慮 Druid 或者 Palo 這些架構,它們是 Lambda 的架構。就是記憶體中會維護最新的狀態,這樣可以做到純即時的更新。

三、OLAP 平臺鏈路優化實踐

1. Kylin 全鏈路架構

最後講一些比較接地氣的,我們在 OLAP 平臺全鏈路的一些優化經驗。首先看一下 Kylin 的整個鏈路。最上頭的 Kylin,它的存儲引擎是 HBase,HBase 又基於 HDFS。所有程式都跑到了JVM 上。首先是 Kylin 本身有一些優化,我們知道假如我們定義了 N 個維度,我查詢的時候,每一個維度可選擇或者不選擇,其實有 2 的 N 次方種選擇。比如說 A、B、C、D 四個維度,這裡頭一共有 16 種維度選擇。這個 Kylin 裡面會提供很多方式來避免這種維度的膨脹。我這兒簡要介紹一下。見圖8。

第一個是聚合組,用戶查詢的時候不會所有條件都查,而是同時只會出現某幾個維度,我們就把這幾個維度定義為一個聚合組。比如說這裡就用了 A、B、C 和 B、C、D。於是只有 A、B、C 內部的這些維度和 B、C、D 內部維度會進行計算,而且如果兩個有交叉,就會計算一份。其他的各種衍生維度、強制維度、層次維度,大家如果做 Kylin 引擎,後續可以在這塊著重關注一下,我這邊就不詳細解釋了。反正原理都是儘量根據查詢的一些條件或者業務的一些條件,儘量減少要計算的維度搭配。

2. Kylin 改造以及優化

我們在 Kylin 裡面做了很多改造和優化,第一個是 Kylin 默認只能在 HBase 中,放在預設庫裡面,首碼是 Kylin 加底線。我們如果要在上面部署多套環境就會有衝突,我們這邊修改了一下,讓它支援配置一個首碼,可以在一套 HBase 中部署多套 Kylin。我們之前講漏了一件事兒,剛才架構圖裡面有一個預上線的 Kylin,我們所有的,因為上線前都會在預上線的 Kylin 裡面進行驗證,然後只有它的資料 OK 了以後才會上到線上,預上線的環境。還有第二個功能就是雙寫。假如一些重要的業務,我會在兩個集群裡邊同時構建。如果主集群先出現問題,立馬切到備用集群。我們修復了它裡面的一個鎖死的問題,主要是更新緩存部分,這個引擎加了社區。

然後接下來是一些,比如說構建的時候,MR 引擎的一些優化,比如開啟壓縮等等。Kylin 也是記憶體型的查詢服務,它是 Java 寫的,所以說自然 GC 的問題,等一下會詳細講一下。如果你使用全域字典,你是要調大一些 MAP 記憶體。定期會做 Segment 的合併,清理一些過期資料,減少 HBase Region 個數。因為 HBase 如果 Region Server 太多,壓力非常大。

最後還有一個默認使用的授權的時候,有個 Byct 加密,這個非常影響性能。後續我會講如何定位這個問題。

3. 系統性能調優

關於系統調優給大家推薦一個工具叫火焰圖,見圖9。火焰圖是什麼呢?它就是一個視覺化的去展示 CPU 佔用情況的圖。每一個小方框裡面都是一個函數,水準方向看它的寬度就是 CPU 的時間佔用,垂直方向看就是一個函數的堆疊。下面的函數就會調用上面的函數。所以自然上面函數的時間會累加到下面函數中。

如何定位性能的問題呢?從下往上看,可以理解成,如果一個山峰一直沒有縮短,到最頂層的時候,調用堆疊最上面的一個函數,那是最終調用的函數,如果它佔用非常多,一定是它的性能問題。

我們之前發現 Kylin 在高併發的時候,CPU 會滿載,非常高,但是查詢非常簡單。利用火焰圖去分析發現,它有 80% 的時間在做一個 Spring 的 Bcybt 加密的驗證。然後右邊那一小塊占 4% 的,這個才是 Kylin 的查詢。我們查了一下,其實 Bcybt 加密是一個比較好,相當於是比較不容易破解的加密。它的方式就是讓它每次計算非常耗性能,降低它的速度。我們 將 Bcybt 加密換成了其他加密的演算法。大家可以看到,這段(Kylin 查詢部分)就是之前 4% 的時間,因為我把其他時間消掉了,所以它的占比就大幅度提升,提升到 40%。提升了大概 1 倍 QPS。

第二塊就是 Java GC。其實 Java 大家應該還是比較熟悉的。像傳統的 CMS 非常經典,但是它的缺點就是對於大記憶體回收會有問題,而我們現在線上基本開到 80G,甚至 90G,CMS 在這種情況下基本上是 STW 非常長,可能要達到幾分鐘。G1 是 Java 最新的垃圾回收演算法。它的核心還是保留了 CMS 的分代概念,但是它把每一代分成了很多 Region,打散。G1 定位是暫停時間可控的 GC,它會根據你設定暫停時間,在暫停時間內盡可能多地回收記憶體。因為打散成了很多 Region,我可以不全部回收,我可以回收部分,保證我每次回收的時間可控。因為新生代的 GC 是發生非常頻繁的,所以我們要控制新生代的大小,保證每次回收時間可控。

圖9是 GC 調優後的對比圖,上面是 CMS,下面是 Java。可以看到,GC 頻率和時間都有大幅地縮短。

第二塊就是我們 Java 的存儲是放在 Hbase 中,HBase 又放在 HDFS 中,HDFS 的底層就是物理上的磁片。我想提升 Java 的查詢性能,從硬體上我就想,現在主流的磁片 IOPS 可能就幾百,但是現在的 SSD,普通的 SSD 能達到幾萬,像 PCI 組建的 SSD 現在已經達到 30、40 萬 IOPS。我們今年年初做了一件事情,我們把 Hadoop 集群從 2.4 升到了 2.7,2.7 裡面引入了一個非常重要的特性,叫異構存儲,或叫混合存儲。就是 Hadoop 支援我把我的磁片標記一個標籤,支援四種標籤。第一種叫記憶體磁片,第二種叫 SSD,第三個是普通磁片,最後一種是歸檔,歸檔是一種性能非常差的磁片,可以認為。定義了存儲策略。比如說 ALLSSD 是指我的所有副本全放在 SSD 中,ONE-SSD 就是放一份,放在 SSD 中。預設是 Hot 全部放磁片。我們這邊會把 HBase 的核心日誌,還有核心業務都會放在 ALLSSD 中,對重要業務會使用 ONE-SSD,如果是普通業務放在磁片上就行了。

剛才提到,我們其實為了節省成本,如果用 SSD 也是主要用 ONE-SSD。ONE-SSD 有一個問題,假如說這個用戶端讀取資料,我有三個備份。其中一個是 SSD,但是我本地的副本是一個機械磁片,預設的社區版會優先讀取本地,因為它的想法就是網路延遲是比較高的。但實際上我們鏈家大資料已經全部是萬兆的架構,所以說網路瓶頸已經是不存在的。我們這裡給它定制了一個 First 選擇策略,叫 SSD-FIRST。就是如果我遠端有 SSD,我還是優先使用遠端的。後面會給一個對比資料,這個已經提交給社區。

圖10是一個 HBase 就是讀寫分離。HBase 預設所有佇列和處理執行緒實際上是不區分的,我查詢請求分 Scan、Get、Write。三種請求都會打到相同的佇列裡面。如果出現了 Scan,把所有佇列和執行緒都堵滿了以後,簡單的小查詢都會卡住。這個是 HBase 在 1.2,還是 1.3 的時候已經實現了,我們可以把佇列和執行緒按照比例去分配給 Scan、Get、Write 三種請求,避免 Scan 請求阻塞小請求。

圖11是我們 HBase 的測試資料。大概是三台機器,使用 ONESSD 測試工具。我們就關注綠色這條線,這條線就是輸送量,或者叫 QPS 。最右邊是 HDD,當我們使用 ONE-SSD 以後,大概能提升 4 倍左右的吞吐。我們再作用,上線 SSD-FIRST 策略以後,又能提升一倍的吞吐。最後在上線讀寫分離以後又能提升一倍,整體提升的大概 10 多倍。而且只有第一步是需要硬體成本的,後面的兩個策略都是純策略上的調整。

圖12還有很多常見的一些優化項,我這邊就不詳細解釋了。主要是針對 Hadoop 的一些讀寫優化。

下面講一些作業系統常見的一些調優。首先給大家講一個最常見的命令 Free,運行出來能顯示當前記憶體的情況。我這裡寫了三種境界,第一種是剛接觸 Linux 同學,Free 是 0,是不是記憶體不夠了,是不是感覺要掛。經過一段時間學習,發現 Cache,Buffer 都是可以拿來用的。看第二行, Free 是 4G,記憶體什麼問題都沒有。真正老司機是什麼樣的呢?我們要知道 Cache 是用來幹什麼的,使用的命中率如何,Cache 是幹什麼的,Cache 預設就是 Page Cache,就是頁面緩存,主要是為了快取檔案系統,它在 VFS 裡面會用到的。然後一般的 Cache 都是可以釋放出來給程式使用的。但是有一種情況 Cache 不能釋放。就是有一種檔案系統叫 tempfs,相當於記憶體的一個檔案系統,利用了 Page Cache 的空間來做存儲,這種資料是沒法被清掉的。所以 Cache 並不是所有的可以用。如果拿來做檔緩存,本身作業系統這麼設計一定是有它的作用,我們想知道這個緩存命中率如何。

我看了一下網上各種工具,實際上沒有一個對緩存命中率計算的方法。還是剛才提到做火焰圖那哥們,在他的博客上公佈了一個利用 ftrace 的方案,原理其實非常簡單。命中率我們現在算 100%-Miss 的概率,Miss 是什麼情況呢?Miss 的分母就是 Page 的訪問次數,分子是,如果發生了 Misses,我就會把它從磁片上讀出來,再放到緩存中。所以說分子就是添加到 Page 緩存的一個次數。我們這邊因為 ftrace 會在系統上有問題,我用 System Tap 把腳本改寫了一下,著重是四個系統的調用,System Tap 就是一個動態跟蹤技術,它可以在內核任何一個函 數上動態去注入一段代碼,然後可以規定在函數運行前或者是運行後,我們這裡只是給了四個篩檢程式,就是在四個條件上面做了過濾。

下面大家看一下 Total,裡面第一項叫 Mark Page Accessed,就是頁面訪問次數,Miss 的第一項就是添加到 LRU 緩存的一個次數。後面減掉的一部分是什麼呢?大家知道,頁面緩存除了去讀的時候用到,寫的時候也會用到。任何一次寫入都會寫入到 Page 中。有時候我們要從分子分母中把寫入的那部分資料給減掉。我們會把 Cache 的命中率定期同步到我們裡面做監控。我們線上現在應該能達到 70%-90% 的命中率。據我們的試驗,一般在 60% 以上,HBase 會比較穩定,因為 HBase 對於 IO 性能要求非常高。如果 Cache 命中率非常高,實際上每次讀寫都會非常快。當然這個也跟業務有關係。如果你的查詢是非常隨機的話,這個緩存也很容易被弄髒。

下面講一個比較大的坑:NUMA。現在大家的伺服器基本上都是多個 CPU,常見的有兩種架構,一種叫 SMP,對稱多處理。記憶體其實是跟 CPU 之間連著一根匯流排。它的問題就是隨著CPU 越來越多,記憶體越來越大,所有的瓶頸都壓在匯流排上。接下來英特爾公司就想到另一個方案,就是我每個 CPU 給它自己專屬的記憶體,這個 CPU 的專屬記憶體存取速度非常快。現在記憶體非常大,每個地方的專屬記憶體應該是足夠的,如果我實在是需要遠端記憶體,我再走主線。預設是一個親和模式,什麼意思呢?就是我這個 CPU 分配的記憶體和淘汰的記憶體都會優先從我的專屬記憶體中來找,比如說我專屬記憶體不夠用了,我就會優先從我專屬記憶體裡去淘汰。這樣會導致一個問題,我系統看系統非常充足,但是我發現有 SWAP 佔用,我的 Java 在做 GC。這個就非常奇怪。其實就是因為預設的親和模式造成的。這個親和模式實際上對小記憶體應用非常好的,因為我一看 CPU 記憶體其實也很大了,至少 64G、128 個 G 都有,因為總共可能 256、512。單個小記憶體應用記憶體是足夠的,但是像 HBase 或者說資料庫,我們現在可以到 80G,甚至 100G,實際上單個 CPU 的專屬記憶體是不夠用的。我們這兒主要做了一個調整策略,就是把它的分配參數調整了一下。如果專屬記憶體不夠的時候,我允許從其他地方獲取記憶體,而不是去做本地的回收。

最後一個也是比較大的一個坑,就是透明大頁。什麼是透明大頁呢?大家知道,現在作業系統都是分頁的,每一個程式有一個頁表。程式分配都是邏輯位址,它要真的訪記憶體要通過一次邏輯位址到物理位址映射,然後 TLB 是用來做一個加速的,現在預設的大小是 4K,隨著記憶體越來越大就會有問題,頁表會越來越長,TLB 緩存命中率也會降低。作業系統想到一個方式,我就把配置調大,就給了兩個選項,一個是 2M,最大可以開到 1G。這樣看起來非常完美,但是在 6.5 的 centOS 中,這個大頁是默認開啟的,但是我們會發現 HBase 在使用過程中,CPU 佔用會非常異常,System Tap CPU 會非常高。大家可以看,JVM 的使用才 24%,但是我內核達到 32%,我們用 Perf 工具來做這個即時採樣。會發現 Kernel 會發生在一個自選鎖上,自選鎖裡面是什麼呢?就能看到,會做一個異名的一個大頁記憶體分配。這個就會造成在系統非常繁忙的時候,內核的 CPU 佔用非常高。然後我們做了一個測試,在開啟透明大頁和關閉透明大頁,看藍色和紅色的線,在開啟透明大頁的功能以後,就是 Always 這個選項下,它的性能有很多毛刺,性能總體會下降 30%- 40%。所以說我們推薦把透明大頁關閉。當然作業系統還有很多其他的一些選項,這邊就不詳細說了,比如說檔數限制,關閉 SWAP ,TCP 這一塊。

以上就是我今天的分享,謝謝大家。

快來與國內頂尖技術領導者一起“遊學”矽谷:

國內製造,金融,互聯網等領域頂級技術領導者同行 啟發無刻不在

量身定制斯坦福明星教授講座,觸摸無限接近的未來

深入十家矽谷最具科技感的巨頭公司和最先鋒獨角獸公司,汲取養分,碰撞思想

訪問全美知名孵化器和非營利科研組織,感受創新源泉

約伯斯同款禪修院冥想之旅,解放被禁錮認知

戳“閱讀原文”瞭解活動詳情,關注高可用架構公眾號更可享8折優惠!

最後五個席位,等你前來~

Next Article
喜欢就按个赞吧!!!
点击关闭提示