歡迎關注我的公衆號:石杉的架構筆記(ID:shishan100)面試
週一至週五早8點半!精品技術文章準時送上!算法
上篇文章《大型系統架構演進之如何設計高容錯分佈式計算系統》,主要聊了一下將單塊系統重構爲分佈式系統,以此來避免單臺機器的負載太高。同時引伸出來了彈性資源調度、分佈式容錯機制等相關的東西。數據庫
這篇文章咱們繼續來聊聊這個系統後續的重構演進過程,先來看下目前的系統架構圖,一塊兒來回顧一下。緩存
上篇文章說到,若是僅僅只是天天億級流量的話,其實基本上目前的系統架構就足夠支撐了,可是呢,咱們面臨的可不只僅是億級流量那麼簡單。咱們面對的是日益增多和複雜的各類業務系統,咱們面對的是不斷增長的系統用戶,咱們面對的是即將迎來天天百億級的高併發流量。性能優化
給你們先說下當時的系統部署狀況,數據庫那塊一共部署了8主8從,也就是16臺數據庫服務器,每一個庫都是部署在獨立的數據庫服務器上的,並且所有用的是物理機,機器的配置,若是沒記錯的話,應該是32核+128G+SSD固態硬盤。服務器
爲啥要搞這麼多物理機,並且所有都是高配置呢?不知道你們發現沒有,目前爲止,咱們最大的依賴就是MySQL!網絡
以前給你們解釋過,在當時的背景下,咱們要對涌入的億級海量數據,實時的運行數百個複雜度爲幾百行到上千行的大SQL,幾秒鐘就要出分析結果。架構
這個是沒有任何一個開源系統能夠作到的,Storm不行,Spark Streaming也不行,所以必須得基於MySQL純自研一套數據平臺架構出來,支撐這個需求場景。併發
因此,只有MySQL是能夠支撐如此複雜的SQL語句完美運行的,所以咱們在早期必須嚴重依賴於MySQL做爲數據的存儲和計算,將源源不斷涌入的數據放在MySQL中進行存儲,接着基於數據分片計算的架構來高性能的運行復雜大SQL基於MySQL來進行計算。運維
因此你們就知道了,MySQL目前爲止是這套系統的命脈。在當時的場景下,每臺數據庫服務器都要抗住每秒2000左右的併發請求,高峯期的CPU負載、IO負載其實都很是高,並且主庫和從庫的延遲在高峯期已經有點嚴重,會達到秒級了。
在咱們的生產系統的實際線上運行狀況下,單臺MySQL數據庫服務器,咱們通常是不會讓他的高峯期併發請求超過2000/s的,由於一旦達到每秒幾千的請求,根據當時線上的資源負載狀況來看,極可能MySQL服務器負載太高會宕機。
因此此時就有一個很尷尬的問題了,假如說天天億級流量的場景下,須要用8主8從這麼多高配置的數據庫服務器來抗,那若是是幾十億流量呢?甚至若是是百億流量呢?難道不停的增長更多的高配置機器嗎?
要知道,這種高配置的數據庫服務器,若是是物理機的話,是很是昂貴的!
以前給你們簡單介紹過項目背景,這整套大型系統組成的商業級平臺,涉及到N多個系統,這個數據產品只是一個子產品而已,不可能爲了這麼一個產品,投入大量的預算經過不停的砸高配置的機器來撐住更高的併發寫入。
咱們必須用技術的手段來重構系統架構,儘可能用有限的機器資源,經過最優秀的架構來抗住超高的併發寫入壓力!
這個架構裏的致命問題之一,就是數據的存儲和計算混在了一個地方,都在同一個MySQL庫裏!
你們想一想,在一個單表裏放上千萬數據,而後你每次運行一個複雜SQL的時候,SQL裏都是經過索引定位到表中他要計算的那個數據分片。這樣搞合適嗎?
答案顯然是否認的!由於表裏的數據量很大,可是你每次實際SQL運算只要對其中很小很小的一部分數據計算就能夠了,實際上咱們在生產環境中實踐事後發現,若是你在一個大表運行一個複雜SQL,哪怕經過各類索引保證定位到的數據量不多,由於表數據量過大,也是會致使性能直線降低的。
所以第一件事情,先將數據的存儲和計算這兩件事情拆開。
咱們當時的思路以下:
bingo!一旦將數據存儲和計算兩個事情拆開,架構裏能夠發揮的空間就大多了。
首先你的數據存儲只要支撐高併發的寫入,日百億流量的話,高峯每秒併發會達到幾十萬,撐住這就能夠了。而後支持計算引擎經過簡單的操做從數據存儲裏提取少許數據就OK。
太好了,這個數據存儲就能夠PASS掉MySQL了,就這點兒需求,你還用MySQL幹什麼?兄弟!
當時咱們通過充分的技術調研和選型以後,選擇了公司自研的分佈式KV存儲系統,這套KV存儲系統是徹底分佈式的,高可用,高性能,輕量級,支持海量數據,並且以前經歷過公司線上流量的百億級請求量的考驗,絕對沒問題。主要支持高併發的寫入數據以及簡單的查詢操做,徹底符合咱們的需求。
這裏給你們提一句,其實業內不少相似場景會選擇hbase,因此你們若是沒有公司自研的優秀kv存儲的話,能夠用選用hbase也是沒問題的,只不過hbase有可能生產環境會有點坑,須要你們對hbase很是精通,合理避坑和優化。
輕量級的分佈式kv系統,通常設計理念都是支持一些簡單的kv操做,大量的依託於內存緩存熱數據來支持高併發的寫入和讀取,由於不須要支持MySQL裏的那些事務啊、複雜SQL啊之類的重量級的機制。
所以在同等的機器資源條件下,kv存儲對高併發的支撐能力至少是MySQL的數倍甚至數十倍。
就比如說,你們應該都用過Redis,Redis普通配置的單機器撐個每秒幾萬併發都是ok的,其實就是這個道理,他很是的輕量級,轉爲高併發而生。
而後,咱們仍是能夠基於MySQL中的一些臨時表來存放kv存儲中提取出來的數據分片,利用MySQL對複雜SQL語法的支持來進行計算就能夠了。也就是說,咱們在這個架構裏,把kv系統做爲存儲,把MySQL用作少許數據的計算。
此時咱們在系統架構中引入了分佈式kv系統來做爲咱們的數據存儲,天天的海量數據都存放在這裏就能夠了,而後咱們的Slave計算引擎每次計算,都是根據那個數據分片從kv存儲中提取對應的數據出來放入MySQL內的一個臨時表,接着就是對那個臨時表內的一兩千條數據分片運行各類複雜SQL進行計算便可。
你們看上面的圖,此時經過這一步計算與存儲架構的分離,咱們選用了適合支撐高併發的kv集羣來抗住天天百億級的流量寫入。而後基於MySQL做爲臨時表放入少許數據來進行運算。這一個步驟就直接把高併發請求能夠妥妥的抗住了。
並且分佈式kv存儲原本就能夠按需擴容,若是併發愈來愈高,只要擴容增長機器就能夠了。此時,就完成了架構的一個關鍵的重構步驟。
下一步,咱們就要對架構追求極致!由於此時咱們面臨的一個痛點就在於說,其實僅僅只是將MySQL做爲一個臨時表來計算了,主要就是用他的複雜SQL語法的支持。
可是問題是,對MySQL的併發量雖然大幅度下降了,但是還並不算過低。由於大量的數據分片要計算,仍是須要頻繁的讀寫MySQL。
此外,每次從kv存儲裏提取出來了數據,還得放到MySQL的臨時表裏,還得發送SQL去MySQL裏運算,這仍是多了幾個步驟的時間開銷。
由於當時面臨的另一個問題是,天天請求量大,意味着數據量大,數據量大意味着時間分片的計算任務負載仍是較重。
老是這麼依賴MySQL,還要額外維護一大堆的各類臨時表,可能多達幾百個臨時表,你要維護,要注意他的表結構的修改,還有分庫分表的一些運維操做,這一切都讓依賴MySQL這個事兒顯得那麼的多餘和麻煩。
所以,咱們作出決定,爲了讓架構的維護性更高,並且將性能優化到極致,咱們要本身研發純內存的SQL計算引擎。
其實若是你要自研一個能夠支持MySQL那麼複雜SQL語法的內存SQL計算引擎,仍是有點難度和麻煩的。可是在咱們仔細研究了業務須要的那幾百個SQL以後,發現其實問題沒那麼的複雜。
由於其實通常的數據分析類的SQL,主要就是一些常見的功能,沒有那麼多的怪、難、偏的SQL語法。
所以咱們將線上的SQL都分析過一遍以後,就針對性的研發出了僅僅支持特定少數語法的SQL引擎,包括了嵌套查詢組件、多表關聯組件、分組聚合組件、多字段排序組件、少數幾個經常使用函數,等等。
接着就將系統完全重構爲再也不依賴MySQL,每次從kv存儲中提取一個數據分片以後,直接放入內存中,而後用咱們自研的SQL計算引擎來在純內存裏針對一個數據分片執行各類複雜的SQL。
這個純內存操做的性能,那就不用多說了,你們應該都能想象到了,基本上純內存的SQL執行,都是毫秒級的,基本上一個時間分片的運算所有下降到毫秒級了。性能進一步獲得了大幅度的提高,並且今後再也不依賴MySQL了,不須要維護複雜的分庫分表等等東西。
這套架構上線以後,完全消除了對MySQL的依賴,理論上,不管多大的流量過來,均可以經過立馬擴容kv集羣以及擴容Slave計算集羣來解決,不須要依賴MySQL的分庫分表、幾百張臨時表等比較耗費人力、麻煩並且坑爹的方案了。並且這種純內存的計算架構直接把計算性能提高到了毫秒級。
並且消除對MySQL的依賴有另一個好處,數據庫的機器老是要高配置的,可是Slave機器主要4核8G的普通虛擬機就夠了,分佈式系統的本質就是儘可能利用大量的廉價普通機器就能夠完成高效的存儲和計算。
所以在百億流量的負載之下,咱們Slave機器部署了幾十臺機器就足夠了,那總比你部署幾十臺昂貴的高配置MySQL物理機來的划算多了!
其實若是對高併發架構稍微瞭解點的同窗都會發現,這個系統的架構中,針對高併發的寫入這塊,還有一個比較關鍵的組件要加入,就是MQ。
由於咱們若是應對的是高併發的非實時響應的寫入請求的話,徹底可使用MQ中間件先抗住海量的請求,接着作一箇中間的流量分發系統,將流量異步轉發到kv存儲中去,同時這個流量分發系統能夠對高併發流量進行控制。
好比說若是瞬時高併發的寫入真的致使後臺系統壓力過大,那麼就能夠由流量分發系統自動根據咱們設定的閾值進行流量控制,避免高併發的壓力打垮後臺系統。
並且在這個流控系統中,咱們其實還作了不少的細節性的優化,好比說數據校驗、過濾無效數據、切分數據分片、數據同步的冪等機制、100%保證數據落地到kv集羣的機制保障,等等。
公司的MQ集羣自然都支撐過大流量寫入以及高併發請求,所以MQ集羣那個層面抗住高併發並非什麼問題,再高的併發按需擴容就能夠了,而後咱們本身的流控系統也是集羣部署的,線上採用的是4核8G的虛擬機,由於這個機器不須要過高的配置。
流控系統,基本線上咱們通常保持在每臺機器承載每秒小三千左右的併發請求,百億流量場景下,高峯每秒併發在每秒小几十萬的級別,所以這個流控集羣部署到幾十臺機器就足夠了。
而公司的kv集羣也是自然支撐過大流量高併發寫入的,所以kv集羣按需擴容,抗住高併發帶流量的寫入也不是什麼問題,並且這裏其實咱們由於在自身架構層面作了大量的優化(存儲與計算分離的關鍵點),所以kv集羣的定位基本就是online storage,一個在線存儲罷了。
經過合理、巧妙的設計key以及value的數據類型,使得咱們對kv集羣的讀寫請求都是優化成最最簡單的key-value的讀寫操做,自然保證高併發讀寫是沒問題的。
另外稍微給你們一點點的劇透,後面講到全鏈路99.99%高可用架構的時候,這個流控集羣會發揮巨大的做用,他是承上啓下的一個效果,前置的MQ集羣故障的高可用保障,以及後置的KV集羣故障的高可用保障,都是依靠流控集羣來實現的。
在完成上述重構以後,咱們又對核心的自研內存SQL計算引擎作了進一步的優化。由於實際生產環境運行過程當中,咱們發現了一個問題:就是每次若是Slave節點都是對一個數據分片提取相關聯的各類數據出來而後進行計算,實際上是不必的!
給你們舉個例子,若是你的SQL要對一些表進行關聯計算,裏面涉及到了一些大部分時候靜態不變的數據,那些表的數據通常不多改變,所以不必每次都走網絡請求從kv存儲裏提取那部分數據。
咱們其實徹底能夠在Slave節點對這種靜態數據作個輕量級的cache,而後只有數據分片裏對應的動態改變的數據才從kv存儲來提取數據。
經過這個數據的動靜分離架構,咱們基本上把Slave節點對kv集羣的網絡請求下降到了最少,性能提高到了最高。你們看下面的圖。
這套架構到此爲止,基本上就演進的比較不錯了,由於超高併發寫入、極速高性能計算、按需任意擴容,等各類特性均可以支持到了,基本上從寫入到計算,這兩個步驟,是沒什麼太大的瓶頸了。
並且經過自研內存SQL計算引擎的方案,將咱們的實時計算性能提高到了毫秒級的標準,基本已經達到極致。
下一步,咱們就要看看這個架構中的左側,還有一個MySQL呢!
首先是實時計算鏈路和離線計算鏈路,都會導入大量的計算結果到那個MySQL中。
其次面向數十萬甚至上百萬的B端商家時,若是是實時展現數據分析結果的話,通常頁面上會有定時的JS腳本,每隔幾秒鐘就會發送請求過來加載最新的數據計算結果。
所以實際上那個專門面向終端用戶的MySQL也會承受極大的數據量的壓力,高併發寫入的壓力以及高併發查詢的壓力。
下一篇文章,咱們就聊聊《大型系統架構演進之如何設計每秒數十萬查詢的高併發架構》,將左側最後剩下的那個MySQL給完全重構掉。
億級流量架構專欄:
一大波微服務、分佈式、高併發、高可用的原創系列
文章正在路上,歡迎掃描下方二維碼,持續關注:
石杉的架構筆記(id:shishan100)
十餘年BAT架構經驗傾囊相授