天天看點

如何保障“雙11”期間億萬買家和賣家愉快地聊天

如何保障“雙11”期間億萬買家和賣家愉快地聊天

作者 | 阿裡雲存儲

來源 | 淩雲時刻

背景

又一屆億萬買家和賣家參與的“雙 11”落下帷幕,“雙 11”這場消費盛宴離不開阿裡雲産品技術團隊的大力支撐,阿裡雲存儲團隊的表格存儲就是其中之一。

把時間向前撥動 10 個月,突如其來的疫情導緻的線上辦公的剛性需求,讓釘釘對現有存儲系統在成本、可運維性、穩定性方面提出了極大的挑戰。經過一系列調研,釘釘最終選擇阿裡雲存儲團隊的表格存儲 Tablestore 作為統一的存儲平台。經過釘釘團隊和表格存儲團隊的緊密合作,釘釘 IM 系統、IMPaaS 多租戶平台的所有消息同步資料和全量消息資料已經基本完成遷移工作。在剛剛過去的 2020 雙 11 購物節,Tablestore 第一次全面支援集團 IM(釘釘、手淘&天貓&千牛客服聊天、餓了麼等)并平穩度過,保障億萬買家和賣家之間更為順暢的交流。

本文将介紹釘釘 IM 存儲架構、表格存儲 Tablestore 為了滿足遷移在穩定性、功能、性能上做的一系列工作。

關于表格存儲 Tablestore 與釘釘消息系統

阿裡雲表格存儲 Tablestore 是一款面向海量結構化資料存儲、搜尋和分析一體的線上資料平台,可擴充至單表 PB 級存儲規模,同時提供每秒億次通路服務能力的産品。作為阿裡雲智能自研産品,Tablestore 具有高性能、低成本、易擴充、全托管、高可靠、高可用、冷熱資料分層和靈活計算分析能力等特性,尤其是在中繼資料、大資料、監控、消息、物聯網、軌迹溯源等類型應用上有豐富的實踐經驗和積累。下圖展示了 Tablestore 的産品架構。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

釘釘消息系統

釘釘消息系統包括:釘釘 IM 系統和 IMPaaS 系統,其中釘釘 IM 系統承載了釘釘 App 的 IM 業務,IMPaaS 作為多租戶 IM 平台支援集團各團隊的接入,目前已經承載了釘釘、手淘&天貓&千牛客服聊天、餓了麼等、高德等集團主要 IM 業務。兩套系統在架構方面是統一的,是以本文後續中不會明顯區分兩套系統(為了友善,統一描述為 IM 系統)。該系統負責個人和群組消息的接收,存儲和轉發環節,同時也包括會話管理,已讀通知等狀态同步。一句話概括:負責端與端的溝通。

存儲架構更新

IM 系統已有架構使用的存儲系統比較多,主要包括 DB(InnoDB、X-Engine)和 Tablestore。其中會話和消息的存儲使用 DB,同步協定(負責消息同步推送)使用 Tablestore。在疫情期間,釘釘的消息量增長非常迅速,讓釘釘團隊意識到使用傳統的關系資料庫引擎來存儲流水型的消息資料局限性較大,不管是在寫入性能、可擴充性、還是存儲成本上,基于 LSM 的分布式 NoSQL 都是更佳的選擇。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

上面是 IM 系統原來的架構圖。在這套系統中,一條消息發送後,會有兩次存儲,包括:

1. 全量消息存儲:如圖中第 4 步,将消息存儲到 DB 中持久化。該消息主要用于使用者主動拉和系統展現 App 的首屏資訊。全量消息會永久保留。

2. 同步協定存儲:如圖第 5 步,将消息存儲到 Tablestore 中,然後讀取合并推送到使用者。同步協定存儲的資料隻會保留若幹天。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

而在架構更新後,全量消息存儲去掉了對 DB 的依賴,把資料全部寫入 Tablestore 中,改造之後 IM 系統存儲隻依賴 Tablestore,并且帶來如下收益:

1. 成本低:Tablestore 基于 LSM 存儲引擎的架構在成本上比分庫分表的架構有優勢,資料存儲打開 EC 功能,冷熱分層存儲、雲服務的按量收費,都能使得釘釘的成本更低。按照目前計費情況,存儲遷移到表格存儲後,釘釘存儲成本節約 60% 以上。

2. 系統彈性能力強,擴充性好:可以按需擴容任意數量機器,擴容速度快且對業務無影響,在機器準備完成(包括克隆系統)的情況下,分鐘級完成擴容。在扛過叢集流量高峰之後,也可以快速縮容,節約資源。

3. 零運維:Tablestore 是全托管的雲服務産品,不需要使用者承擔任何運維工作,并且 Tablestore 也能做到運維期間業務無感覺。另外,Tablestore是 schema free 的架構,使用者也不必為業務需求變化帶來的表結構調整而煩惱。

穩定性保障

穩定性是一切的根本,也是業務選型最大的顧慮。在複雜的分布式系統中,每一個元件都有出問題的可能,大到機房挖斷光纜,小到網卡 bit 翻轉,每一個被漏掉的問題都可能導緻災難性故障,甚至引發嚴重的輿情風險。穩定性工作的核心就是充分考慮這些可能性并盡可能的提供容錯能力。作為阿裡雲智能的核心基礎産品,Tablestore 被部署在阿裡雲全球所有的服務區,穩定性得到了很好的錘煉,同時在服務釘釘的過程中,又專門做了穩定性的加強,具體包括:

主備雙叢集容災

如何保障“雙11”期間億萬買家和賣家愉快地聊天

為了避免單機房故障引起的服務不可通路,Tablestore 具備主備雙叢集容災的能力。主備叢集是兩個獨立的叢集,資料通過背景異步的方式來複制,是以這種容災方式并不能保證資料強一緻,一般會有幾秒的延遲。在主叢集單機房故障時,會将所有流量切換到備叢集,利用備叢集來提供服務,保證了服務的可用性。

3AZ 強一緻容災

如何保障“雙11”期間億萬買家和賣家愉快地聊天

主備容災雖然能解決單機房的容災問題,但是在主叢集非預期當機情況下不能做到資料強一緻,切換過程需要和業務配合(容忍一段時間内資料不一緻,後續配合補資料等)等缺點。3AZ(Available Zone)的容災模式就能解決這些問題。

3AZ 的容災部署方式是将一套系統部署在 3 個實體機房之上,寫資料的時候,盤古會将資料的 3 個副本均勻分布在 3 個機房,保證任何一個機房故障不可用,另外兩個機房都有全部資料,進而做到不丢資料。Tablestore 每次寫資料傳回成功後,3 個副本都已經在 3 個機房落盤。是以 3AZ 的架構是保證資料強一緻的。當其中一個機房故障時,Tablestore 就會将分區都排程到另外 2 個機房繼續對外提供服務。3AZ 的部署方式相比主備叢集有如下優勢:

1. 成本低,底層盤古的 3 份副本均勻分布在 3 個機房,而主備叢集需要多一份資料複制;後續還可以采用 EC 繼續降低成本。

2. 機房間資料強一緻(RPO = 0)。

3. 切換更順滑,由于資料是強一緻,是以業務不需要在切換期間做額外的處理邏輯。

負載均衡系統

在表格存儲 Tablestore 内部,為了使表有高擴充能力,表的存儲在邏輯上分為很多分區,服務會根據分區鍵範圍對表進行分區(range 分區),并将這些分區排程到不同的機器上對外服務。為了讓整個系統的吞吐和性能達到最優,應該讓系統内各個機器負載均衡,也就是需要分布在各個機器上的通路量和資料量實作負載均衡。

但實際上,在系統運作過程中,常常會出現由于設計問題或者業務問題帶來資料通路和分布不均衡的問題,常見的如資料傾斜、局部熱點分區等。這裡面上有兩個大的問題需要解決:1. 如何自動發現;2. 發現後如何自動處理。這裡強調自動主要是因為表格存儲 Tablestore 作為一個多租戶系統,上面運作的執行個體、表數量巨大,如果全靠人來回報和處理,不僅成本巨大,實際也沒有可操作性。表格存儲 Tablestore 為了解決這個問題,設計了自己的負載均衡系統,其中要點:1. 收集分區級别詳細的資訊統計,便于後續進行分析适用;2. 基于通路量的分裂點計算,也就是發現熱點;3. 多元度精細的分組功能,主要是為了管理分區的分布。在此基礎之上,負載均衡系統還開發了數十種政策分别來解決各種類型的熱點問題。

這套負載均衡系統能夠自動的發現并處理 99% 的叢集中常出現的熱點問題,也提供了分鐘級自動化處理問題,分鐘級白屏化定位以及處理問題的能力,目前已經全域運作。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

完善的流控體系

Tablestore 在流控方面提供了一整套完善的流控體系,大緻如下:

1. 執行個體和表級别的全局流控:主要解決業務流量超過期望,防止打滿叢集資源。如下圖,左側是流控架構,右側是流控模型,可以從執行個體以及表級别分别控制通路流量。如果執行個體級的流量超限的話,那麼該執行個體的所有請求都會受到影響;也在表級别可以單獨設定表的流量上限,避免單表的流量超限對其他表産生影響。依賴這套系統,Tablestore 能夠精确的對指定執行個體、表、操作進行定向流控,提供多元度的個性化控制手段,保障服務的 SLA。在疫情期間,釘釘多次出現流量打滿叢集的情況,全局流控就能比較好地解決該問題。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

2. 分區主動流控:Tablestore 按照 range 對資料進行分區,在業務資料有傾斜的時候容易發生分區熱點讀寫。分區主動流控主要解決單分區熱點問題,防止單分區熱點影響其他分區。在程序中會統計每個分區的通路資訊,包括但不限于:通路的流量、行數、cell 數等。

在上述流控體系的保障下,做到了多級的防禦,保障了服務的穩定。

極緻性能優化

Tablestore 是典型的存儲計算分離架構,其依賴元件多,請求鍊路複雜,是以性能優化不能隻局限在某一個子產品,需要有一個全局視角,對請求鍊路上的各個元件協同優化,才能獲得比較好的全鍊路性能優化效果。

在釘釘場景中,經過一系列優化,在相同 SLA 以及同等硬體資源情況下,讀寫吞吐提升 3 倍以上,讀寫延遲下降 85%。讀寫性能的提升,也節省了更多機器資源,降低了業務成本。下面介紹一下主要的性能工作。

存儲/網絡優化

在 Tablestore 的核心資料鍊路上,包含 3 個主要的系統元件:

1. Tablestore Proxy:作為使用者請求的統一接入層,做請求鑒權、合法性檢驗、轉發;當所有前置的檢查操作完成後,會将請求轉發到 Table Engine 層。

2. Table Engine:表格存儲引擎以 LSM tree 為處理模型,提供分布式表格能力。

3. 盤古:分布式檔案系統,用來承接表格存儲引擎在資料處理過程中的資料持久化工作。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

在請求鍊路中,第一跳網絡(Tablestore Proxy 到 Table Engine)和第二跳網絡(Table Engine 到盤古)在原來的架構中,使用的是基于 kernel tcp 網絡架構,會有多次的資料拷貝以及 sys cpu 的消耗。是以在存儲網絡優化上,将第一跳使用 Luna(新一代的使用者态 RPC 架構)作為資料傳輸,降低 kernel 的資料拷貝以及 sys 的 cpu 消耗;在資料持久化的第二跳中,使用阿裡雲分布式雲存儲平台——盤古提供的 RDMA 網絡庫,進一步優化資料傳輸延遲。

盤古作為存儲底座,新一代的盤古 2.0,面向新一代網絡和存儲軟硬體進行架構設計和工程優化,釋放軟硬體技術發展的紅利,在資料存儲鍊路上,獲得極大的性能提升,讀寫延遲進入 100 微秒級時代。基于存儲底座的架構優化,Table Engine 也更新了資料存儲通路,接入盤古 2.0,打通極緻的 IO 通路。

疊代器優化

Tablestore 的表格模型支援非常豐富資料存儲語義,包括:

1. 資料列多版本

2. 列的 schema-free,寬表模型

3. 靈活的删除語義:如行删除,列多版本删除,指定列版本删除

4. TTL 資料過期

每一種政策在實作的過程中,為了快速疊代,都以火山模型方式實作疊代器體系(如下圖左半部)。這個方式帶來的一個問題就是随着政策的複雜,疊代層次越來越深(即使不用到政策,也會被加到這個體系中);這個過程的動态多态造成的函數調用開銷大,并且從研發的角度看,很難形成統一視角,為後續的優化也帶來了阻礙。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

為了優化資料疊代性能,将每種政策需要的上下文資訊組裝成一個政策類,以一個全局的視角将政策相關的疊代器拍平,進而降低嵌套的疊代器層次在讀取鍊路上的性能損耗;在多路歸并上,使用列存提供的多列 PK 跳轉能力,降低多列歸并排序時的比較開銷,提升資料讀取性能。

鎖優化

除了存儲格式上的改進之外,資料在記憶體處理過程中,面臨的一個比較大的問題就是鎖競争造成的性能損失。而在系統各個子產品中無處不在,除了顯式的用于同步或并發臨界區的鎖外,也有容易忽略的原子操作的樂觀鎖性能問題(如:全局的 metric 計數器,CAS 指令沖突等),另外像小對象的記憶體配置設定,也可能造成底層記憶體配置設定器的鎖沖突嚴重。在優化鎖結構上,主要在以下方面做了優化:

1. 将隐式事務中,互斥鎖轉換成讀寫鎖;隻有顯示需要事務保證的請求,才通過寫鎖做串行化,對于正常路徑的無事務需求的請求,通過最大程度的讀鎖進行并行化。

2. 在讀路徑上,cache 的 lru 功能也引入了鎖做臨界區保護,在高并發讀取時,lru 的政策很容易造成鎖競争,是以實作了一個無鎖的 lru cache 政策,提升讀性能。

基于實踐的功能創新

作為阿裡自研 NoSQL 産品,Tablestore 在和釘釘合作過程中,充分發揮了自研的優勢,結合釘釘業務開發了多個新功能,進一步減少了 CPU 消耗,并且實作性能極緻優化和業務層的無感覺更新。

PK 自增+ 1 功能

釘釘同步協定使用了 Tablestore 的 PK 自增功能,PK 自增的功能是在使用者成功寫入一行資料後傳回一個自增 ID,首先簡單介紹下釘釘利用自增 ID 實作消息同步推送的場景:

1. 使用者發送消息,應用伺服器收到消息後,将消息寫入 Tablestore 中,寫入成功後,傳回消息的自增 ID。

2. 應用伺服器,根據上次已經推送的消息 ID,查詢兩次 ID 之間的所有消息。然後将查詢到的消息推送給使用者。

在上述場景中,有一種特殊的場景,就是該使用者隻有一條未推送消息,如果保障傳回消息的 ID 在保證自增的同時,還能保證自增+1,那麼應用伺服器再寫入消息後,如果發現傳回的 ID 和上次推送的 ID 隻相差 1,那麼就不需要從 Tablestore 中查詢,直接将該消息推送給使用者即可。

PK 自增+1 功能上線後,減少了 40% 以上的應用端讀,減少了 Tablestore 伺服器和業務自身伺服器的 CPU 消耗,進一步節約了釘釘業務成本。

局部索引

在此之前,Tablestore 已經具備了異步的全局二級索引功能。但是在釘釘場景下,絕大部分查詢都是查詢某個使用者之下的資料,即索引隻需在某個使用者自己的資料下生效即可。為此,我們開發了強一緻的局部索引功能,并且支援存量資料的動态索引建構。

應用場景

如下圖中的場景,使用者可以直接找到所有@自己的消息。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

對應到業務側,會有一張使用者消息表存儲該使用者的所有消息,然後在這張表上,建立一張最近@我的消息的局部索引表,那麼直接掃描該局部索引表,便可以得到 AT 該使用者的所有消息,兩張表結構分别如下圖:

如何保障“雙11”期間億萬買家和賣家愉快地聊天

消息主表

如何保障“雙11”期間億萬買家和賣家愉快地聊天

消息局部索引表

索引增量建構

對于增量索引建構,通過考察整個鍊路上的各個子產品,我們做了比較極緻的性能優化,使一行索引的建構時間可以做到 10us 左右:

1. 通過分析表的 Schema,與目前使用者寫入的資料進行對比,來節省掉不必要的寫前讀。

2. 通過記錄資料更新之前的原始值到日志,成倍的降低了索引建構成功後寫日志的 IO 資料量。

3. 通過批量的索引建構,在一次建構過程中,便計算出所有索引表的索引行。

4. 通過在進入日志之前便進行計算,充分利用了多線程并行的能力。

存量索引建構

對于存量資料的索引,在業界也是一個比較難處理的問題:局部二級索引要求強一緻,即寫入成功,就可以從索引表中讀到,另外一方面,存量索引建構過程中,希望盡量的減少對線上寫入的影響(不能停寫)。

如下圖所示,傳統資料庫沒有很好的解決這個問題,在存量到增量的轉換過程中,需要鎖表,禁止使用者的寫入,造成停服,甚至 DynamoDB 直接不支援在表上進行存量局部二級索引的建構。

如何保障“雙11”期間億萬買家和賣家愉快地聊天

傳統資料庫的存量建構方式

如何保障“雙11”期間億萬買家和賣家愉快地聊天

Tablestore 存量索引的建構方式

Tablestore 利用自身引擎的特性,巧妙的解決了這個問題,同時進行存量資料和增量資料的索引建構,做到了存量資料建構局部二級索引完全不需要停止線上寫入流量,而且存量建構結束後,整個索引建構過程即結束,業務層完全無感覺。

後記

數字化轉型過程不是“開着飛機換引擎”,而是“飛機加速時換引擎”。在業務快速發展過程中,如何保障存儲系統乃至整個業務系統實作平穩高效的更新将是每一位 CIO 需要解答的問題。

最後,歡迎加入釘釘公開群(釘釘号:11789671),一起探讨技術,交流技術。