坑:緩存 + 哈希 = 高并發?

當前互聯網時代,怎么少的了 高并發 呢? 高并發 和 高可用 一樣, 已經變成各個系統的標配了,如果你的系統QPS沒有個大幾千上萬,都不好意思跟人打招呼,雖然可能每天的調用量不超過100。

高并發 這個詞,我個人感覺是從電商領域開始往外流傳的,特別是電商領域雙11那種藐視全球的流量,再把技術架構出來分享一把,現在搞得全互聯網都在說 高并發 ,而且你注意回憶一下所有你看到的高并發系統,往往都逃不開一個核心概念,那就是 緩存+哈希 ,一切都是以這個概念和基礎的,仿佛這就是高并發的核心技術了。`

我們看到的高并發技術

圍繞這個核心技術,通常我們看到的各種高并發的架構系統,在博客、論壇、現場分享出來的高并發系統,都跑不出以下幾個方面的東西。

資源靜態化

就是那種單個頁面流量巨大無比,每秒的QPS幾十萬上百萬的系統,確實 并發高的系統 ,核心解決方案就是靜態化,靠機器和帶寬去抗,假如沒有CDN的話,所有流量都落到同一個IP下面的話,基本上也就是用Nginx的文件靜態化了,單機的承受能力主要取決于帶寬和單機的性能,要再多的話,那就 LVS(或者F5)+集群 了,這種的典型場景就是搞活動時候的首頁,活動頁面了,還有就是引流搜索引擎的著陸頁了,一般都是現成的圖片和文字靜態化,當然,這種還有很多前端的技巧和技術了,這一點我不是很了解,就不得瑟了,就中后臺來說,大部分情況下直接Nginx搞定了,核心還是使用了 緩存技術 。

讀寫分離和分庫分表

讀寫分離是大家看到的第二個高并發的架構了,也很常規,因為一般情況下讀比寫要多得多,所以數據庫的主庫寫,從庫們提供讀操作,一下就把數據庫的并發性能提高了。

如果還不夠,那么分庫分表把,把數據分到各個數據庫的各個機器上,進一步的減少單臺機器的壓力,從而達到 高并發 的目的。

如果是分庫分表,有時候使用的就是 哈希技術 了,以某個字段哈希一下然后來分庫分表,讀寫分離的讀操作,基本也是通過 哈希技術 把讀落到不同的機器上去減輕單機壓力。

萬能的緩存

說到高并發,不得不說緩存了,現在各種緩存的工具也很多也很成熟, memcache , redis 之類的KV數據庫作為緩存已經非常成熟了,而且基本上都可以集群化部署,操作起來也很簡單,簡直變成了一個 高并發 的代言詞了,核心就是 緩存技術 了,而 memcache 和 redis 只是用來實現這個緩存技術的工具而已。

無敵的哈希

但凡大數據處理,高并發系統,必言哈希,隨機插入,時間復雜度O(1),隨便查詢,時間復雜度O(1),除了耗費點空間以外,幾乎沒什么缺點了,在現在這個內存廉價的時代,哈希表變成了一個高并發系統的標配。

正面的例子

我們來看個例子,看看一些個大家眼中標準的高并發系統的設計,這些設計大家應該都看過,無非就是上面的幾個要點,最重要的就是 緩存+哈希 ,這兩個東西的組合好像無所不能。

活動秒殺頁面

活動秒殺頁面,這是個標準的高并發吧,到了搞活動的那個時刻,單頁面的訪問量是天量數據了,但這種系統有個特點 邏輯簡單,只要帶寬和性能夠,就一定能提供穩定的服務
服務能迅速的返回數據即可,沒有什么計算邏輯,這種高并發系統的設計基本上就是在怎么壓榨機器的IO性能了,如果有CDN絕對使用CDN,能在本機讀取的絕不走網絡獲取,能讀取到內存中絕不放在硬盤上,把系統的磁盤IO和網絡IO都盡可能的壓榨,使用 緩存+哈希 技術,只要設計合理,99%的情況能搞定。

活動頁面的沖擊感實在太強,想象一下幾千萬人同時訪問網站還沒有掛,讓很多人覺得 高并發 應該就是這樣子,這估計也是 高并發 這次經常在電商技術中出現的原因吧,因為搞個活動就可以搞出一個 高并發 事件。

這樣的場景再擴展一點,就是凡是能提前提供數據的并發訪問,就可以用 緩存+哈希 來搞定并發。

12306

接下來,我們再看看這個星球并發量最瘋狂的網站,瞬間的訪問量碾壓其他網站的12306,這種場景下的高并發也有個特點,那就是 雖然量大,但其實無法給每個用戶提供服務 。

類似的其實還有商品的搶購系統,商品和車票一共就1000張,你100萬的人搶,你系統做得再好,也無法給100萬人提供服務,之前12306剛剛上線的時候很多人噴,說如果讓某某公司來做肯定能做好,但大家很多只看到了表面,讓某很厲害的公司來做,最多也只能做到大家訪問的時候不會掛掉,你買不到車票還是買不到,而且現在的12306體驗也已經做得很好了,也不卡了,但是還是很多人罵,為什么?還不是因為買不到票。

對于這樣的系統,設計關注的就不僅僅是提高性能了,因為性能瓶頸已經擺在那了,就1000張票,做得更多的是分流和限流了,除了 緩存+哈希 來保證用戶體驗以外,出現了奇葩驗證碼,各個站點分時間點放票。然后通過排隊系統來以一種 異步 的方式提供最終的服務。

我們給這樣的場景再擴展一下,凡是不能提前提供數據的,可以通過 緩存+哈希 來提高用戶體驗,然后通過 異步方式 來提供服務。

高并發系統如何設計

如果把上面兩個場景的情況合并一下,仿佛 緩存+哈希 變成萬能的了,很多人眼中的 高并發 就是上面的場景的組合,認為 緩存+哈希 就可以解決高并發的問題,其他的場景中,加上 緩存 提高讀寫速度,在加上 哈希 提供分流技術,再通過一個 異步 提供最終服務,高并發就這么搞定了,但實際上是不是這樣呢?顯然沒那么簡單,那如何來設計一個高并發的系統呢?

合理的數據結構

舉個例子來說吧,搜索提示功能大家都知道吧,就是下面這個圖的東西。

?

如果是google,baidu這種大型搜索系統,或者京東淘寶這種電商系統,搜索提示的調用量是搜索服務本身調用量的幾倍,因為你每輸入一個鍵盤,就要調用一次搜索提示服務,這算得上是個標準的 高并發 系統吧?那么它是怎么實現的呢?

可能很多人腦子里立刻出現了 緩存+哈希 的系統,把搜索的搜索提示詞存在 redis 集群中,每次來了請求直接 redis 集群中查找key,然后返回相應的value值就行了,完美解決,雖然耗費點內存,但是空間換時間嘛,也能接受,這么做行不行?恩,我覺得是可以的,但有人這么做嗎?沒有。

了解的人應該知道,沒有人會這么來實現,這種搜索提示的功能一般用 trie樹 來做,耗費的內存不多,查找速度為O(k),其中k為字符串的長度,雖然看上去沒有哈希表的O(1)好,但是少了網絡開銷,節約了很多內存,并且實際查找時間還要不比 緩存+哈希 慢多少,一種合適當前場景的核心數據結構才是 高并發 系統的關鍵, 緩存+哈希 如果也看成一種數據結構,但這種數據結構并不適用于所有的 高并發 場景,所以

高并發系統的設計,關鍵在合理的數據結構的設計,而不在架構的套用

不斷的代碼性能優化

有了上面的數據結構,并且設計出了系統了,拿到線上一跑,效果還行,但感覺沒達到極限,這時候可千萬不能就直接上外部工具(比如緩存)提升性能,需要做的是不斷的代碼性能的優化,簡單的說,就是不斷的review你的代碼,不斷的找出可以優化的性能點,然后進行優化,因為之前設計的時候就已經通過理論大概能算出來這個系統的并發量了,比如上面那個搜索提示,如果我們假定平均每個搜索詞6個字符,檢索一次大約需要查詢6次,需要2-3毫秒,這樣的話,如果8核的機器,多線程編程方式,一秒鐘最多能接受3200次請求(1000ms/2.5ms*8),如果沒達到這個量級,那么肯定是代碼哪里有問題。

這個階段可能需要借助一些個工具了,JAVA有JAVA的性能優化工具,大家都有自己覺得好使的,我本身JAVA用得很少,也沒啥可推薦的,如果是Golang的話,自帶的 go tool pprof 就能很好的進行性能優化。

或者最簡單的,就是把各個模塊的時間打印出來,壓測一遍,看看哪個模塊耗時,然后再去仔細review那個模塊的代碼,進行算法和數據結構的優化,我個人比較推崇這個辦法,雖然比較笨,但是比較實在,性能差就是差,比較直觀能看出來,也能看出需要優化的點,而且比較貼近代碼,少了外部工具的干擾,可能也比較裝逼吧。

這個過程是一個長期的過程,也是《重構:改善代碼的既有設計》中提到的,一個優秀的系統需要不斷的進行代碼級別的優化和重構,所以

高并發系統的實現,就是不斷的優化你代碼的性能,不斷逼近你設計時的理論值

再考慮外部通用方法

以上兩個都完成了,并發量也基本達到理論值了,但是還有提升的需求,這時候再來考慮外部的通用方法,比如加一個LRU緩存,把熱詞的查詢時間變成O(1),進一步提高性能。

說起LRU,多說一句,這是個標準的 緩存技術 了,實現起來代碼也不復雜,就是個 哈希表+鏈表 的數據結構,一個合格的開發人員,即便沒有聽說過,給定一個場景,應該也能自己設計出來,我見過很多簡歷都說自己有 大型高并發系統的開發經驗,能熟練運用各種緩存技術,也對緩存技術有深入的了解 ,但是一面試的時候我讓他寫個LRU,首先有50%的人沒聽說過,OK,沒聽過沒關系,我描述一下,然后給一個場景, 硬盤上有N條數據,并且有一個程序包,提供GET和SET方法,可以操作磁盤讀寫數據,但是速度太慢,請設計一個內存中的數據結構,也提供GET和SET方法,保存最近訪問的前100條數據 ,這個數據結構就是一個LRU了,讓面試者實現出來,如果覺得寫代碼麻煩,可以把數據結構設計出來描述一下就行了,就這樣,還很多人不會,這怎么能說是對緩存技術有深入了解呢?就這樣,怎么能說有過大型高并發系統的經驗呢?這只是開源工具的使用經驗罷了。

在沒把系統的性能壓榨完全之前,不要使用外部的通用方法,因為使用了以后就沒有太多進一步優化空間了。

最后靠運維技術了

上面幾種都已經弄完了,還需要提升性能,這時候再考慮運維的技術了,比如常規的加負載均衡,部署成集群之類的,通過運維和部署的方法提高服務的并發性。

高并發 系統只是相對的,沒有什么無上限的高并發,流量的洪流來了,再高的高并發一樣掛,新浪微博的 高并發 應該做得很好吧?但是 林心如 發條微博說她和 霍建華 談戀愛了,一樣把微博搞掛了(非官方消息啊,我猜測的,呵呵,那天下午新浪微博正好掛了),呵呵,你說要是 TFBOY 明天過生日,微博是不是要連夜加幾個 redis 集群啊?如果有微博的朋友,留個言溜溜唄:)

總結

羅里吧嗦說了這么多,其實我就想表達一個意思,不管是前面的 高可用 ,還是今天的 高并發 。

代碼才是關鍵,架構都是錦上添花的東西,既然是錦上添花的,必然坑多,沒有什么捷徑。

代碼的健壯性決定了高可用,這些印度人就能做到,而高性能,高并發需要的不僅僅是代碼的健壯性,還有數據結構的設計和代碼的調優能力了。

架構模式是大家總結出來的,和你的系統可能關系不是很大,學習太多的架構,腦袋會亂,還不如實打實的看幾本書,然后對著架構多推敲練習,很多人對數據結構嗤之以鼻,覺得對于現有的開發來說,數據結構沒那么重要了,但對于后端開發來說,數據結構是很重要的技能,雖然面試的時候不會讓你去翻轉一棵二叉樹,但二叉樹是什么,什么場景下用還是應該知道的吧?

找準合適的數據結構,不斷的優化代碼,這樣來提升你的系統性能,這樣的系統才是你可控的,才有不斷優化的空間,更好的 高并發 ,如果一開始就上外部的 緩存技術 ,很可能如果性能達不到要求,就沒有優化空間了,因為要修改外部的系統還是很困難的。

這幾篇我覺得我都在瞎扯淡,雖然比較虛,但也是我工作這么些年淌坑無數以后的總結吧,后面的文章會寫一些實際的了,我的搜索引擎部分還沒寫完呢。敬請期待。

 

 

來自:http://blog.jobbole.com/109905/

 

 本文由用戶 AraTsa 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!