隨着互聯網的高速發展,市面上也出現了愈來愈多的網站和app
。咱們判斷一個軟件是否好用,用戶體驗就是一個重要的衡量標準。好比說咱們常常用的微信,打開一個頁面要十幾秒,發個語音要幾分鐘對方纔能收到。相信這樣的軟件你們確定是都不肯意用的。軟件要作到用戶體驗好,響應速度快,緩存就是必不可少的一個神器。緩存又分進程內緩存和分佈式緩存兩種:分佈式緩存如redis
、memcached
等,還有本地(進程內)緩存如ehcache
、GuavaCache
、Caffeine
等。html
緩存做爲一個數據數據模型對象,那麼它有一些什麼樣的特徵呢?下面咱們分別來介紹下這些特徵。java
hitCount
(命中次數)。適用於保證高頻數據有效性場景。本地緩存的話是咱們的應用和緩存都在同一個進程裏面,獲取緩存數據的時候純內存操做,沒有額外的網絡開銷,速度很是快。它適用於緩存一些應用中基本不會變化的數據,好比(國家、省份、城市等)。redis
進程緩存的話,通常能夠在應用啓動的時候,把須要的數據加載到系統中。更新緩存的話能夠採起定時更新(實時性不高)。具體實現的話就是在應用中起一個定時任務(ScheduledExecutorService、TimerTask等),讓它每隔多久去加載變動(數據變動以後能夠修改數據庫最後修改的時間,每次查詢變動數據的時候均可以根據這個最後變動時間加上半小時大於當前時間的數據)的數據從新到緩存裏面來。若是以爲這個比較麻煩的話,還能夠直接所有全量更新(就跟項目啓動加載數據同樣)。這種方式的話,對數據更新可能會有點延遲。可能這臺機器看到的是更新後的數據,那臺機器看到的數據仍是老的(機器發佈時間可能不同)。因此這種方式比較適用於對數據實時性要求不高的數據。若是對實時性有要求的話能夠經過廣播訂閱mq
消息。若是有數據更新mq
會把更新數據推送到每一臺機器,這種方式的話實時性會比前一種定時更新的方法會好。可是實現起來會比較複雜。
算法
常見本地緩存有如下幾種實現方式:
從上述表格咱們看出性能最佳的是Caffeine
。關於這個本地緩存的話我仍是強烈推薦的,裏面提供了豐富的api
,以及各類各樣的淘汰算法。如需瞭解更加詳細的話能夠看下之前寫的這個篇文章《本地緩存性能之王Caffeine》。數據庫
redis
、MemCache
等。在高併發的環境下,好比春節搶票大戰,一到放票的時間節點,分分鐘大量用戶以及黃牛的各類搶票軟件流量進入12306
,這時候若是每一個用戶的訪問都去數據庫實時查詢票的庫存,大量讀的請求涌入到數據庫,瞬間Db
就會被打爆,cpu
直接上升100%
,服務立刻就要宕機或者假死。即便進行了分庫分表也是沒法避免的。爲了減輕db的壓力以及提升系統的響應速度。通常都會在數據庫前面加上一層緩存,甚至可能還會有多級緩存。api
指大量緩存同一時間段集體失效,或者緩存總體不能提供服務,致使大量的請求所有到達數據庫
對數據CPU
和內存形成巨大壓力,嚴重的會形成數據庫宕機。所以而造成的一系列連鎖反應形成整個系統奔潰。
解決這個問題能夠從如下方面入手:緩存
redis
節點下線,緩存仍是能夠用。通常稍微大點的公司還可能會在多個機房部署Redis。key
的過時時間,定時去輪詢這些key
的過時時間。例如一個key
的value
設置的過時時間是30min
,那咱們能夠爲這個key設置它本身的一個過時時間爲20min
。因此當這個key
到了20min的時候咱們就能夠從新去構建這個key
的緩存,同時也更新這個key
的一個過時時間。指查詢一個不存在的數據,每次經過接口或者去查詢數據庫都查不到這個數據,好比黑客的惡意攻擊,好比知道一個訂單號後,而後就僞造一些不存在的訂單號,而後併發來請求你這個訂單詳情。這些訂單號在緩存中都查詢不到,而後會致使把這些查詢請求所有打到數據庫或者SOA接口。這樣的話就會致使數據庫宕機或者你的服務大量超時。
這種查詢不存在的數據就是緩存擊穿。
解決這個問題能夠從如下方面入手:服務器
是指緩存裏面的一個熱點key
(拼多多的五菱宏光神車的秒殺)在某個時間點過時。針對於這一個key有大量併發請求過來而後都會同時去數據庫請求數據,瞬間對數據庫形成巨大的壓力。
這個的話能夠用緩存雪崩的幾種解決方法來避免:微信
key
的過時時間,定時去輪詢這些key
的過時時間。例如一個key
的value
設置的過時時間是30min
,那咱們能夠爲這個key設置它本身的一個過時時間爲20min
。因此當這個key到了20min的時候咱們就能夠從新去構建這個key
的緩存,同時也更新這個key
的一個過時時間。key
的狀況下,好比你有100
個併發請求都要來取A
的緩存,這時候咱們能夠藉助redis分佈式鎖來構建緩存,讓只有一個請求能夠去查詢DB其餘99個(沒有獲取到鎖)都在外面等着,等A查詢到數據而且把緩存構建好以後其餘99
個請求都只須要從緩存取就行了。原理就跟咱們java
的DCL
(double checked locking)思想有點相似。咱們通常的緩存更新主要有如下幾種更新策略:網絡
若是想要真正的設計好一個緩存,咱們仍是必需要掌握不少的知識,對於不一樣場景,緩存有各自不一樣的用法。好比實際工做中咱們對於訂單詳情的一個緩存。咱們可能會根據訂單的狀態來來構建緩存。咱們就以機票訂單爲例,已出行、或者已經取消的訂單咱們基本上是不會去管的(訂單狀態已經終止了),這種的話數據基本也不會變了,因此對於這種訂單咱們設置的過時時間是否是就能夠久一點,好比7天或者30天。對於未出行即將起飛的訂單,這時候顧客是否是就會頻繁的去刷新訂單看看,看看有沒有晚點什麼的,或者登機口是在哪。對於這種實時性要求比較高的訂單咱們過時時間仍是要設置的比較短的,若是是須要更改訂單的狀態查詢的時候能夠直接不走緩存,直接查詢master
庫。畢竟這種更改訂單狀態的操做仍是比較有限的。大多數狀況都是用來展現的。展現的話是能夠容許實時性要求沒那麼高。總的來講須要開具體的業務,沒有通用的方案。看你的業務需求的容忍度,畢竟脫離了業務來談技術都是耍流氓,是業務驅動技術。
站在巨人的肩膀上摘蘋果:
https://juejin.im/post/6844903665845665805
https://tech.meituan.com/2017/03/17/cache-about.html
http://www.javashuo.com/article/p-ftdcqtnl-cx.html