天貓雙11成了全民購物狂歡節。自2009年開始,天貓雙11已經經曆了11個年頭,11年的時間,雙11的成交額從5000萬增長到了2019年的2600億,整整翻了4000多倍。今年的雙十一,1分30秒左右,交易額突破100億,平均每秒1.05億的交易額,1小時的時候,交易額突破1000億,TPS達到了54萬。而QPS和PV等指標遠遠大于這個值,這是真正的高並發系統。那阿裏是怎麽打造這麽一個強大的系統?今天我們就聊聊高並發系統。
一、高並發系統特點
高並發系統的特點非常明顯,就是請求量非常大,請求量可以用QPS、TPS、PV等指標來表示。當單機的QPS高于10000的時候,就算的上一個真正的高並發系統。像淘寶、百度、12306這都屬于高並發的系統。
高並發系統都會有一個流量的高峰期,像天貓的雙11、12306每年春節的搶購火車票以及各大電商網站舉辦的秒殺活動。這些特定的時期,網站的流量會急劇增加,是平常的十倍百倍。所以,這就要求系統能自動擴容伸縮,當流量高峰期到來的時候,可以快速擴容機器來應對大量的請求,到流量高峰期過去,可以縮減機器或者用這些機器去做別的業務,減少成本。
二、高並發系統瓶頸
一個應用系統的正常運行需要計算機的各種資源,如CPU、內存、磁盤、數據庫連接池等。當這些資源任何一項達到瓶頸,系統就達到了它的最大承受範圍。
- 2.1 CPU使用率過高
CPU使用率過高是在高並發的系統最常見的一個現象。當有大量的請求的時候,CPU使用率瞬間飙到90%以上。因爲此時服務器需要處理大量的請求,會創建大量的tcp連接。應用系統中如果有大量的計算型邏輯和不正確的編碼,也會增加CPU的負擔,導致CPU使用率過高。
- 2.2 內存使用率過高
高並發系統中,會創建大量的線程,而每一個線程都會創建很多對象,每一個對象都會占用內存。如果對象的生命周期很長,不能及時被GC,那就會占用大量的內存空間,當內存耗盡,系統就會發生OOM,最終終止服務。
- 2.3 數據庫壓力大,連接池不夠用。
高並發系統中,對于後端數據庫是一個很大的考驗。因爲應用服務器可以很容易的進行擴展,但是,數據庫很難擴展。當大量的請求發送到數據庫端,數據庫的每一次查詢耗時會明顯增大,導致連接不能釋放,最終會耗盡數據庫的連接數。如果業務中存在慢sql,大量的查詢會導致數據庫死鎖,最終停止服務。
三、高並發系統技術點
當系統遇到高並發的瓶頸的時候,我們需要排查的瓶頸的原因,然後針對這些原因進行排查優化。作爲一個高並發的系統,以下技術必須具備。
- 3.1 分布式集群
由于硬件設備的限制,單機的吞吐能力有限,不管采用什麽手段優化,都不可能突破單機硬件的瓶頸。所以,高並發系統我們需要分布式集群部署,通過集群的能力來解決高並發的問題。在集群搭建上我們應該分地區搭建。假如業務是國際性,在全球範圍內都用業務,所以我們可以考慮在美東、美西、日本、新加坡等地分別建立服務器集群,這樣通過DNS讓域名就近訪問,可以避免網絡延遲,提高用戶端內容的響應速度。
- 3.2 緩存
在高並發的業務當中,數據庫的壓力非常大。磁盤IO和網絡IO的消耗非常大,所以,我們需要采取緩存技術。
通過緩存技術將數據放到離CPU最近的地方,減少與數據庫交互的次數,減少傳輸時間時間,從而提高系統的響應時間。
在使用緩存的過程當中,我們可能會碰到緩存穿透、緩存擊穿、緩存雪崩等問題,我們針對這三個問題做一個簡單的分析。
- 緩存穿透
在設計緩存的時候,如果查詢不到數據不寫入緩存,會導致這個不存在的數據一直查詢數據庫,失去了緩存的意義。
解決方案:如果查詢不到數據,仍然寫入緩存,但是這個key的過期時間很短。
- 緩存擊穿
當熱點key值過期的時候,正好這個key有大量的請求發送過來,因爲緩存已經失效了,請求就會大量的落到DB層,最後導致DB被壓垮。
解決方案:使用互斥鎖(mutex key),當查詢到key值不存在的時候,使用可以緩存框架提供的某些帶成功返回值的操作去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法。在redis中可以使用setnx方法。
- 緩存雪崩
緩存的key設置了相同的過期時間,導致某一時刻所有緩存全部失效,請求全部轉發到DB,導致DB雪崩。
解決方案:設置緩存失效的時間的時候,隨機一個特定範圍內的數字,緩存的時間爲原有的時間加上這個隨機的數字。
- 3.3、CDN
高並發系統CDN並不可少。像淘寶、京東等電商網站,他們的前端頁面全部都是通過CDN進行緩存。用戶根據地理位置訪問距離自己最近的CDN服務器,這樣減輕了應用服務器的壓力,提升了用戶的訪問速度。
通過配置CDN,不僅可以提升靜態頁面的的加載速度,同時可以增加內容冗余,轉移故障、節省帶寬和防DDOS攻擊,保障服務安全。關于CDN的工作原理,我看過一篇文章,說的很清楚,在這裏引用一下:
CDN其實更像是放在應用服務器與用戶之間的一層緩存。所以如果DNS的時候,返回給客戶端的是CDN機器的IP而不是應用的IP,那麽自然就走到了CDN機器上。
爲了實現上述目的,我們會爲該域名配置一個 CNAME(大家注意上面提到的CNAME與A記錄的優先級),那麽這個CNAME是最終如何解析到對應的CDN機器呢?其實流程與DNS解析是一樣的。當發現一個域名設置了CNAME時,DNS解析器會繼續解析這個CNAME別名(其實就是另一個域名)。對這個CNAME解析的時候會用到全局負載DNS解析,它會根據訪問者的地理位置信息返回對應的IP(CDN機器的IP)。因此客戶端實際上得到的是距離它最近的CDN機器的IP地址。
如果說用戶訪問CDN,但是CDN上沒有對應內容會怎麽辦?此時CDN機器其實會根據自身專用的DNS解析服務,根據域名得到源站的IP,然後向源站發送請求獲取數據,並把這些數據緩存到本地,方便後續使用;同時返回本次結果,完成本次請求的訪問。
需要說一下的是,CDN其實也是分層的。距離用戶最近的稱之爲邊緣節點。而CDN的中心服務器集群被稱爲二級緩存。在上面就是應用部署的源站。一般邊緣節點沒數據就去找二級緩存,二級緩存沒數據就去找源站(被稱爲回源)。
- 3.4、分庫分表
這是數據庫層面的解決方案。高並發系統對于數據庫的讀寫次數特別頻繁,除了在應用層面使用緩存技術減輕DB壓力,分庫分表是必須的。通過分庫分表將海量數據分散,減少單表的數據量,從而提升查詢效率。
分庫分表一般分爲垂直拆分和水平拆分。
水平拆分:將一個表的數據拆分到一個或多個庫的不同表中,每個表的數據結構是相同的。通過減輕單表的數據,可以極大的提升查詢效率。同時,通過多個數據實例,也可以增加數據庫可以承受的最大並發。
垂直拆分:將一個有很多字段的寬表拆分成小表,每個表的字段不一樣。可以將熱點字段放到一張表中,不常用的字段放到另一張表中。然後通過緩存將不常用的字段進行緩存,提高查詢效率。
五、高並發系統自我保護
一個良好高並發系統必須要有系統級別的自我保護功能。當請求數超過服務器的最大極限的時候,不是讓用戶無限的等待,而是需要系統啓動自我保護機制,拒絕接受請求。
通常我們采用限流的機制來保護系統。當請求達到限制的速率的時候,系統拒接請求。常用的限流算法有令牌桶算法、漏桶算法和計數器算法。
令牌桶: 令牌桶是指按照固定周期向桶中添加令牌,令牌的數目可以根據實際QPS進行調整,請求時需要從桶中獲取令牌,如果沒有令牌,可以選擇等待,或者放棄。
漏桶:一個裝滿水的桶,每秒向外漏一滴, 如何當前請求能接到,那就可以正常請求,否則,就只能等待下一滴水。
兩種方法看起來很像,但還是有區別的。漏桶的速率是固定的,而令牌桶則是只要桶中有令牌,請求就可以拿。所以這在一定程度允許並發。假設桶中有10個令牌,同時有10個請求發起,那這10個請求都可以拿到令牌。
令牌桶是實際應用比較廣泛的限流算法。很多限流算法都是基于令牌桶的思想實現的。
計數器算法:主要用來限制總並發數,對全局總請求數或者單位時間內的總請求數進行限流。
最後
高並發系統是對一個企業的技術最大的考驗。高並發系統不僅需要良好的架構和技術選型,還需要實際高並發的業務場景來全鏈路進行測試。阿裏正是在通過每一年的雙十一活動一次次的驗證系統、優化架構和技術,最終打造了世界上吞吐量最高的系統,每秒54萬的交易次數。一次正常的交易過程需要很複雜的業務處理,牽涉到很多系統的相互調度,獲取商品、獲取用戶信息、獲取地址、提交訂單、支付、庫存處理、日志等等業務,這是一個很長很複雜的處理流程,能做到每秒54萬的交易次數,平均一次交易的時間不到 0.00185,將機器的性能發揮到了極致。天貓雙十一不僅是一場購物狂歡節,更像是一場技術狂歡節。