轉:https://www.cnblogs.com/bethunebtj/p/9159914.htmlhtml
在高併發請求時,爲什麼咱們頻繁提到緩存技術?最直接的緣由是,目前磁盤IO和網絡IO相對於內存IO的成百上千倍的性能劣勢。
作個簡單計算,若是咱們須要某個數據,該數據從數據庫磁盤讀出來須要0.1s,從交換機傳過來須要0.05s,那麼每一個請求完成最少0.15s(固然,事實上磁盤和網絡IO也沒有這麼慢,這裏只是舉例),該數據庫服務器每秒只能響應67個請求;而若是該數據存在於本機內存裏,讀出來只須要10us,那麼每秒鐘可以響應100,000個請求。web
經過將高頻使用的數據存在離cpu更近的位置,以減小數據傳輸時間,從而提升處理效率,這就是緩存的意義。redis
一切地方。例如:數據庫
上面這是系統層面,在軟件系統設計層面,不少地方也用了緩存:瀏覽器
緩存頗有用,可是緩存用很差也會埋不少坑:緩存
緩存穿透是說收到了一個請求,可是該請求緩存裏沒有,只能去數據庫裏查詢,而後放進緩存。這裏面有兩個風險,一個是同時有好多請求訪問同一個數據,而後業務系統把這些請求全發到了數據庫;第二個是有人惡意構造一個邏輯上不存在的數據,而後大量發送這個請求,這樣每次請求都會被髮送到數據庫,可能致使數據掛掉。服務器
怎麼應對這種狀況呢?對於惡意訪問,一個思路是事先作校驗,對惡意數據直接過濾掉,不要發到數據庫層;第二個思路是緩存空結果,就是對查詢不存在的數據仍然記錄一條該數據不存在在緩存裏,這樣能有效的減小查詢數據庫的次數。網絡
那麼非惡意訪問呢?這個要結合緩存擊穿來說。併發
上面提到的某個數據沒有,而後好多請求都被髮到數據庫其實能夠歸爲緩存擊穿的範疇:對於熱點數據,當數據失效的一瞬間,全部請求都被下放到數據庫去請求更新緩存,數據庫被壓垮。分佈式
怎麼防範這種問題呢?一個思路是全局鎖,就是全部訪問某個數據的請求都共享一個鎖,得到鎖的那個纔有資格去訪問數據庫,其餘線程必須等待。可是如今的業務都是分佈式的,本地鎖無法控制其餘服務器也等待,因此要用到全局鎖,好比用redis的setnx實現全局鎖。
另外一個思路是對即將過時的數據主動刷新,作法能夠有不少,好比起一個線程輪詢數據,好比把全部數據劃分爲不一樣的緩存區間,按期分區間刷新數據等等。這第二個思路又和咱們接下來要講的緩存雪崩有關係。
緩存雪崩是指好比咱們給全部的數據設置了一樣的過時時間,而後在某一個歷史性時刻,整個緩存的數據所有過時了,而後瞬間全部的請求都被打到了數據庫,數據庫就崩了。
解決思路要麼是分治,劃分更小的緩存區間,按區間過時;要麼是給每一個key的過時時間加個隨機值,避免同時過時,達到錯峯刷新緩存的目的。
說到刷新緩存,其實也有坑的。好比我以前的一份工做裏,有一次大活動,正是如火如荼的時候,全部的廣告位忽然都變空白了。後來追查緣由,全部的廣告素材都在緩存裏,而後起了個程序,專門負責刷新緩存,每次把當前的素材全量刷新。
壞就壞在這個全量上。由於大活動的時候流量極大,廣告更新壓力也很大,把負責提供更新素材的程序壓崩了。刷新緩存的程序在請求時,收到了一個返回結果Null
。接下來就喜聞樂見了,刷新程序根據這個null,清空了整個緩存,全部廣告素材都失效了。
總之,想要作好高併發系統的緩存,就要考慮到各類邊角狀況,當心設計,任何細小的疏忽均可能致使系統崩潰。