李陽:京東零售OLAP平臺建設(shè)和場景實(shí)踐
導(dǎo)讀:今天和大家分享京東零售OLAP平臺的建設(shè)和場景的實(shí)踐,主要包括四大部分:
- 管控面建設(shè)
- 優(yōu)化技巧
- 典型業(yè)務(wù)
- 大促備戰(zhàn)
01管控面建設(shè)
1. 管控面介紹
管控面可以提供高可靠高效可持續(xù)運(yùn)維保障、快速部署小時交付的能力,尤其是針對ClickHouse這種運(yùn)維較弱但是性能很高的OLAP核心引擎,管控面就顯示得尤其重要。
2. 架構(gòu)設(shè)計(jì)
管控面的整體架構(gòu)設(shè)計(jì)如上圖所示,從開始請求、域名解析和分流規(guī)則,到達(dá)后端服務(wù)adminServer,adminServer有一層校驗(yàn)層,校驗(yàn)完成后會向隊(duì)列中發(fā)送任務(wù),worker會不斷地消費(fèi)隊(duì)列中的任務(wù),消費(fèi)完成后會將任務(wù)的結(jié)果寫到后端的存儲。如果有大量的集群的部署、配額的更改,就會有一系列的任務(wù)在這里完成。完成之后,再到數(shù)據(jù)部門進(jìn)行保存,這就是整體的架構(gòu)設(shè)計(jì)。
3. 業(yè)務(wù)管理
在業(yè)務(wù)管理方面,管控面可以提供以下功能:
- 可以用于用戶的集群賬號的申請;
- 業(yè)務(wù)級別的登記;
- 用戶可以進(jìn)行配額查詢,這些配額主要包括查詢數(shù)、執(zhí)行的并發(fā)以及超時等;
- 用戶可以自定義監(jiān)控告警,通過這些監(jiān)控告警去實(shí)時探索自己的整體服務(wù)的可靠性和穩(wěn)定性;
- 慢查詢統(tǒng)計(jì)告警,可以通過管控面看到當(dāng)前集群業(yè)務(wù)有多少慢查詢以及錯誤的查詢、查詢的總數(shù)等。
4. 運(yùn)維管理
在運(yùn)維管理方面:
- 第一,可以進(jìn)行新集群的部署,比如物理資源或者容器資源已經(jīng)申請好之后,可以及時進(jìn)行創(chuàng)建資源,并及時給用戶使用;
- 第二,比如ClickHouse有節(jié)點(diǎn)故障時(例如硬件故障如CPU、內(nèi)存或磁盤故障),要進(jìn)行及時的節(jié)點(diǎn)上下線或者節(jié)點(diǎn)替換,否則就會影響整個集群,一是影響DDL,二是影響寫入。
- 第三,可以做配額的管控,這一點(diǎn)在大促中非常有用,它可以用于限制用戶的查詢數(shù)、并發(fā)還有超時等,防止突增的流量,導(dǎo)致集群的不穩(wěn)定。
- 第四,可以進(jìn)行集群的巡檢,集群巡檢之后,可以查看每個集群的服務(wù)狀態(tài),比如它是否可以創(chuàng)建表、刪除表、插入數(shù)據(jù)、查詢數(shù)據(jù)是否都正常等,也有實(shí)時告警集群巡檢的服務(wù)狀態(tài)。
以上就是我們京東零售OLAP管控面核心功能,它在集群運(yùn)維方面不僅提升集群交付的效率,還節(jié)約運(yùn)維的成本。
02優(yōu)化技巧
1. 場景難點(diǎn)
京東零售是以電商交易和用戶流量為核心的場景,有以下兩方面難點(diǎn):
- 第一點(diǎn)是交易的業(yè)務(wù)比較復(fù)雜,需要關(guān)聯(lián)多張表、sql中的邏輯多,另外就是數(shù)據(jù)會實(shí)時更新,比如交易的狀態(tài)和金額的變化、組織架構(gòu)的變化等;
- 第二點(diǎn)是流量數(shù)據(jù),它有個特點(diǎn),首先追加不修改,其次是量大,因?yàn)榘擞脩舻狞c(diǎn)擊和瀏覽等各類行為的數(shù)據(jù),以及衍生的各種指標(biāo),比如UV的計(jì)算。最后是它的數(shù)據(jù)質(zhì)量也會經(jīng)常變化。
針對以上場景難點(diǎn),我們主要用到了實(shí)時的數(shù)據(jù)更新,還有物化視圖、join的優(yōu)化。接下來通過一些具體案例詳細(xì)講解。
2. 實(shí)時數(shù)據(jù)更新
首先看一下實(shí)時數(shù)據(jù)更新。我們創(chuàng)建了兩張表,一張是本地表,還有一張是分布式表。
本地表主要采用ReplacingMergeTree去重的引擎,字段分別是create_time創(chuàng)建時間、ID、comment注釋,還有數(shù)據(jù)的版本,分區(qū)是創(chuàng)建時間進(jìn)行格式化得到的天分區(qū),然后按照ID進(jìn)行排序鍵去重?,F(xiàn)在的需求是對相同的ID進(jìn)行實(shí)時的數(shù)據(jù)更新。
我們在集群的兩個分片中,比如分片1插入了三條數(shù)據(jù),分片2插入了三條數(shù)據(jù)都是相同的ID(0),但是查詢分布式表發(fā)現(xiàn),數(shù)據(jù)并沒有去重。
第一種解決方式是使用optmize去重。通過執(zhí)行一個optmize去重之后,通過查詢本地表就發(fā)現(xiàn)optmize在多分區(qū)間和分片間不能去重,只能在同一個分區(qū)中去重。
第二種方式是使用final去重。通過查詢一個本地表的final,發(fā)現(xiàn)剛才的11日和12日的數(shù)據(jù)只保留了一條數(shù)據(jù),這時再通過查詢分布式表final去重,發(fā)現(xiàn)有兩條12日的數(shù)據(jù),所以我們的結(jié)論是final的方式在多個分區(qū)間可以去重,但是在多分片間不能去重。
因?yàn)槲覀兊募憾际嵌喾制模赃€有第三種方式——使用argMax。我們通過argMax加了一個數(shù)據(jù)的版本,可以選擇最大的一個版本號,然后通過去查詢分布式表,發(fā)現(xiàn)argMax可以在多分片間去重,這也是我們推薦使用的一種方式。
所以實(shí)時數(shù)據(jù)更新方式一般有以上三種,但是各種方案更新的范圍不同,我們可以根據(jù)自己的業(yè)務(wù)場景去使用不同的去重方式,optmize可以在分區(qū)范圍內(nèi)去重,final可以在本地表范圍內(nèi)驅(qū)動,而argMax可以在分布式表范圍內(nèi)去重。
3. 物化視圖
接下來,我們看一下物化視圖。使用物化視圖的場景,比如:業(yè)務(wù)最近3小時看小時的數(shù)據(jù),三天之前想看天粒度的數(shù)據(jù),這時候物化視圖,就是很好的選擇。那么物化視圖該如何使用?我們看一下這個案例,有一張明細(xì)表test,它大概有13億行左右,直接實(shí)時的count聚合進(jìn)行查詢,發(fā)現(xiàn)它的耗時大概是2.1秒左右,怎樣能讓查詢變得更快一些?
我們創(chuàng)建了一張物化視圖,對原始表進(jìn)行預(yù)聚合,物化視圖選用了SummingMergeTree,這是聚合的一種引擎,大家也可以選擇其他引擎去聚合。它會根據(jù)排序鍵進(jìn)行二次聚合,也就是 Date 字段。還有一個select語句,它的作用是通過批次寫入,把這個select語句寫入到物化視圖列表中。
我們創(chuàng)建物化視圖之后,再去執(zhí)行相同的語句,查詢性能提升了大概113倍,耗時0.002秒左右,所以物化視圖在比如量大而且可以預(yù)聚合的這種場景下非常好用。
那么物化視圖就又是什么原理能夠達(dá)到這樣的效果?整體如圖所示。
物化視圖會創(chuàng)建一個隱藏的內(nèi)表來保存視圖里面的數(shù)據(jù),然后物化視圖會將寫入原始表的數(shù)據(jù),也就是通過select第一次聚合后的結(jié)果,寫入物化視圖的內(nèi)表中列表,再根據(jù)排序鍵進(jìn)行二次聚合,這樣原始表的數(shù)據(jù)量會大量減少,查詢就可以得到加速。
4. join優(yōu)化
在正式介紹join優(yōu)化前先補(bǔ)充一點(diǎn)基礎(chǔ)知識:對本地表的查詢我們稱之為部分查詢,以下劃線L為結(jié)尾的表稱為本地表。在做這種優(yōu)化之前,先看一下整體的分布式表執(zhí)行的流程。
首先分布式表會將查詢拆分成對本地表的查詢。比如city在精確去重之后,查詢分布式表,通過路由下發(fā)到各個分片的本地表上面進(jìn)行查詢,然后第一個接收到的查詢的節(jié)點(diǎn),再將本地的查詢部分的結(jié)果進(jìn)行合并,返回給用戶,這是整體分布式表執(zhí)行的流程。
join的執(zhí)行過程如上圖所示。比如select id, name, score from student join score,首先展開分布式表,向每個分片分發(fā)請求,計(jì)算左表的每個本地表join的結(jié)果,第二步當(dāng)分片收到1中的請求后,需要計(jì)算右表的結(jié)果,向每個分片再發(fā)送請求。這樣假如集群有100個分片,就需要100×100的部分查詢,每一次展開都要通過磁盤網(wǎng)卡,都會有耗時。
第一種優(yōu)化是global join。在原始的查詢中,會先計(jì)算右表結(jié)果,展開第一個分布式表,然后合并,成為一個臨時表,假設(shè)命名為b_004,這是第一次展開。第二次展開時,它會將臨時表b_004發(fā)送,所有的分片計(jì)算部分的join結(jié)果,就是第二次展開的分布式表,然后第三步,合并2中的結(jié)果,為最終的結(jié)果。這樣整體的global join就是,假如我們有100個分片,就只需要2×100次的部分查詢,大大減少了查詢。
第二種優(yōu)化方案就是本地join,將右表的分布式表改成本地表。這種方式的執(zhí)行流程是,我們展開左表,只需要把左表的分布式表下發(fā)到各個分片上面,而右邊它本身就是本地表,就直接進(jìn)行合并計(jì)算,最后會合并整個部分結(jié)果即為最終的結(jié)果。假如總共有100個分片,只需要展開100次,下發(fā)每個分片,100次的查詢就行了,這樣就減少了帶寬消耗,提升了性能。
可以優(yōu)先使用本地join,其次是global join,最后要小表放在右邊,這樣就可以提升join的性能。
以上就是我們針對業(yè)務(wù)場景難點(diǎn)的一些優(yōu)化技巧。
03典型業(yè)務(wù)
我們也希望實(shí)現(xiàn)高并發(fā)查詢,有大吞吐的寫入,但是ClickHouse在默認(rèn)的配置下,不支持高并發(fā)的查詢,而且寫入也很慢,這是我們業(yè)務(wù)上的兩大痛點(diǎn)。下面具體看一下兩種場景。
1. 高并發(fā)查詢
以廣告實(shí)時跟單項(xiàng)目為例,它是用于實(shí)時產(chǎn)生廣告效果,最終數(shù)據(jù)報表展示,幫助廣告主執(zhí)行營銷計(jì)劃落地。如圖所示,可以看到每秒的QPS達(dá)到將近2000,這是618時候的一個截圖。我們的集群整體的配置是7分片6副本1進(jìn)程,硬件的配置是42臺32C128G,900G*3的SSD的磁盤,整個集群的QPS可以達(dá)到2000。當(dāng)然這個配置如果要達(dá)到2000的話,我們要進(jìn)行一系列的技術(shù)優(yōu)化。
首先第一點(diǎn)技術(shù)優(yōu)化就要增加副本,因?yàn)樵黾痈北究梢蕴嵘麄€集群的并發(fā)能力。第二是max_threads,減少每一個查詢所用的線程數(shù),ClickHouse如果不設(shè)置這個參數(shù),會用物理內(nèi)核的所有線程去進(jìn)行查詢,這樣就會導(dǎo)致有些任務(wù)無法調(diào)度,所以要設(shè)置這個參數(shù)。第三就是要調(diào)整query_thread_log的存儲,因?yàn)榇罅康腝PS過來,會有很多的請求日志,如果我們不調(diào)整存儲,很快就會將磁盤打滿,造成集群的不可用。
上圖展示了優(yōu)化前后的最大穩(wěn)定運(yùn)行并發(fā)數(shù)。優(yōu)化前,大概只能達(dá)到1000QPS,同樣的集群下優(yōu)化后可以穩(wěn)地運(yùn)行在2000QPS左右,可以滿足業(yè)務(wù)需求。
2. 大吞吐寫入
第二個典型業(yè)務(wù)是大吞吐的寫入。以京東云監(jiān)控項(xiàng)目為例,它負(fù)責(zé)京東云負(fù)載均衡訪問日志的存儲,日志量極其大,單集群寫作的峰值可以達(dá)到6000億條/天,還可以保持?jǐn)?shù)據(jù)的強(qiáng)一致??梢钥吹郊喝粘4蟾攀?G/秒,大促可達(dá)到6G/秒。我們的集群配置是60分片兩副本1進(jìn)程,硬件配置是120臺64核的256G1T*1的SSD。這樣集群配置下,我們可以實(shí)現(xiàn)這6000億條每天的寫入。為支持這個寫入量,我們也需要一系列的技術(shù)優(yōu)化。
第一點(diǎn)就是引入了chproxy流量負(fù)載均衡,請求粒度細(xì)化至每條sql,這樣每一個sql請求都會路由到不同的節(jié)。如果不引入chproxy,就會通過域名的方式直連客戶端,直連集群,如果連接不及時釋放,就會一直往節(jié)點(diǎn)里寫,很容易就把集群單節(jié)點(diǎn)打爆了。引入了chproxy的流量負(fù)載平衡之后,sql就可以均衡地路由到各個節(jié)點(diǎn)。
第二點(diǎn)就是本地表的寫入,可以提升整體的寫入性能,大概是分布式表的兩到三倍左右。
最后我們看一下優(yōu)化前后,每天最大的寫入量,優(yōu)化前大概是1000億每天,優(yōu)化后可以達(dá)到6000億每天,這樣就實(shí)現(xiàn)了大吞吐的寫入。
04大促備注
電商場景下,經(jīng)常遇到大促備戰(zhàn),需要保證olap服務(wù)的穩(wěn)定性。
大促備戰(zhàn)的整體流程如圖所示,我們在不同的時間段需要做不同的事情。一開始是啟動備戰(zhàn)制定備戰(zhàn)方案,收集業(yè)務(wù)的資源需求,梳理業(yè)務(wù)等級,接下來是集群的擴(kuò)容壓測,還有故障演練優(yōu)化等,最后迎來開門紅,決戰(zhàn)618。
我們的OLAP是如何保證業(yè)務(wù)的呢?
第一,業(yè)務(wù)資源收集以及等級確認(rèn)。大促前,我們平臺會向業(yè)務(wù)收集有資源的需求以及等級確認(rèn),并做合理的規(guī)劃和分配,來保障大促的流量急增時有足夠的資源支撐運(yùn)轉(zhuǎn)。比如資源需求,可能有新上線的業(yè)務(wù)、擴(kuò)容的業(yè)務(wù)、遷移的業(yè)務(wù),還有替換已有集群的業(yè)務(wù),這些都是我們大促之前要進(jìn)行梳理的,這樣可以提前做好預(yù)案。
第二,業(yè)務(wù)方要及時的訂閱監(jiān)控和報警。比如監(jiān)控有CH系統(tǒng)層的、服務(wù)層的,還有CH查詢和寫入層的監(jiān)控。我們有兩個告警系統(tǒng):一個是服務(wù)層的,比如監(jiān)控CH的一些重要的指標(biāo),ZK的一些監(jiān)控告警,以及chproxy流量負(fù)載的一些監(jiān)控報警等;另一個是系統(tǒng)層的MDC告警,例如CPU、內(nèi)存、磁盤、連通性,這些主要是監(jiān)控硬件是否有故障。右圖就是報警和監(jiān)控的樣例,我們可以通過它們來及時修復(fù)集群故障,也需要業(yè)務(wù)方去訂閱這些監(jiān)控和報警,來一起監(jiān)督整個集群的穩(wěn)定性和可靠性。
大促集群是如何保障的呢?
第一點(diǎn)是壓測,我們要進(jìn)行高保真的一些壓測,壓測的結(jié)果,要設(shè)置合理的配額,比如我們共享集群的CPU一般是40%,獨(dú)占集群是80%,我們通過這些目標(biāo)值設(shè)置業(yè)務(wù)的合理的配額。如果壓測有問題,我們可以及時的協(xié)助業(yè)務(wù)方進(jìn)行優(yōu)化,來滿足他們的QPS和集群的穩(wěn)定性。
第二點(diǎn)是故障演練。我們的故障演練有很多,其中第一就是雙流切換。比如我們的零級業(yè)務(wù)就是非常核心的業(yè)務(wù),要進(jìn)行主備雙流,在不同的機(jī)房分別部署了兩個集群,如果同一個機(jī)房有問題,要及時切到備用集群去。另外就是故障的修復(fù)。故障發(fā)生后,我們要通過管控面進(jìn)行及時下線或者替換,來保證集群的穩(wěn)定性和業(yè)務(wù)的可用性。
第三點(diǎn)就是降級措施。我們的降級措施會針對不同的業(yè)務(wù)等級進(jìn)行合理分配,尤其是大促的時候不參加壓測的業(yè)務(wù)。如果不參加壓測,我們就會在大促前期進(jìn)行業(yè)務(wù)降級,防止他們的突增流量影響大促核心業(yè)務(wù),以保證大促時整體的集群穩(wěn)定性。
以上三點(diǎn)就是我們集群保障最核心的三個步驟,從一開始的高保真壓測,到故障的演練,再到最后的降級措施,我們都會和業(yè)務(wù)方一起去完成,以保證整體穩(wěn)定運(yùn)行。
05精彩問答
Q:請問老師您在這個話題中遇到的最大的挑戰(zhàn)是什么?
A:我遇到的最大挑戰(zhàn)就是解決高并發(fā)的問題,因?yàn)楦卟l(fā)瞬間QPS能達(dá)到2000以上,而我們的ClickHouse默認(rèn)就是100個并發(fā)。我們在高并發(fā)方面做出了很多技術(shù)調(diào)優(yōu),可以讓業(yè)務(wù)達(dá)到高并發(fā)的場景。高并發(fā)的場景,遇到過很多問題,我們首先增加了多副本(一般默認(rèn)情況下就是三副本或者兩副本來保證數(shù)據(jù)的安全),因?yàn)槊吭黾右慌_副本,就可以提升整體的一個分片的查詢能力。我們還進(jìn)行了一些參數(shù)調(diào)優(yōu),比如如果高并發(fā)過來,有很多的隊(duì)列,這些線程我們都要去控制好,不然很容易就無法調(diào)度了。另外,高并發(fā)場景會很容易把集群的一些日志給打滿,因?yàn)槲覀兊拿恳粭l查詢都會記錄一條日志,我們要把日志的表的存儲周期設(shè)置小一點(diǎn)。還要加快它的merge,因?yàn)槿绻患涌靘erge,刪除數(shù)據(jù)就很慢,也很容易將磁盤打滿,這是查詢?nèi)罩镜姆矫?。第三點(diǎn)就是高并發(fā)很容易觸發(fā)我們的一些配額的限制,我們要對它進(jìn)行一些放大。我們要進(jìn)行內(nèi)存的一些限制,如果不進(jìn)行這些限制,或者是不放大這些限制都會引發(fā)QPS達(dá)不到,造成整體的穩(wěn)定性和可用性不夠。
還有一個難點(diǎn)是join的優(yōu)化,效能優(yōu)化里面其中有一個是本地join,本地join我們也做了很多的測試。比如和字典表做對比,我們發(fā)現(xiàn)字典表在100萬以下的數(shù)據(jù)量,就是使用字典表做join性能較好,100萬以上我們發(fā)現(xiàn)用本地join就非常好,我們通過一系列的測試實(shí)驗(yàn)才得到這個結(jié)論。一開始我們都是用字典表去進(jìn)行黃金眼刷,但是我們最后發(fā)現(xiàn)在一定的性能之上,字典表還不如本地表的join。大量的POC才得到了這個結(jié)論。所以大家在字典表和本地join,也可以自己做一下全面的性能測試。
以上就是我們的兩點(diǎn)挑戰(zhàn)。
Q:OLAP是什么?主要用哪些引擎?
A:OLAP是在線的多維高性能實(shí)時分析服務(wù),專業(yè)術(shù)語就是在線聯(lián)機(jī)查,和mysql OLTP在線事務(wù)查詢是兩種不同的類型。OLAP主要面向海量數(shù)據(jù)。
我們京東零售主要用clickhouse為主、doris為輔的兩個引擎?,F(xiàn)在最流行的就是ClickHouse,其次是doris和druid這兩個引擎,但是現(xiàn)在很多大廠,包括騰訊阿里字節(jié)都在往ClickHouse上面轉(zhuǎn),當(dāng)然京東零售也應(yīng)用ClickHouse兩三年了。我們也進(jìn)行了一系列的內(nèi)核的研發(fā),解決一些zookeeper的性能,還有在線彈性伸縮系統(tǒng)的一些東西,因?yàn)镃lickHouse在彈性伸縮系統(tǒng)方面不太好,所以我們也在做這方面的工作。
Q:看到有一個業(yè)務(wù)場景中使用了120臺高配置的機(jī)器,那么如果申請到這么多的資源進(jìn)行業(yè)務(wù)支持,怎么考慮投入產(chǎn)出?
A:我們投入了120臺,產(chǎn)出就是可以把整個京東云的所有的負(fù)載均衡。第一,我們?yōu)槭裁匆?20臺,為什么要用SSD的機(jī)型?還有為什么這么高配的機(jī)器?因?yàn)樗膶懭肓亢艽?,平均每天大?000億,算出每秒大概有1000萬的數(shù)據(jù)量在往集群里寫,如果不用這么高配的機(jī)器,磁盤已經(jīng)是SSD了,它的性能永遠(yuǎn)達(dá)不到這個效果。第二點(diǎn)就是投入產(chǎn)出比,我們可以通過這個集群監(jiān)控整個京東云的日志,還有負(fù)載均衡的效果。比如京東云,一是對外,二是對內(nèi),監(jiān)控和負(fù)載均衡都是非常重要的,所以用了我們的京東零售的OLAP來實(shí)監(jiān)控京東云的一個整體效果,還有整體穩(wěn)定性,這樣產(chǎn)出比就非常大。
Q:主備庫切換時數(shù)據(jù)有延遲嗎,如何做到讓用戶感知最???
A:主備庫切換,我們采用的是雙寫的流程,我們核心的業(yè)務(wù)都是雙寫的,就算在日常也都是雙寫,然后分流去查詢,不會造成主備儲備的集群的空閑。大促的時候,會采用一個百分比,比如說或者100%在主機(jī)型另一個集群就是當(dāng)做備用,或者是會按照一定的比例80%-20%左右采用雙寫。業(yè)務(wù)方切換的時候基本上沒有任何延遲,只是將域名切換了一下,數(shù)據(jù)都是在實(shí)時寫入,兩個集群,基本上沒有延遲。這是我們準(zhǔn)備切換的一個功能。
Q:想問一下咱們的調(diào)優(yōu)過程是怎么樣的?
A:我們的調(diào)優(yōu)過程先是結(jié)合自己的經(jīng)驗(yàn),去優(yōu)化一些參數(shù),業(yè)務(wù)再進(jìn)行壓測。因?yàn)橄脒_(dá)到這么大的QPS和這么高的大吞吐的寫入,要時常進(jìn)行壓測,壓測時如果遇到問題,會進(jìn)行內(nèi)核源碼的分析,然后再進(jìn)行一系列參數(shù)調(diào)優(yōu)或者內(nèi)核優(yōu)化。
今天的分享就到這里,謝謝大家。