五一搶票,靠的可不僅是運氣哦!點擊查看,為你揭開背后的關鍵技術~~
摘要:每逢節假日,當全國幾百萬小伙伴同時查票、訂票時,12306是如何保證余票顯示准、車票不超賣的?為你揭開背后的關鍵技術:數據強一致性。
眼瞅着五一小長假就要來了,小雲還在為沒有搶到高鐵票心急如焚。
她再次打開APP,一遍遍刷新,發現還有余票2張,趕緊下單,一頓操作猛如虎,結果還是沒搶到。
但仔細想想也能理解:從勾選乘車人到正式下單,整個流程至少需要10秒,如果“見者有份”,恐怕這兩個座位大家要擠擠共用了。
那么,每逢節假日,當全國幾百萬小伙伴同時查票、訂票時,12306是如何保證余票顯示准、車票不超賣的?
於是,按捺不住好奇心的我們,進行了一番深入研究。原來問題背后隱藏着一個分布式數據庫領域極其關鍵的技術——數據強一致性保障。
1. 什么是強一致?
在介紹概念之前,我們不妨先來模擬一場球賽直播。
假設筆者做了一款APP,后台使用上圖的主從數據庫。比分寫入主節點,從節點分擔用戶查詢。比賽中,Alice驚呼比賽結束,Bob聞聲刷新APP,卻顯示比賽仍在繼續!Bob體驗到了明顯的數據不一致,於是默默給APP打了個差評……
那么,產生不一致的原因究竟是什么?
異步復制時,主節點不等待從節點寫入就直接返回了。由於網絡延遲等原因,從節點無法保證更新時間。Alice和Bob明明在同時同地查詢同一系統,得到正確結果卻有先有后。其實這就是典型的弱一致性。
實際上,為解決單點故障、增強吞吐性能,分布式數據庫內部都會對同一份數據進行復制,把冗余副本分散保存到不同節點上。簡單的異步復制只能構建出弱一致系統,很難滿足業務要求。
那么,究竟什么樣的一致性才靠譜?有哪些類別?下面我們就來認識這個神秘家族!
1.1 強一致性/線性一致性(Linearizability)
強一致性/線性一致性是一致性的最高標准,實現難度最高。核心要求是:一旦寫操作完成,隨后任意客戶端的查詢都必須返回這一新值。以下圖為例,一旦“寫入b”完成,必須保證讀到b。而寫入過程中,認為值的跳變可能發生在某一瞬間,因此讀到a或b都是可能的。
從業務角度來說,強一致性帶來的體驗簡直可以用絲滑來形容!因為它內部的數據“仿佛”只有一份,即使並發訪問不同節點,每個操作也都能原子有序。正因如此,強一致數據庫在業務架構中往往被用在關鍵位置。
etcd是強一致俱樂部里的元老。它基於Raft共識算法,真正實現了強一致,也因此在Leader選舉、服務發現等場景起到重要作用。GaussDB(for Redis)作為一款分布式雲數據庫,憑借多年潛心打磨,也是強一致的代言人。
1.2 順序一致性(Sequential Consistency)
順序一致性弱於線性一致,不保證操作的全局時序,但保證每個客戶端操作能按順序被執行。下圖中,A先寫x=10,后寫x=20;B先寫x=99,后寫x=999。當C讀取時,順序一致性保證了10先於20被讀到、99先於999被讀到。
Zookeeper基於ZAB協議,所有寫操作都經由主節點協調,實現了順序一致性。
1.3 因果一致性(Causal Consistency)
進一步放寬要求,因果一致性只對並發訪問中具有因果關系的操作保序。例如:
A寫入3,B讀到后乘以100再更新它。在這個場景下,由於“A寫入3”與“B寫入300”有着明確因果關系,因果一致性保證300晚於3被讀到。
因果一致性多用於各種博客的評論系統、社交軟件等。畢竟,我們回復某條評論的內容,不應早於評論本身被顯示出來。
1.4 最終一致性(Eventual Consistency)
它指的是停止寫入並等待一段時間,最終所有客戶端都能讀到相同的新數據,但具體時限不作保證。許多分布式數據庫都滿足最終一致性,如MySQL主從集群等。
然而,這其實是一個非常弱的保證。由於不確定系統內部過多久才能收斂一致,在此之前,用戶隨時可能體驗到數據不一致。因此最終一致性有天然的局限性,經常會給業務邏輯帶來混亂。
1.5 弱一致性(Weak Consistency)
說弱一致性最為“厚臉皮”也不為過,因為它連數據寫入后將來被讀到都不能保證。弱一致性實現技術門檻低,應用場景也不多。嚴格來說,單純的開源Redis主從集群就屬於這一類別。
OK,一致性家族的各位成員已經跟大家打過照面。顯然,一致性越強的數據庫系統,能夠支撐的業務場景越多。有的業務同學小聲說,強一致技術再牛,可我業務簡單,不用也沒關系吧。實際上恰恰相反:
強一致不僅僅是技術問題,它更是一個不可忽視的業務需求、運維需求!
接下來我們就先來聊一聊:業務上那些只有強一致才能搞定的事兒!
2 強一致是業務剛需
2.1 計數器/限流器
計數服務是典型的強一致應用場景。電商在秒殺活動中,往往會搭建Redis主從集群給下層MySQL做緩存。因為要抗住超大流量,需要Redis的計數器功能做限流。簡單講,我們初始化counter=5000。隨后每次業務訪問都執行DECR命令,當counter歸零就阻塞后續請求。此外,每隔一個時間段重置counter=5000,通過這樣的手段來實現“細水長流”。
然而,完美的假設還不夠!
開源Redis采用異步復制,如遇網絡不暢,經常發生主節點復制buffer堆積。這將導致從節點counter偏大很多。此時,一旦主節點宕機,切換到從節點繼續執行DECR命令,壓力很容易超出閾值,全部落到下層脆弱的MySQL,隨時可能引起系統雪崩!
因此,在限流場景下,只有真正的強一致才能提供可靠的計數器。
2.2 Leader選舉
當業務部署的節點較多、可用性要求高時,往往要用到Leader選舉。etcd作為強一致KV存儲,能完美cover這一場景。etcd依賴兩大功能實現Leader選舉:
(1)TTL:給key設置有效期,到期后key自動刪除。
(2)CAS:對key的原子操作 (這一功能只有強一致數據庫才能實現) ,使用etcd搭建Leader選舉服務的設計如下:
1)約定key,用於選舉時搶占。其value用於保存Leader節點名稱。
2)約定TTL,用於給key設定有效期。
3)啟動時:每個參與節點嘗試cas create key&設置TTL。在etcd集群強一致CAS機制保障下,只有一個節點能執行成功。該節點成為Leader並將名稱寫入value;其余節點成為Follower。
4)運行中:每個節點定期TTL/2嘗試get key,將value與自身名稱對比:
如相同,說明已是Leader,此后只需每隔TTL/2刷新key的TTL即可。
如不同,說明是Follower,接下來要每隔TTL/2執行cas create key&設置TTL。
5)當Leader節點異常退出,無法刷新TTL,key會很快過期。此時,其余Follow之中便會有新的Leader產生。
從原理上能看出,強一致能力是Leader選舉的根基。類似的“剛需”業務場景還有很多,強一致不可或缺。
好了,業務上的事兒就聊到這里,接下來讓我們聽聽運維怎么說。
3. 強一致為運維減負
3.1 輔助組件架構復雜、問題難定位
后台架構中,MySQL主從熱備也是常見的部署方式。由於數據保存在本地磁盤中,當主庫發生嚴重故障,僅僅依靠MySQL自身同步機制,主從切換后無法保證所提供數據與之前狀態完全一致。於是出現了“重量級”的輔助組件——MHA(Master High Availability)。我們來看一下它的部署方式:
MHA負責在故障轉移過程中,幫助從庫盡量追平主庫最新狀態,提供近似一致的數據。但這一能力需要額外的Manager節點,同時還要在每一個MySQL節點上部署Node服務。故障切換時,Manager先為從庫補充落后的數據,再通過切換VIP恢復用戶訪問,過程可能長達數十秒。
這樣的HA系統部署和后期維護都很復雜。如未能順利執行故障切換或發生數據丟失,運維面臨的場面都將很棘手。其實運維同學何嘗不希望手中的系統穩定運行呢?要是數據庫自身能提供強一致保障,何苦再依賴復雜的輔助組件!
讀到這里,對強一致的看法,相信各位讀者心里已經有了自己的一桿秤。讓我們再一次划重點:
強一致不僅僅是技術問題,它更是一個不可忽視的業務需求、運維需求!
從產品選型角度出發,開源Redis提供的一致性保證很弱。而etcd雖有強一致能力,但它單點寫入性能不足,也未能提供hash、sorted set、stream等誘人的數據結構……
此時,有人會陷入糾結,到底選擇哪一種,GaussDB(for Redis)應聲而起——我,都可以。
4. GaussDB(for Redis)與強一致
自設計之初,GaussDB(for Redis)(后文簡稱高斯Redis)給自己的定位就是“強一致KV數據庫”,因此徹底摒棄了開源Redis的異步復制機制。借助華為雲GaussDB系列先進的“存算分離”架構,將全量數據下沉到強一致存儲層(DFV Pool),從核心技術上超越了傳統開源產品的極限。
讓我們來一起認識一下高斯Redis的強悍:
用戶購買的實例作為一個整體,提供強一致KV存儲。
用戶業務統一通過Proxy集群接入高斯Redis,不用考慮內部復雜邏輯。多點並發訪問實例,讀寫操作滿足強一致性,再也不必擔心開源Redis異步復制的不一致隱患。
計算層智能處理數據分片、動態故障轉移,將數據全量下沉到共享存儲池。
cfgsvr集群統一管理ShardServer節點,自動對海量數據進行分片。並能夠在故障場景實現秒級接管,嚴格防止任何中間態下的數據不一致。
存儲層通過RDMA高速網絡實現高性能分布式數據持久化,三副本冗余保證強一致、零丟失。
DFV Pool是強一致、高性能的分布式存儲系統。這是華為內部自研的公司級Data Lake,它能夠穩定支撐各類全棧數據服務。高斯Redis突破了開源Redis“小格局”的內存架構,將數據全量下沉,基於DFV Pool強大的一致性保障能力,給用戶業務帶來更廣闊的拓展空間。
5. 結語
試想,當處在關鍵位置的數據庫“不給力”,業務層就要忙於為系統添加復雜、易出錯的一致性保障邏輯。與此同時,運維還要時刻擔心故障引發的數據落后問題…這樣的系統真的“香”嗎?
試想如果12306顯示你搶到票了,上車的時候卻發現有四五個人和你是同一個座位,太詭異了。
所以,專業的事情交給專業的團隊來做。GaussDB(for Redis)自研發初期就持續關注數據強一致性設計,借助強一致存儲池DFV Pool,它可以提供真正強一致的海量KV存儲解決方案。