天天看點

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

本文轉載自雲加社群公衆号

導語| 遠端辦公期間,線上會議使用者需求激增,騰訊會議8天完成100萬核雲伺服器擴充,Redis叢集僅在半小時以内就高效完成了數十倍規模的擴容,單叢集的擴容流程背景處理時間不超過30分鐘。在這背後,騰訊雲Redis是如何做到的呢?本文是伍旭飛老師在「雲加社群沙龍online」的分享整理,詳細闡述了騰訊雲Redis無損擴容的實踐和挑戰。

點選視訊,檢視完整直播回放

一、疫情帶來的挑戰

今年疫情帶來的挑戰很明顯,遠端辦公和線上教育使用者暴漲,從1月29到2月6日,日均擴容1.5w台主機。業務7×24小時不間斷服務,遠端辦公和線上教育要求不能停服,停服一分鐘都會影響成百上千萬人的學習和工作,是以這一塊業務對于我們的要求非常高。

線上會議和遠端辦公都大量使用了redis,使用者暴增的騰訊會議背後也有騰訊雲Redis提供支援,同時海量請求對redis的快速擴容能力提出了要求。我們有的業務執行個體,從最開始的3片一天之内擴容到5片,緊接着發現還是不夠,又擴到12片,第二天繼續擴。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

二、開源Redis擴容方案

1. 騰訊雲Redis的叢集版架構

騰訊雲Redis跟普遍Redis有差别,我們加入了Proxy,提高了系統的易用性,這個是因為不是所有的語言都支援叢集版用戶端。

為了相容這部分客戶,我們做了很多的相容性處理,能夠相容更多普通用戶端使用,像做自動的路由管理,切換的時候可以自由處理MOVE和ASK,增加端到端的慢查詢統計等功能,Redis預設的slowlog隻包含指令的運算時間,不包括網絡來回的時間,不包括本地實體機卡頓導緻的延時,Proxy可以做端到端的慢日志統計,更準确反應業務的真實延遲。

對于多帳戶,Redis不支援,現在把這部分功能也挪到Proxy,還有讀寫分離,這個對于客戶非常有幫助,客戶無須敲寫代碼,隻需要在平台點一下,我們在Proxy自動實作把讀寫派發上去。

這一塊功能放到Redis也是可以,那為什麼做到Proxy呢?主要是考慮安全性!因為Redis承載使用者資料,如果在Redis做資料會頻繁更新功能疊代,這樣對于使用者資料安全會産生比較大的威脅。

2. 騰訊雲Redis如何擴容

騰訊雲Redis怎麼擴容呢?我們的擴容從三個次元出發,單個節點容量擴容, 比如說三分片,每個片4G,我們可以每節點擴到8G。單節點容量擴容,一般來說隻要機器容量足夠,就可以擴容上去。

還有副本擴容,現在客戶使用的是一主一從,有的同學開讀寫分離,把讀全部打到從機,這種情況下,增加讀qps比較簡單的方法就是增加副本的數量,還增加了資料安全性。

最近的實際業務,我們遇到的主要是擴分片,

對于叢集分片數,最主要就是CPU的處理能力,擴容分片就是相當于擴充CPU,擴容處理能力也間接擴容記憶體。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

最早騰訊雲做過一個版本,利用開源的原生版的擴容方式擴容。

簡單描述一下操作步驟:首先Proxy是要做slot容量計算,否則一旦搬遷過去,容易把新分片的記憶體打爆。

計算完每個slot記憶體後,按照算法配置設定,決定好目标分片有哪些slot。

先設定目标節點slot 為importing狀态 ,再設定源節點的slot為 migrating狀态。

這裡存在一個小坑,在正常開發中這兩個設定大家感覺順序無關緊要,但是實際上有影響。

如果先設定源節點的slot為migrating,再設定目标節點的slot為importing,那麼在這兩條指令的執行間隙,如果有對應slot的指令打到源節點,而key又恰好不存在,則會重定向到目标節點,由于目标節點此時slot并未來得及設定為importing, 又會把這條指令重定向給源節點,這樣就無限重定向了。

好在常見的用戶端(比如jedis)對重定向次數是有限制的, 一旦打到上限,就會抛出錯誤。

(1)準備

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

(2)搬遷

設定完了這一步,下一步就是搬遷。從源節點來擷取slot的搬遷,從源程序慢慢逐個搬遷到目标節點。

此操作是同步的,什麼意思呢?在migrate指令結束之前程序不能直接處理客戶請求,實際上是源端臨時建立一個socket,連接配接目标節點,同步執行指令,确認執行成功了後,把本地的Key删掉,這個時候源端才可以繼續處理客戶新的請求。

在搬遷過程中,整個叢集仍然是可以處理請求的。這一塊開源Redis有考慮,如果這個時候有Key讀請求,剛好這個slot發到源程序,程序可以判斷,如果這個Key在本程序有資料,就會當正常的請求傳回給它。

那如果不存在怎麼辦?就會發一個ASK給客戶,使用者收到ASK知道這個資料不在這個程序上,馬上重新發一個ASKING到目标節點,緊接着把指令發到那邊去。

這樣會有一個什麼好處呢?源端的Key的slot隻會慢慢減少,不會增加,因為新增加的都發到目标節點去了。随着搬遷的持續,源端的Key會越來越少,目标端的key逐漸增加,使用者感覺不到錯誤,隻是多了一次轉發延遲,隻有零點零幾毫秒,沒有特别明顯的感覺。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

(3)切換

方案到什麼時候切換呢?就是slot源程序發現這個slot已經不存在資料了,說明所有資料全部搬到目标程序去了。

這個時候怎麼辦呢?先發送set slot給目标,然後給源節點發送set slot指令,最後給叢集所有其他節點發送set slot。這是為了更快速把路由更新過去,避免通過自身叢集版協定推廣,降低速度。

這裡跟設定遷移前的準備步驟是一樣,也有一個小坑需要注意。

如果先給源節點設定slot,源節點認為這個slot歸屬目标節點,會給客戶傳回move,這個是因為源節點認為Key永久歸屬目标程序,用戶端搜到move後,就不會發ASKing給目标,目标如果沒有收到ASK,就會把這個消息重新傳回源程序,這樣就和打乒乓球一樣,來來回回無限重複,這樣客戶就會感覺到這裡有錯誤。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

三、 無損擴容挑戰

1. 大Key問題

像這樣遷移其實也沒有問題,客戶也不會感覺到正常的通路請求的問題。但是依然會面臨一些挑戰,第一就是大Key的問題。

前文提到的搬遷内部,由于這個搬遷是同步的搬遷,同步搬遷會卡住,這個卡住時間由什麼決定的?

主要不是網速,而是搬遷Key的大小來決定的,由于搬遷是按照key來進行的,一個list也是一個Key,一個哈希表也是Key,一個list會有上千萬的資料,一個哈希表也會有很多的資料。

同步搬遷容易卡非常久,同步搬遷100兆,打包有一兩秒的情況,客戶會覺得卡頓一兩秒,所有通路都會逾時,一般Redis業務設定逾時大部分是200毫秒,有的是100毫秒。如果同步搬移Key超過一秒,就會有大量的逾時出現,客戶業務就會慢。

如果這個卡時超過15秒,這個時間包括搬遷打包時間、網絡傳輸時間、還有loading時間。超過15秒,甚至自動觸發切換,把Master判死,Redis會重新選擇新的Master,由于migrating狀态是不會同步給slave的,是以slave切換成master後,它身上是沒有migrating狀态的。

然後,正在搬遷的目标節點會收到新的master節點對這個slot的所有權聲明, 由于這個slot是importing的,是以它會拒絕承認新master擁有這個slot。進而在這個節點看來,slot的覆寫是不全面的, 有的slot無節點提供服務,叢集狀态為fail。

一旦出現這種情況,假如客戶繼續在寫,由于沒有migrating标記了,新Key會寫到源節點上,這個key可能在目标節點已經有了,就算人工處理,也會出現哪一邊的資料比較新, 應該用哪一邊的資料, 這樣的一些問題,會影響到使用者的可用性和可靠性。

這是整個開源Redis的核心問題,就是容易卡住,不提供服務,甚至影響資料安全。

開源版如何解決這個問題呢?老規矩:惹不起就躲,如果這個slot有最大Key超過100M或者200M的門檻值不搬這個slot。

這個門檻值很難設定,由于migrate指令一次遷移很多個key,過小的門檻值會導緻大部分slot遷移不了,過大的門檻值還是會導緻卡死,是以無論如何對客戶影響都非常大,而且這個影響是不能被預知的,因為這個Key大小可以從幾k到幾十兆,不知道什麼時候搬遷到大key就會有影響,隻要搬遷未結束,客戶在相當長時間都心驚膽戰。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

2. Lua問題

除了Key的整體搬遷有這樣問題以外,我們還會有一個問題就是Lua。

假如業務啟動的時候通過script load加載代碼,執行的時候使用evalsha來,擴容是新加了一個程序,對于業務是透明,是以按照Redis開源版的辦法搬遷Key,key搬遷到目标節點了,但是lua代碼沒有,隻要在新節點上執行evalsha,就會出錯。

這個原因挺簡單,就是Key搬遷不會遷移代碼,而且Redis沒有指令可以把lua代碼搬遷到另外一個程序(除了主從同步)。

這個問題在開源版是無解,最後業務怎麼做才能夠解決這個問題呢?

需要業務那邊改一下代碼,如果發現evalsha執行出現代碼不存在的錯誤,業務要主動執行一個script load,這樣可以規避這個問題。

但是對很多業務是不能接受的。因為要面臨一個重新加代碼然後再釋出這樣一個流程,業務受損時間是非常長的。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

3. 多Key指令/Slave讀取

還有一個挑戰,就是多Key指令,這個是比較嚴重的問題,Mget和mset其中一個Key在源程序,另外一個Key根本不存在或者在目标程序,這個時候會直接報錯,很多業務嚴重依賴于mget的準确性,這個時候業務是不能正常工作的。

這也是原生版redis沒有辦法解決的問題,因為隻要是Key搬遷,很容易出現mget的一部分key在源端,一部分在目标端。

除了這個問題還有另一個問題,這個問題跟本身分片擴容無關,但是開源版本存在一個bug,就是我們這邊Redis是提供了一個讀寫分離的功能,在Proxy提供這個功能,把所有的指令打到slave,這樣可以降低master的性能壓力。

這個業務用得很友善,業務想起來就可以開啟,發現不行就可以馬上關閉。

這裡比較明顯的問題是:當每個分片資料比較大的時候,舉一個例子20G、30G的資料量的時候,我們剛開始挂slave,slave身份推廣跟主從資料是兩個機制,可能slave已經被叢集認可了,但是還在等master的資料,因為20G資料的打包需要幾分鐘(和具體資料格式有關系)。

這個時候如果客戶的讀指令來到這個slave, 會出現讀不到資料傳回錯誤, 如果用戶端請求來到的時候rdb已經傳到slave了,slave正在loading, 這個時候會給用戶端回loading錯誤。

這兩個錯誤都是不能接受,客戶會有明顯的感覺,擴容副本本來為了提升性能,但是結果一擴反而持續幾分種到十幾分鐘内出現很多業務的錯誤。

這個問題其實是跟Redis的基本機制:身份推廣機制、主從資料同步機制有關,因為這兩個機制是完全獨立的,沒有多少關系,問題的解決也需要我們修改這個狀态來解決,下文會詳細展開。

最後一點就是擴容速度。前文說過,Redis通過搬Key的方式對業務是有影響的,由于同步操作,速度會比較慢,業務會感受到明顯的延時,這樣的延時業務肯定希望越快結束越好。

但是我們是搬遷Key,嚴重依賴Key的速度。因為搬Key不能全速搬,Redis是單線程,基本上線是8萬到10萬之間,如果搬太快,就占據使用者CPU。

使用者本來因為同步搬遷卡頓導緻變慢,搬遷又要占他CPU,導緻雪上加霜,是以一般這種方案是不可能做特别快的搬遷。比如說每次搬一萬Key,相當于占到12.5%,甚至更糟,這對于使用者來說是非常難以接受的。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

四、行業其他方案

既然開源版有這麼多問題,為什麼不改呢?不改的原因這個問題比較多。可能改起來不容易,也确實不太容易。

關于搬遷分片擴容是Redis的難點,很多人回報過,但是目前而言沒有得到作者的回報,也沒有一個明顯的解決的趨勢,行業内最常見就是DTS方案。

DTS方案可以通過下圖來了解,首先通過DTS建立同步,DTS同步跟Redis-port是類似,會僞裝一個slave,通過sync或者是psync指令從源端slave發起一次全量同步,全量之後再增量,DTS接到這個資料把rdb翻譯成指令再寫入目标端的執行個體上,這樣就不要求目标和源執行個體的分片數目一緻,dts在中間把這個活給幹了。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

等到DTS完全遷移穩定之後,就可以一直同步增量資料,不停從源端push目标端,這時候可以考慮切換。

切換首先觀察是不是所有DTS延遲都在門檻值内,這個延遲指的是從這邊Master到那邊Master的中間延遲。如果小于一定的資料量,就可以斷連用戶端,等待一定時間,等目标執行個體完全追上來了,再把LB指向新執行個體,再把源執行個體删除了。一次擴容就完全實作了,這是行業比較常見的一種方案。

DTS方案解決什麼問題呢?大Key問題得到了解決。因為DTS是通過源程序slave的一個程序同步的。

Lua問題有沒有解決?這個問題也解決了,DTS收到RDB的時候就有lua資訊了,可以翻譯成script load指令。多Key指令也得到了解決,正常使用者通路不受影響,在切換之前對使用者來說無感覺。

遷移速度也能夠得到比較好的改善。遷移速度本身是因為原執行個體通過rdb翻譯,翻譯之後并發寫入目标執行個體,這樣速度可以很快,可以全速寫。

這個速度一定比開源版key搬遷更快,因為目标執行個體在切換前不對外工作,可以全速寫入,遷移速度也是得到保證。

遷移中的HA和可用性和可靠性也都還可以。當然中間可用性要斷連30秒到1分鐘,這個時間使用者不可用,非常小的時間影響使用者的可用性。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

DTS有沒有缺點?有!首先是其複雜度,這個遷移方案依賴于DTS元件,需要外部元件才能實作,這個元件比較複雜,容易出錯。

其次是可用性,前文提到步驟裡面有一個踢掉用戶端的情況,30秒到1分鐘這是一般的經驗可用性影響,完全不可通路。

還有成本問題,遷移過程中需要保證全量的2份資源,這個資源量保證在遷移量比較大的情況下,是非常大的。

如果所有的客戶同時擴容1分片,需要整個倉庫2倍的資源, 否則很多客戶會失敗,這個問題很緻命,意味着我要理論上要空置一半的資源來保證擴容的成功, 對雲服務商來說是不可接受的,基于以上原因我們最後沒有采用DTS方案。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

五、騰訊雲Redis擴容方案

我們采用方案是這樣的,我們的目标是首先不依賴第三方元件,通過指令行也可以遷。第二是我們資源不要像DTS那樣遷移前和遷移後兩份資源都要保留,這個對于我們有相當大的壓力。最後用的是通過slot搬遷的方案。具體步驟如下:

首先還是計算各slot記憶體大小,需要計算具體搬遷多少slot。配置設定完slot之後,還要計算可配置設定到目标節點的slot。

跟開源版不一樣,不需要設定源程序的migrating狀态,源程序設定migrating是希望新Key自動寫入到目标程序,但是我們這個方案是不需要這樣做。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

再就是在目标程序發起slot指令,這個指令執行後,目标節點根據slot區間自動找到程序,然後對它發起sync指令(帶slot的sync),源程序收到這個sync指令,執行一個fork,将所有同步的slot區間所有的資料生成rdb,同步給目标程序。

每一個slot有哪一些Key在源程序是有記錄的,這裡周遊将每一個slot的key生成rdb傳輸給目标程序,目标程序接受rdb開始loading,然後接受aof,這個aof也是接受跟slot相關的區間資料,源程序也不會把不屬于這個slot的資料給目标程序。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

一個目标程序可以從一兩個源點建立這樣的連接配接,一旦全部建立連接配接,并且同步狀态正常後,當offset足夠小的時候,就可以發起failover操作。

和Redis官方主動failover機制一樣。在failover之前,目标節點是不提供服務的,這個和開源版有巨大的差别。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

通過這個方案,大Key問題得到了解決。因為我們是通過fork程序解決的,而不是源節點搬遷key。切換前不對外提供服務,是以loading一兩分鐘沒有關系,客戶感覺不到這個節點在loading。

還有就是Lua問題也解決了,新節點接受的是rdb資料,rdb包含了Lua資訊在裡面。還有多Key指令也是一樣,因為我們完全不影響客戶正常通路,多Key的指令以前怎麼通路現在還是怎麼通路。遷移速度因為是批量slot打包成rdb方式,一定比單個Key傳輸速度快很多。

關于HA的影響,遷移中有一個節點挂了會不會有影響?開源版會有影響,如果migrating節點挂了叢集會有一個節點是不能夠對外提供服務。

但我們的方案不存在這個問題,切換完了依然可以提供服務。因為我們本來目标節點在切換之前就是不提供服務的。

還有可用性問題,我們方案不用斷用戶端連接配接,用戶端從頭到尾沒有受到任何影響,隻是切換瞬間有小影響,毫秒級的影響。

成本問題有沒有解決?這個也得到解決,因為擴容過程中,隻建立最終需要的節點,不會建立中間節點,零損耗。

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫  

六、Q&A

Q:Cluster數量會改變槽位的數量嗎?

A:不會改變槽位數量,一直是16814,這個跟開源版是一緻的。

Q:遷移之前怎麼評估新slot接觸資料不會溢出?

A:根據前文所述,我們有一個準備階段,計算所有slot各自記憶體大小,怎麼計算我們會在slave直接執行一次掃描計算,基本上能夠計算比較準确。這裡沒有用前一天的備份資料,而是采用slave實時計算,CPU裡有相應控制,這樣可以計算出slot總量大小。我們會預定提前量(1.3倍),使用者還在寫,保證目标不會遷移中被寫爆,萬一寫爆了也隻是流程失敗,使用者不會受到影響。

Q:對于 redis 擴容操作會發生的問題,你們有采用什麼備用方案,和緊急措施嗎?

A:新的擴容方案,如果出現問題,不會影響客戶的使用,隻會存在多餘的資源,目前這塊依賴工具來處理。資料的安全性本身除了主從,還依賴每日備份來保證。

Q:請問老師,高峰期需要擴容,但總有回歸正常請求量的時候,此時的擴容顯得有些備援,怎麼樣讓Redis叢集能夠既能夠快速回收多餘容量,同時又能友善下一次高峰請求的再次擴容呢?

A:可能你想了解的是serverless,按需付費自動擴充的模式,目前的資料庫大多是提供的PAAS服務,PASS層的資料庫将過去的資源手動管理變成了半自動化,擴縮容還是需要運維參與。Serverless形式的服務會是下一步的形态,但是就算是serverless可能也會面臨着需要資源手動擴充的問題,特别是對超大規模的運算服務。

Q:slot來源于多個分片,同時和多個節點同步進行嗎?

A:是,确實會這樣做的。

Q:slot産生過程中産生新資料怎麼同步?

A:類似aof概念的機制同步到目标程序。這個aof跟普通aof傳輸到slave有差別,隻會将跟目标slot相關的資料同步過去,而不會同步别的。

Q:還在招人嗎?

A:騰訊雲Redis還在招人,歡迎大家投履歷,履歷請投至郵箱:[email protected]。大家還可以選擇在官網上投遞履歷,或者搜尋關注騰訊雲資料庫的公衆号,檢視21日的推送文章,我們貼上了招聘職位的連結,也可以@社群小助手,小助手會收集上來發到我這邊。

講師簡介

伍旭飛

騰訊雲進階工程師,騰訊雲Redis技術負責人

伍旭飛,騰訊雲進階工程師,騰訊雲Redis技術負責人,有多年和遊戲和資料庫開發應用實踐經驗, 聚焦于遊戲開發和NOSQL資料庫在各個領域的應用實踐。

推薦閱讀

騰訊會議空中加油級的擴容,隻需按一個按鈕

特惠體驗雲資料庫  

騰訊會議使用者暴漲,Redis叢集如何實作無縫擴容?一、疫情帶來的挑戰二、開源Redis擴容方案三、 無損擴容挑戰四、行業其他方案六、Q&A講師簡介推薦閱讀特惠體驗雲資料庫