整體上:html
開濤大神在博客中說過:在開發高併發系統時有三把利器用來保護系統:緩存、降級和限流。java
根據業務系統的類型,考慮不一樣的針對在數據庫方面的擴容:mysql
緩存設置的地方nginx
手段redis
主要是Redis、CDN、瀏覽器等,其次可能一些問題算法
2.3可能存在的問題sql
2.3.1一致性數據庫
緩存一致性的話,主要可能考慮到如下幾種可能致使一致性問題:編程
2.3.2緩存併發api
因爲併發時大量請求瞬間涌入,那麼在創建或修改緩存時(還沒完成),就可能已經打到DB或老緩存上了,形成雪崩或緩存一致性問題
解決方案:
鎖機制
2.3.3緩存穿透
緩存穿透是指查詢一個必定不存在的數據,因爲緩存是不命中時被動寫的,若是從存儲層查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊咱們的應用,這就是漏洞。
解決方案:
最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(不論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。
2.3.4雪崩
緩存雪崩是指在咱們設置緩存時採用了相同的過時時間,致使緩存在某一時刻同時失效,請求所有轉發到DB,DB瞬時壓力太重雪崩。
解決方案
大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。這裏分享一個簡單方案就時講緩存失效時間分散開,好比咱們能夠在原有的失效時間基礎上增長一個隨機值,好比1-5分鐘隨機,這樣每個緩存的過時時間的重複率就會下降,就很難引起集體失效的事件。
作到了大量請求異步與解耦,好比下單與短信驗證碼請求 的處理,及早返回處理。
好比遠程調用較慢,能夠用消息隊列攢一堆去完成調用。
基於消息的模型,等通知便可,而不是處理(那是RPC),以及索引庫更新,只是一個通知,而不是強完成(重量級),只是通知而不是職責所在(好比上游系統對下的通知)。
僅僅最終一致性。
需求場景
及一些非核心業務或者多個對象、數據量小時,而
相比RPC框架(dubbo等),RPC場景是須要 強一致性、對延遲敏感、注重結果立馬返回、重量級
SOA 微服務等
我的見解,原則是:
1.業務是否適合拆分,拆分後耦合度不能高
2.是否須要拆分,拆分是爲了高併發高可用,是否解決了高併發熱點問題?
好比股票系統中,收盤後,你有幾百萬數據須要入庫了,若是不作限流,瞬間將會把帶寬所有打滿,其它服務暫時GG或者主從延遲很大等等問題。
限流的算法
常見的限流算法有:計數器、漏桶和令牌桶算法。
計數器
計數器是最簡單粗暴的算法。好比某個服務最多隻能每秒鐘處理100個請求。咱們能夠設置一個1秒鐘的滑動窗口,窗口中有10個格子,每一個格子100毫秒,每100毫秒移動一次,每次移動都須要記錄當前服務請求的次數。內存中須要保存10次的次數。能夠用數據結構LinkedList來實現。格子每次移動的時候判斷一次,當前訪問次數和LinkedList中最後一個相差是否超過100,若是超過就須要限流了。
很明顯,當滑動窗口的格子劃分的越多,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確。
漏桶算法
漏桶算法即leaky bucket是一種很是經常使用的限流算法,能夠用來實現流量整形(Traffic Shaping)和流量控制(Traffic Policing)。貼了一張維基百科上示意圖幫助你們理解:
漏桶算法的主要概念以下:
漏桶算法比較好實現,在單機系統中可使用隊列來實現(.Net中TPL DataFlow能夠較好的處理相似的問題,你能夠在這裏找到相關的介紹),在分佈式環境中消息中間件或者Redis都是可選的方案。
令牌桶算法
令牌桶算法是一個存放固定容量令牌(token)的桶,按照固定速率往桶裏添加令牌。令牌桶算法基本能夠用下面的幾個概念來描述:
以下圖:
令牌算法是根據放令牌的速率去控制輸出的速率,也就是上圖的to network的速率。to network咱們能夠理解爲消息的處理程序,執行某段業務或者調用某個RPC。
應用級限流
限流總併發/鏈接/請求數
對於一個應用系統來講必定會有極限併發/請求數,即總有一個TPS/QPS閥值,若是超了閥值則系統就會不響應用戶請求或響應的很是慢,所以咱們最好進行過載保護,防止大量請求涌入擊垮系統。
若是使用過Tomcat,其Connector其中一種配置有以下幾個參數:
acceptCount:若是Tomcat的線程都忙於響應,新來的鏈接會進入隊列排隊,若是超出排隊大小,則拒絕鏈接;
maxConnections:瞬時最大鏈接數,超出的會排隊等待;
maxThreads:Tomcat能啓動用來處理請求的最大線程數,若是請求處理量一直遠遠大於最大線程數則可能會僵死。
詳細的配置請參考官方文檔。另外如MySQL(如max_connections)、Redis(如tcp-backlog)都會有相似的限制鏈接數的配置。
若是接口可能會有突發訪問狀況,但又擔憂訪問量太大形成崩潰,如搶購業務;這個時候就須要限制這個接口的總併發/請求數總請求數了;由於粒度比較細,能夠爲每一個接口都設置相應的閥值。可使用Java中的AtomicLong進行限流:
=================================
try {
if(atomic.incrementAndGet() > 限流數) {
//拒絕請求
}
//處理請求
} finally {
atomic.decrementAndGet();
}
=================================
=================================
=================================
咱們使用Guava的Cache來存儲計數器,過時時間設置爲2秒(保證1秒內的計數器是有的),而後咱們獲取當前時間戳而後取秒數來做爲KEY進行計數統計和限流,這種方式也是簡單粗暴,剛纔說的場景夠用了。
Guava RateLimiter提供了令牌桶算法實現:平滑突發限流(SmoothBursty)和平滑預熱限流(SmoothWarmingUp)實現。
分佈式限流
分佈式限流最關鍵的是要將限流服務作成原子化,而解決方案可使使用redis+lua或者nginx+lua技術進行實現,經過這兩種技術能夠實現的高併發和高性能。
使用redis+lua實現時間窗內某個接口的請求數限流,實現了該功能後能夠改造爲限流總併發/請求數和限制總資源數。Lua自己就是一種編程語言,也可使用它實現複雜的令牌桶或漏桶算法。
1.服務降級
當服務器壓力劇增的狀況下,根據當前業務狀況及流量對一些服務和頁面有策略的降級,以此釋放服務器資源以保證核心任務的正常運行。
降級後的處理能夠設置一些默認的頁面或返回,如:
2.服務熔斷
服務熔斷通常是指軟件系統中,因爲某些緣由使得服務出現了過載現象,爲防止形成整個系統故障,從而採用的一種保護措施,因此不少地方把熔斷亦稱爲過載保護。
3.對比區別和共性
相似性的:
而二者的區別也是明顯的:
出現如下狀況時開始考慮
如下爲數據庫優化,參考以前寫的MySQL優化部分。