前言:大數(shù)據(jù)背景下,傳統(tǒng)關(guān)系型多維分析 ROLAP 引擎遇到極大挑戰(zhàn),因而鏈家轉(zhuǎn)向基于 Hadoop 生態(tài)的 MOLAP(Kylin)及 HOLAP (多引擎)。在由七牛云和鏈家聯(lián)合主辦的架構(gòu)師實踐日北京站中,鏈家大數(shù)據(jù)集群架構(gòu)組負(fù)責(zé)人鄧鈁元進行演講,分享了鏈家在多維分析引擎方面的一些實踐經(jīng)驗,主要從 OLAP 的背景和簡介、鏈家多維分析架構(gòu)演進和展望、OLAP 平臺鏈路優(yōu)化這三部分來介紹。 鄧鈁元,鏈家網(wǎng)大數(shù)據(jù)資深研發(fā)工程師。2015 年開始負(fù)責(zé)鏈家大數(shù)據(jù)集群建設(shè),專注于 Hadoop 生態(tài)組的定制開發(fā)及應(yīng)用,深入 Hadoop/HBase 等源碼,成為 contributor 回饋社區(qū)。擅長底層性能調(diào)優(yōu),打造公司級計算存儲平臺。 一、OLAP 的背景和簡介 1. OLAP vs OLTP OLAP 翻譯成中文叫聯(lián)機分析處理,OLTP 叫聯(lián)機事務(wù)處理。OLTP 它的核心是事務(wù),實際上就是我們常見的數(shù)據(jù)庫。我們業(yè)務(wù)數(shù)據(jù)庫就是面向于事務(wù)。它的并發(fā)量會比較高,但是操作的數(shù)據(jù)量會比較小。它是實時更新的。數(shù)據(jù)庫的設(shè)計會按照 3NF 范式,更高的話可能會按照 BC 范式之類的來做。而 OLAP 的核心是分析,面向應(yīng)用是分析決策,需要分析的數(shù)據(jù)級會非常大,可能 TB,甚至 PB 都會有。它的數(shù)據(jù)更新會稍微慢一些,它的設(shè)計一般是反范式的,因為面向分析。常見的是雪花模型和星型模型。 剛才說的比較空洞,實際上 OLAP 是什么呢?非常簡單,就是一個 SQL,這里按照兩個維度,一個 returnflag,一個 orderstatus 來做 Group By,然后做一下 Sum,Group By 這段就叫維度,F(xiàn)rom 這段叫做指標(biāo),非常簡單。 2. OLAP 引擎分類 OLAP 引擎的一些常見分類大概有這幾種。
3. HOLAP 最后一種叫 HOLAP,叫混合 OLAP,這個就非常簡單了,就是兩個雜交一下。根據(jù)業(yè)務(wù)場景路由不同的引擎。 圖一這是簡單的 ROLAP 模型,具體操作流程剛才已經(jīng)提到了,它的優(yōu)勢就是,它其實就是個數(shù)據(jù)庫,所以任何的 SQL 都可以在里面執(zhí)行。數(shù)據(jù)是沒有冗余的。缺點就是數(shù)據(jù)量很大的時候,計算速度會下降很多,所以并發(fā)會比較差。它的場景就是不知道要查什么數(shù)據(jù),靈活性非常高時,一般會選 ROLAP。 4. MOLAP 第二種 MOLAP,剛才提到 MOLAP 主要是要定義一個模型,比如說圖2樣例里面定義了三個維度:Time,Web page,Action。從這三個維度把所有的組合提前預(yù)計算好,存在一個存儲引擎中,需要的時候里面取出來做一下聚合就可以,所以它的原始數(shù)據(jù)支持非常大,查詢速度,因為直接算好了,可以很快返回。它的缺點就是因為聚合了以后,就查不到明細(xì)數(shù)據(jù)。它的靈活性稍微差一點,因為需要預(yù)先去定義維度,還有指標(biāo)。所以說是需要能夠知道查詢的模式,才能用這個 MOLAP。 1. 早期分析數(shù)據(jù)實現(xiàn) 剛才呂毅已經(jīng)講到,我們剛開始做大數(shù)據(jù)的時候,Hadoop 組建基本上都是基于開源的。我們會從日志、Kafka 去導(dǎo)數(shù)據(jù),MySQL 數(shù)據(jù)會用 Sqoop 同步到 Hadoop 中。當(dāng)時是用一個開源的調(diào)度引擎 Ooize,我們根據(jù)業(yè)務(wù)需求去建表,然后把數(shù)據(jù)導(dǎo)到 MySQL 中,因為要做呈現(xiàn),必須要在一個比較快的返回的數(shù)據(jù)庫中。所以放在 MySQL。 它的缺點是什么?當(dāng)它的數(shù)據(jù)量越來越大,MySQL 做存儲,它的擴展性是非常不好的。數(shù)據(jù)量大以后,第二個問題速度會比較慢。第三個,面臨分析的維度非常多,每個維度的分析,都要一個數(shù)據(jù)開發(fā)做需求,平均做一個需求時間可能要兩周。 2. 技術(shù)選型 能不能用一些 OLAP 引擎來簡化操作?我們總結(jié)了對 OLAP 引擎的一些需求。 第一,響應(yīng)要快。因為業(yè)務(wù)分析人員等不了太久。需要一定并發(fā)。最好有一個 SQL 接口。支持?jǐn)?shù)據(jù)級要非常大。當(dāng)然離線方面,我們目前是 T+1 的模式,所以說綜合考慮選擇了 Kylin。Kylin 就是基于 Hadoop 之上的提供 SQL 查詢和多維分析的能力,它能支持超大規(guī)模數(shù)據(jù)級。它能在亞秒返回巨大的一個查詢。實際上它就是標(biāo)準(zhǔn)的 MOLAP 方案,我們需要預(yù)先定義維度和指標(biāo),提前預(yù)計算它的 Cube,把結(jié)果存在 HBase,查詢時解析 SQL,根據(jù)路由把它到 HBase 到對應(yīng)的表中去拿取數(shù)據(jù)。 圖4是 Kylin 的架構(gòu)圖,最下面是構(gòu)建引擎,左邊是 Hadoop 數(shù)據(jù)倉庫,右邊是 HBase。最下面會根據(jù) Hadoop 不同的原始數(shù)據(jù)進行構(gòu)建,然后把數(shù)據(jù)存在 HBase。查詢的時候,在 Query Engine 做解析,解析完了以后,下面路由選擇,去 HBase 中查對應(yīng)的數(shù)據(jù)。左邊大家看到有根虛線,這個是什么意思呢?剛才提到 MOLAP 只能支持預(yù)聚合的數(shù)據(jù),要查原始的明細(xì)數(shù)據(jù)應(yīng)該怎么辦,Kylin 是不支持的。這條虛線劃出來,官方說會在后續(xù)某個版本支持,但實際上一直沒做。后面會講到我們是怎么解決這個問題的。 再介紹一下鏈家 Kylin 使用統(tǒng)計,定位是離線的 OLAP 引擎。線上有 100 多個 Cube,公司 8 個業(yè)務(wù)現(xiàn)在使用,總共存儲容量應(yīng)該也到 30T。總的數(shù)據(jù)量應(yīng)該 800 億行左右,單個 Cube 最大已經(jīng)到 40 億行。每天的查詢量大概有 10 萬多,查詢性能還是非常好的,95% 能在 500ms 以內(nèi),99% 都在 1s 中返回。除開那種超大的,可能會在 10s 左右,還是非常滿足我們需求的。 3. 鏈家 OLAP 平臺架構(gòu) 圖5是鏈家整個 OLAP 平臺的架構(gòu)。首先核心還是基于 Kylin 這樣一個 MOLAP 引擎,我們 Kylin 做了讀寫分離的部署,也做了負(fù)載均衡的高可用。Build 機器是構(gòu)建任務(wù)的機器,下面對接的是主要的 Hadoop 集群,負(fù)責(zé)所有的數(shù)據(jù)倉庫存儲和所有的計算。我們有自研的調(diào)度系統(tǒng),每天會去調(diào)度 Kylin 的構(gòu)建任務(wù)。關(guān)于調(diào)度系統(tǒng),其實跟剛才黨老師的調(diào)度非常像,調(diào)度提供了很多功能,比如說分布式調(diào)度,會根據(jù)機器的負(fù)載情況去分發(fā)任務(wù),會做任務(wù)的依賴,會做任務(wù)的監(jiān)控等等。當(dāng)然今天的重點不是它。 第二塊就是 HBase 集群,是個單獨的集群。為什么這么做呢?如果 HBase 跟 Hadoop 在一起,Hadoop 一旦運行大的任務(wù),內(nèi)存壓力大的時候,HBase 就會性能非常差,所以把查詢服務(wù)獨立出來。所有的查詢 Query 機器都會去查 HBase。上面指標(biāo)分析平臺就是鏈家可視化的分析平臺,它底層的引擎主要就是 Kylin,它所有的預(yù)建模的查詢都會走 Kylin,當(dāng)然會做緩存,把那些常用的 SQL,重復(fù)的 SQL 緩存住。 第二塊就是剛才提到的,如果有查明細(xì)數(shù)據(jù)該怎么辦?鏈家這邊集群組做了另一個引擎叫 Query Engine,這是我們自己的。它主要是給另外一個業(yè)務(wù)提供服務(wù),提供即時查詢服務(wù)。它底層是兩個引擎,Presto 和 Spark SQL。在指標(biāo)上會把明細(xì)的查詢和靈活的查詢,如果 Kylin 不支持,就會把它轉(zhuǎn)到 Query Engine,Query Engine 會根據(jù)它的 Cost來做優(yōu)化,來選擇 Presto 或者 Spark SQL 來查詢。這樣就解決了明細(xì)查詢的問題。 最后一塊就是預(yù)警監(jiān)控,我們所有模塊都有 Supervisior 存活監(jiān)控,保證它掛了以后能啟動,整個鏈路都會上報到 OP 部門 維護的基于 Falcon 的監(jiān)控平臺。 圖6是報表查詢頁面,這里會顯示我有權(quán)限的報表,在里面搜索一下,我想看的一些報表。這是一張,首先選擇一個時間的維度,然后這里可以篩選很多條件,比如說根據(jù)分公司,根據(jù)店組去查。比如想看東北區(qū)的詳情,點進去就可以顯示到這個區(qū)里面所有店主。如果需要看這個店主再點進去,就可以到這個店主里面的每個人。這個在 OLAP 領(lǐng)域,這個過程叫做下鉆的過程。如果往回這個過程叫上卷,相當(dāng)于把那個結(jié)果再往回聚合。我們感覺 UI 設(shè)計的還可以。 然后圖7這是我們自研的 Cube 管理。如果大家有用過 Kylin 的話,應(yīng)該覺得主要的功能都是差不多的,因為都是根據(jù) Kylin 來做定制的。比如說選擇維度,選擇指標(biāo)。然后可以設(shè)置一些參數(shù),可以在這里面做查詢。它的可能就是它對接了我們權(quán)限平臺,能夠自己對它做管理。 總結(jié)一下鏈家 OLAP 的一些特色。一是自研的可視化平臺,支持上卷下鉆,維度對比,可視化報表創(chuàng)建,指標(biāo)管理。開源的時候,其實有些開源產(chǎn)品比如說叫 Saiku,有些公司有應(yīng)用。相比于它的話,個人感覺 UI 還是美觀一些,更切近業(yè)務(wù),方便靈活定制。我們的引擎能力,其實并不是簡單的 MOLAP,而是一個混合的 OLAP 模型,既支持明細(xì)查詢,又支持聚合查詢。針對需求實現(xiàn)了跨 Cube 查詢的功能。就是 Kylin 中,一個 Cube 對應(yīng)一張實時表。一個 Cube 里面只能查一張實時表的數(shù)據(jù)。假如說有多個實時表,但是我查的維度是類似的,我想把結(jié)果做聚合運算,Kylin 是不支持的,我們平臺里面支持了跨 Cube 查詢。然后在這里面完整監(jiān)控,有高可用的架構(gòu)。Cube 管理剛才也提到,前端會做一些簡化,對接了權(quán)限,簡化配置,提升我們的管理效率。 5. OLAP 展望 下面是我們 OLAP 下半年的展望。 一是擴展能力。剛才提到已經(jīng)有跨 Cube 查詢,但是隨著業(yè)務(wù)增長,可能有更多的需求。比如說想把 Kylin 的數(shù)據(jù)去跟 Hive 里的數(shù)據(jù)做計算,或者業(yè)務(wù)那邊接入他們自己的 Oracle,里面放了一些他們不想給我們的數(shù)據(jù),但是想跟我們的數(shù)據(jù)做一些混合運算,我們這邊做一個多元數(shù)據(jù)查詢。 第二塊會做一些更多的路由優(yōu)化,優(yōu)化查詢效率。第三就是 Kylin 目前它的源數(shù)據(jù)同步,Build 機器相當(dāng)于 Master,Query的機器相當(dāng)于 Slave,目前是 Push 的模式,Build 機器 build 好了,把一些元數(shù)據(jù)同步過去。但是缺點是有時候會出現(xiàn)失敗的情況。我們目前做得非常 low,就是用 Crontab,定期讓 Slave 去重新刷一下緩存。 后期我們正在做的就是讓 Slave 用心跳機制,Master 那里用一個狀態(tài)機模型,把每一個要下發(fā)的任務(wù)放在狀態(tài)機里,Slave 心跳了之后來取這個狀態(tài)。當(dāng)我更新完緩存以后,我在下次心跳的時候,把這個狀態(tài)再置為已經(jīng)更新。其實這個非常類似于 Hadoop 里面的 YARN 架構(gòu),都是基于狀態(tài)機的。這樣的話,擴展性會非常好,減少 Master 的一些壓力。我們正在做 Kylin 2.0 調(diào)研,比如它支持雪花模型,不用轉(zhuǎn)化數(shù)據(jù)倉庫。支持 Spark 構(gòu)建,提升速度。 第三塊,我們目前的 OLAP 還是一個離線服務(wù),隨著業(yè)務(wù)的發(fā)展,我們有更多實時需求。主要是 MOLAP 里面的一些實時的方向,一個是 Kylin 自身支持 Streaing Cubing,類似于 Spark Streaming,實際上是小批量近實時的,做到分鐘級。另外我們也考慮 Druid 或者 Palo 這些架構(gòu),它們是 Lambda 的架構(gòu)。就是內(nèi)存中會維護最新的狀態(tài),這樣可以做到純實時的更新。 三、OLAP 平臺鏈路優(yōu)化實踐 1. Kylin 全鏈路架構(gòu) 最后講一些比較接地氣的,我們在 OLAP 平臺全鏈路的一些優(yōu)化經(jīng)驗。首先看一下 Kylin 的整個鏈路。最上頭的 Kylin,它的存儲引擎是 HBase,HBase 又基于 HDFS。所有程序都跑到了JVM 上。首先是 Kylin 本身有一些優(yōu)化,我們知道假如我們定義了 N 個維度,我查詢的時候,每一個維度可選擇或者不選擇,其實有 2 的 N 次方種選擇。比如說 A、B、C、D 四個維度,這里頭一共有 16 種維度選擇。這個 Kylin 里面會提供很多方式來避免這種維度的膨脹。我這兒簡要介紹一下。見圖8。 第一個是聚合組,用戶查詢的時候不會所有條件都查,而是同時只會出現(xiàn)某幾個維度,我們就把這幾個維度定義為一個聚合組。比如說這里就用了 A、B、C 和 B、C、D。于是只有 A、B、C 內(nèi)部的這些維度和 B、C、D 內(nèi)部維度會進行計算,而且如果兩個有交叉,就會計算一份。其他的各種衍生維度、強制維度、層次維度,大家如果做 Kylin 引擎,后續(xù)可以在這塊著重關(guān)注一下,我這邊就不詳細(xì)解釋了。反正原理都是盡量根據(jù)查詢的一些條件或者業(yè)務(wù)的一些條件,盡量減少要計算的維度搭配。 2. Kylin 改造以及優(yōu)化 我們在 Kylin 里面做了很多改造和優(yōu)化,第一個是 Kylin 默認(rèn)只能在 HBase 中,放在默認(rèn)庫里面,前綴是 Kylin 加下劃線。我們?nèi)绻谏厦娌渴鸲嗵篆h(huán)境就會有沖突,我們這邊修改了一下,讓它支持配置一個前綴,可以在一套 HBase 中部署多套 Kylin。我們之前講漏了一件事兒,剛才架構(gòu)圖里面有一個預(yù)上線的 Kylin,我們所有的,因為上線前都會在預(yù)上線的 Kylin 里面進行驗證,然后只有它的數(shù)據(jù) OK 了以后才會上到線上,預(yù)上線的環(huán)境。還有第二個功能就是雙寫。假如一些重要的業(yè)務(wù),我會在兩個集群里邊同時構(gòu)建。如果主集群先出現(xiàn)問題,立馬切到備用集群。我們修復(fù)了它里面的一個死鎖的問題,主要是更新緩存部分,這個引擎加了社區(qū)。 然后接下來是一些,比如說構(gòu)建的時候,MR 引擎的一些優(yōu)化,比如開啟壓縮等等。Kylin 也是內(nèi)存型的查詢服務(wù),它是 Java 寫的,所以說自然 GC 的問題,等一下會詳細(xì)講一下。如果你使用全局字典,你是要調(diào)大一些 MAP 內(nèi)存。定期會做 Segment 的合并,清理一些過期數(shù)據(jù),減少 HBase Region 個數(shù)。因為 HBase 如果 Region Server 太多,壓力非常大。 最后還有一個默認(rèn)使用的授權(quán)的時候,有個 Byct 加密,這個非常影響性能。后續(xù)我會講如何定位這個問題。 3. 系統(tǒng)性能調(diào)優(yōu) 關(guān)于系統(tǒng)調(diào)優(yōu)給大家推薦一個工具叫火焰圖,見圖9。火焰圖是什么呢?它就是一個可視化的去展示 CPU 占用情況的圖。每一個小方框里面都是一個函數(shù),水平方向看它的寬度就是 CPU 的時間占用,垂直方向看就是一個函數(shù)的堆棧。下面的函數(shù)就會調(diào)用上面的函數(shù)。所以自然上面函數(shù)的時間會累加到下面函數(shù)中。 如何定位性能的問題呢?從下往上看,可以理解成,如果一個山峰一直沒有縮短,到最頂層的時候,調(diào)用堆棧最上面的一個函數(shù),那是最終調(diào)用的函數(shù),如果它占用非常多,一定是它的性能問題。 我們之前發(fā)現(xiàn) Kylin 在高并發(fā)的時候,CPU 會滿載,非常高,但是查詢非常簡單。利用火焰圖去分析發(fā)現(xiàn),它有 80% 的時間在做一個 Spring 的 Bcybt 加密的驗證。然后右邊那一小塊占 4% 的,這個才是 Kylin 的查詢。我們查了一下,其實 Bcybt 加密是一個比較好,相當(dāng)于是比較不容易破解的加密。它的方式就是讓它每次計算非常耗性能,降低它的速度。我們 將 Bcybt 加密換成了其他加密的算法。大家可以看到,這段(Kylin 查詢部分)就是之前 4% 的時間,因為我把其他時間消掉了,所以它的占比就大幅度提升,提升到 40%。提升了大概 1 倍 QPS。 第二塊就是 Java GC。其實 Java 大家應(yīng)該還是比較熟悉的。像傳統(tǒng)的 CMS 非常經(jīng)典,但是它的缺點就是對于大內(nèi)存回收會有問題,而我們現(xiàn)在線上基本開到 80G,甚至 90G,CMS 在這種情況下基本上是 STW 非常長,可能要達到幾分鐘。G1 是 Java 最新的垃圾回收算法。它的核心還是保留了 CMS 的分代概念,但是它把每一代分成了很多 Region,打散。G1 定位是暫停時間可控的 GC,它會根據(jù)你設(shè)定暫停時間,在暫停時間內(nèi)盡可能多地回收內(nèi)存。因為打散成了很多 Region,我可以不全部回收,我可以回收部分,保證我每次回收的時間可控。因為新生代的 GC 是發(fā)生非常頻繁的,所以我們要控制新生代的大小,保證每次回收時間可控。 圖9是 GC 調(diào)優(yōu)后的對比圖,上面是 CMS,下面是 Java??梢钥吹?,GC 頻率和時間都有大幅地縮短。 第二塊就是我們 Java 的存儲是放在 Hbase 中,HBase 又放在 HDFS 中,HDFS 的底層就是物理上的磁盤。我想提升 Java 的查詢性能,從硬件上我就想,現(xiàn)在主流的磁盤 IOPS 可能就幾百,但是現(xiàn)在的 SSD,普通的 SSD 能達到幾萬,像 PCI 組建的 SSD 現(xiàn)在已經(jīng)達到 30、40 萬 IOPS。我們今年年初做了一件事情,我們把 Hadoop 集群從 2.4 升到了 2.7,2.7 里面引入了一個非常重要的特性,叫異構(gòu)存儲,或叫混合存儲。就是 Hadoop 支持我把我的磁盤標(biāo)記一個標(biāo)簽,支持四種標(biāo)簽。第一種叫內(nèi)存磁盤,第二種叫 SSD,第三個是普通磁盤,最后一種是歸檔,歸檔是一種性能非常差的磁盤,可以認(rèn)為。定義了存儲策略。比如說 ALLSSD 是指我的所有副本全放在 SSD 中,ONE-SSD 就是放一份,放在 SSD 中。默認(rèn)是 Hot 全部放磁盤。我們這邊會把 HBase 的核心日志,還有核心業(yè)務(wù)都會放在 ALLSSD 中,對重要業(yè)務(wù)會使用 ONE-SSD,如果是普通業(yè)務(wù)放在磁盤上就行了。 剛才提到,我們其實為了節(jié)省成本,如果用 SSD 也是主要用 ONE-SSD。ONE-SSD 有一個問題,假如說這個客戶端讀取數(shù)據(jù),我有三個備份。其中一個是 SSD,但是我本地的副本是一個機械磁盤,默認(rèn)的社區(qū)版會優(yōu)先讀取本地,因為它的想法就是網(wǎng)絡(luò)延遲是比較高的。但實際上我們鏈家大數(shù)據(jù)已經(jīng)全部是萬兆的架構(gòu),所以說網(wǎng)絡(luò)瓶頸已經(jīng)是不存在的。我們這里給它定制了一個 First 選擇策略,叫 SSD-FIRST。就是如果我遠程有 SSD,我還是優(yōu)先使用遠程的。后面會給一個對比數(shù)據(jù),這個已經(jīng)提交給社區(qū)。 圖10是一個 HBase 就是讀寫分離。HBase 默認(rèn)所有隊列和處理線程實際上是不區(qū)分的,我查詢請求分 Scan、Get、Write。三種請求都會打到相同的隊列里面。如果出現(xiàn)了 Scan,把所有隊列和線程都堵滿了以后,簡單的小查詢都會卡住。這個是 HBase 在 1.2,還是 1.3 的時候已經(jīng)實現(xiàn)了,我們可以把隊列和線程按照比例去分配給 Scan、Get、Write 三種請求,避免 Scan 請求阻塞小請求。 圖11是我們 HBase 的測試數(shù)據(jù)。大概是三臺機器,使用 ONESSD 測試工具。我們就關(guān)注綠色這條線,這條線就是吞吐量,或者叫 QPS 。最右邊是 HDD,當(dāng)我們使用 ONE-SSD 以后,大概能提升 4 倍左右的吞吐。我們再作用,上線 SSD-FIRST 策略以后,又能提升一倍的吞吐。最后在上線讀寫分離以后又能提升一倍,整體提升的大概 10 多倍。而且只有第一步是需要硬件成本的,后面的兩個策略都是純策略上的調(diào)整。 圖12還有很多常見的一些優(yōu)化項,我這邊就不詳細(xì)解釋了。主要是針對 Hadoop 的一些讀寫優(yōu)化。 下面講一些操作系統(tǒng)常見的一些調(diào)優(yōu)。首先給大家講一個最常見的命令 Free,運行出來能顯示當(dāng)前內(nèi)存的情況。我這里寫了三種境界,第一種是剛接觸 Linux 同學(xué),F(xiàn)ree 是 0,是不是內(nèi)存不夠了,是不是感覺要掛。經(jīng)過一段時間學(xué)習(xí),發(fā)現(xiàn) Cache,Buffer 都是可以拿來用的??吹诙?, Free 是 4G,內(nèi)存什么問題都沒有。真正老司機是什么樣的呢?我們要知道 Cache 是用來干什么的,使用的命中率如何,Cache 是干什么的,Cache 默認(rèn)就是 Page Cache,就是頁面緩存,主要是為了緩存文件系統(tǒng),它在 VFS 里面會用到的。然后一般的 Cache 都是可以釋放出來給程序使用的。但是有一種情況 Cache 不能釋放。就是有一種文件系統(tǒng)叫 tempfs,相當(dāng)于內(nèi)存的一個文件系統(tǒng),利用了 Page Cache 的空間來做存儲,這種數(shù)據(jù)是沒法被清掉的。所以 Cache 并不是所有的可以用。如果拿來做文件緩存,本身操作系統(tǒng)這么設(shè)計一定是有它的作用,我們想知道這個緩存命中率如何。 我看了一下網(wǎng)上各種工具,實際上沒有一個對緩存命中率計算的方法。還是剛才提到做火焰圖那哥們,在他的博客上公布了一個利用 ftrace 的方案,原理其實非常簡單。命中率我們現(xiàn)在算 100%-Miss 的概率,Miss 是什么情況呢?Miss 的分母就是 Page 的訪問次數(shù),分子是,如果發(fā)生了 Misses,我就會把它從磁盤上讀出來,再放到緩存中。所以說分子就是添加到 Page 緩存的一個次數(shù)。我們這邊因為 ftrace 會在系統(tǒng)上有問題,我用 System Tap 把腳本改寫了一下,著重是四個系統(tǒng)的調(diào)用,System Tap 就是一個動態(tài)跟蹤技術(shù),它可以在內(nèi)核任何一個函數(shù)上動態(tài)去注入一段代碼,然后可以規(guī)定在函數(shù)運行前或者是運行后,我們這里只是給了四個過濾器,就是在四個條件上面做了過濾。 下面大家看一下 Total,里面第一項叫 Mark Page Accessed,就是頁面訪問次數(shù),Miss 的第一項就是添加到 LRU 緩存的一個次數(shù)。后面減掉的一部分是什么呢?大家知道,頁面緩存除了去讀的時候用到,寫的時候也會用到。任何一次寫入都會寫入到 Page 中。有時候我們要從分子分母中把寫入的那部分?jǐn)?shù)據(jù)給減掉。我們會把 Cache 的命中率定期同步到我們里面做監(jiān)控。我們線上現(xiàn)在應(yīng)該能達到 70%-90% 的命中率。據(jù)我們的試驗,一般在 60% 以上,HBase 會比較穩(wěn)定,因為 HBase 對于 IO 性能要求非常高。如果 Cache 命中率非常高,實際上每次讀寫都會非???。當(dāng)然這個也跟業(yè)務(wù)有關(guān)系。如果你的查詢是非常隨機的話,這個緩存也很容易被弄臟。 下面講一個比較大的坑:NUMA?,F(xiàn)在大家的服務(wù)器基本上都是多個 CPU,常見的有兩種架構(gòu),一種叫 SMP,對稱多處理。內(nèi)存其實是跟 CPU 之間連著一根總線。它的問題就是隨著CPU 越來越多,內(nèi)存越來越大,所有的瓶頸都壓在總線上。接下來英特爾公司就想到另一個方案,就是我每個 CPU 給它自己專屬的內(nèi)存,這個 CPU 的專屬內(nèi)存訪問速度非常快?,F(xiàn)在內(nèi)存非常大,每個地方的專屬內(nèi)存應(yīng)該是足夠的,如果我實在是需要遠程內(nèi)存,我再走主線。默認(rèn)是一個親和模式,什么意思呢?就是我這個 CPU 分配的內(nèi)存和淘汰的內(nèi)存都會優(yōu)先從我的專屬內(nèi)存中來找,比如說我專屬內(nèi)存不夠用了,我就會優(yōu)先從我專屬內(nèi)存里去淘汰。這樣會導(dǎo)致一個問題,我系統(tǒng)看系統(tǒng)非常充足,但是我發(fā)現(xiàn)有 SWAP 占用,我的 Java 在做 GC。這個就非常奇怪。其實就是因為默認(rèn)的親和模式造成的。這個親和模式實際上對小內(nèi)存應(yīng)用非常好的,因為我一看 CPU 內(nèi)存其實也很大了,至少 64G、128 個 G 都有,因為總共可能 256、512。單個小內(nèi)存應(yīng)用內(nèi)存是足夠的,但是像 HBase 或者說數(shù)據(jù)庫,我們現(xiàn)在可以到 80G,甚至 100G,實際上單個 CPU 的專屬內(nèi)存是不夠用的。我們這兒主要做了一個調(diào)整策略,就是把它的分配參數(shù)調(diào)整了一下。如果專屬內(nèi)存不夠的時候,我允許從其他地方獲取內(nèi)存,而不是去做本地的回收。 最后一個也是比較大的一個坑,就是透明大頁。什么是透明大頁呢?大家知道,現(xiàn)在操作系統(tǒng)都是分頁的,每一個程序有一個頁表。程序分配都是邏輯地址,它要真的訪內(nèi)存要通過一次邏輯地址到物理地址映射,然后 TLB 是用來做一個加速的,現(xiàn)在默認(rèn)的大小是 4K,隨著內(nèi)存越來越大就會有問題,頁表會越來越長,TLB 緩存命中率也會降低。操作系統(tǒng)想到一個方式,我就把配置調(diào)大,就給了兩個選項,一個是 2M,最大可以開到 1G。這樣看起來非常完美,但是在 6.5 的 centOS 中,這個大頁是默認(rèn)開啟的,但是我們會發(fā)現(xiàn) HBase 在使用過程中,CPU 占用會非常異常,System Tap CPU 會非常高。大家可以看,JVM 的使用才 24%,但是我內(nèi)核達到 32%,我們用 Perf 工具來做這個實時采樣。會發(fā)現(xiàn) Kernel 會發(fā)生在一個自選鎖上,自選鎖里面是什么呢?就能看到,會做一個異名的一個大頁內(nèi)存分配。這個就會造成在系統(tǒng)非常繁忙的時候,內(nèi)核的 CPU 占用非常高。然后我們做了一個測試,在開啟透明大頁和關(guān)閉透明大頁,看藍色和紅色的線,在開啟透明大頁的功能以后,就是 Always 這個選項下,它的性能有很多毛刺,性能總體會下降 30%- 40%。所以說我們推薦把透明大頁關(guān)閉。當(dāng)然操作系統(tǒng)還有很多其他的一些選項,這邊就不詳細(xì)說了,比如說文件數(shù)限制,關(guān)閉 SWAP ,TCP 這一塊。 以上就是我今天的分享,謝謝大家。 快來與國內(nèi)頂尖技術(shù)領(lǐng)導(dǎo)者一起“游學(xué)”硅谷:
戳“閱讀原文”了解活動詳情,關(guān)注高可用架構(gòu)公眾號更可享8折優(yōu)惠! 最后五個席位,等你前來~ |
|