本文做者:黃小斜git
轉載請務必在文章開頭註明出處和做者。程序員
作後端開發的同窗,想必對緩存都不會陌生了,平時咱們可能會使用Redis,MemCache這類緩存組件,或者是本地緩存,來實現一些後端的應用。github
那麼,嚴格來講,到底什麼纔是緩存呢,先來看看百度百科的定義。面試
緩存(cache),原始意義是指訪問速度比通常隨機存取存儲器(RAM)快的一種高速存儲器,一般它不像系統主存那樣使用DRAM技術,而使用昂貴但較快速的SRAM技術。緩存的設置是全部現代計算機系統發揮高性能的重要因素之一。redis
最先,「緩存」一詞是用來指代計算機硬件中的高速緩存,由於CPU和內存的運算速度差距過大,若是CPU直接和內存交互的話,會浪費掉CPU的大量運算時間,因而有了高速緩存,來爲這兩個速度差距甚遠的組件作中介。數據庫
具體的工做原理是,CPU要取數據的時候,先找高速緩存要,因爲它們倆的速度差距並不大,因此CPU不會損失掉太多性能,若是數據就在緩存中,那麼就直接在緩存裏取,不然則到內存去取,取完以後還要留在高速緩存中,以便於下次CPU要使用時無需再到內存中去取。後端
其實,高速緩存還能夠分爲一級緩存,二級緩存和三級緩存等,每往下一級,速度也就越慢,價格也越低,畢竟,成本是咱們不得不考慮的因素,要否則一切硬件都上頂配,就不須要討論軟件的優化了。數組
除了高速緩存外,其實還有硬盤緩存、網絡緩存等操做系統自身實現的緩存,目的也是爲了在兩個運算速度不一樣的組件之間創建一個橋樑,而且,緩存中的數據每每都是局部數據,又稱熱點數據,這部分數據常常被使用,所以更具備被緩存的價值緩存
上面講了一些關於計算機中已有的緩存,那麼,咱們平時在代碼開發中用的緩存又是什麼東西呢?好像不太同樣啊。安全
相信你們都聽過Redis,這是業界最流行的緩存組件之一,不妨看看它是如何被定義的。
redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。
在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。
根據上文的描述,咱們能夠看出,Redis是一個K/V的存儲系統,而且它是把數據緩存在內存中的,你們都知道,內存比硬盤的速度要快得多,而咱們平時常用的數據庫,本質上仍是基於硬盤IO來工做的,每次讀寫數據庫都要通過硬盤的IO操做。
因此,讀寫數據庫的耗時要比讀內存要慢得多,剛剛咱們說了,CPU讀寫內存和高速緩存的速度差別也很大,因此,這裏Redis和高速緩存扮演了同一個角色,只不過一個是硬件,一個是軟件,一個鏈接的是內存和CPU,另外一個鏈接的是磁盤和內存。
除了Redis之外,還有不少緩存組件好比memcache也是基於這種方式來實現的,經過緩存數據庫的熱點數據,來提升應用的訪問速率,在後端開發中是很是常見的一種技術應用。
做爲一個後端開發工程師,不懂緩存是不行的,即便你每天作的是CRUD,至少也要會使用本地緩存吧。
另外,面試的時候常常也會有緩存方面的問題,簡單點的,可能就讓你說一說Redis的基礎和實現原理,複雜點的,會結合場景,問你緩存可能引起的一些問題,又或者,分佈式場景下的緩存如何設計。
從會使用到了解原理,再到學習進階的用法,學習緩存和學習其餘技術同樣,須要先易後難,如今就讓咱們一塊兒來看看,如何搞定緩存這個難纏的小妖精吧。
第一次接觸緩存,實際上是我在學習hashmap的時候,hashmap自己就是一個KV的鍵值對對象,和我們剛剛說的Redis彷佛有點像,那麼,如何讓hashmap真正變成一個緩存呢,其實也不難。
實際上,不少本地緩存就是直接用concurrenthashmap來實現的,注意,這裏用的是支持併發的concurrenthashmap,這由於啊,緩存對象是能夠支持多個線程同時訪問的,若是出現同時put好的狀況,就有可能出現異常,因而,使用concurrenthashmap就能夠避免這種問題。
爲了方便起見,後面對於concurrenthashmap簡稱chm。chm不只能夠保證對其訪問是線程安全的,並且比直接用synchronized等關鍵字要更加高效靈活。
那麼,chm實現緩存的原理是什麼呢?首先,在應用中,緩存的本質就是把一些常常出現的數據存在內存中,而Java的內存又能夠分爲堆內存和棧內存,緩存對象天然是要放在堆內存中了。
因此它是一個類裏的一個成員變量,好比這樣。
private Map<String, CacheObj> CACHE_OBJECT_MAP = new ConcurrentHashMap<>();
可是這樣使用緩存有一個問題,這個成員變量屬於實例,若是實例沒有初始化或者是被回收,那麼這個緩存對象也就跟着消失了,那確定是不行的,畢竟緩存的生命週期應該和應用自己同樣長,另外,這個緩存對象還可能被修改,好比我能夠在代碼裏隨便操做一下,讓它 = null,或者是從新初始化一下,那同樣也是不被容許的。
因此,標準的chm本地緩存應該是這樣被初始化的。
private static final Map<String, CacheObj> CACHE_OBJECT_MAP = new ConcurrentHashMap<>();
靜態變量保證它一直存在於堆內存中,而final關鍵字修飾可讓它不會被從新初始化或者指向其餘對象。
接下來的事情就簡單了,你只要使用get和put方法就能夠了。
固然,這只是最基礎的本地緩存實現,還能夠實現成LRU緩存,搞各類騷操做,這裏咱們再也不具體討論,網上有不少demo,能夠自行去參考。
除了本地緩存以外,咱們還應該去了解一些經常使用的緩存技術,好比Redis、memcache這類緩存組件,還有相似Ecache這類的Java緩存框架。
這類成熟的緩存技術,更加值得咱們學習和使用,它們通常都能提供各個場景下的先進解決方案,好比如何處理熱點數據,如何進行分佈式部署,實現高可用,如何進行數據同步,主從複製,以及實現分佈式鎖、分佈式ID生成器等各個場景的應用。
有一個面試題相信你們都遇到過,那就是問你redis和memcache的區別,咱們不妨經過這個面試題來了解一下,到底它們都有些什麼特色。
Redis不只支持簡單的k/v類型的數據,同時還支持list、set、zset(sorted set)、hash等數據結構的存儲,使得它擁有更廣闊的應用場景。
而Memcached惟一支持的數據類型是字符串string,很是適合緩存只讀數據,由於字符串不須要額外的處理。
Redis最大的亮點是支持數據持久化,它在運行的時候能夠將數據備份在磁盤中,斷電或重啓後,緩存數據能夠再次加載到內存中,只要Redis配置的合理,基本上不會丟失數據。
這一點很要命,memcache不持久話數據,萬一斷電了就裂開。
Memcache在併發場景下,能用cas保證一致性,而Redis事務支持比較弱,只能保證事務中的每一個操做連續執行。
雖然Redis支持事務而memcache不支持,可是memcache對並不是的支持不亞於Redis。
性能方面,Redis在讀操做和寫操做上是略領先Memcached的。
Memcached的內存管理不像Redis那麼複雜,元數據metadata更小,相對來講額外開銷就不多。
對於制度數據來講,即便沒有數據備份也沒什麼關係,可是若是存在讀寫,那麼顯然memcache是不合適的,總體來看,Redis仍是略勝一籌。
Redis這類緩存組件實際上是經過C/S方式部署服務的,而另外一種緩存組件ecache,則是直接集成,緩存的數據就放在JVM裏,有點相似於咱們的本地緩存,其實ecache還應用在hibernate中,因此不少時候咱們都是在不知情的狀況下就已經使用了cache。
固然了,ecache這類緩存的數據放在JVM,要共享起來的話就比較麻煩了,特別是須要應用在分佈式場景的時候,實現起來是比較複雜的。
相信你在看了本地緩存那一部分的時候,會以爲緩存實現起來也沒什麼難度啊,一個hashmap就搞定了。
實際上,緩存的實現要考慮的問題還不少,就拿Redis來講,使用什麼樣的數據結構來存儲數據就是很重要的一個問題,咱們固然但願用盡可能小的空間來存儘可能多的數據,同時還要提高緩存CRUD的效率。
Redis中支持多種數據結構,好比字符串、數組、字典(map)以及列表、set、有序set等,別看這些數據結構很簡單,可是做者實現起來都花了很多功夫。
每每一個結構有多種底層實現,目的就是爲了壓縮空間,提升效率。有興趣的朋友能夠看下這篇文章
Redis數據結構的底層實現https://blog.csdn.net/Future_LL/article/details/88525004
除了數據結構以外,Redis自己的實現也回味無窮,好比,Redis的服務端和客戶端是怎麼設計的,另外,Redis是單線程工做的,爲何要這麼設計,還有Redis的事務是如何實現的,這些內容都值得咱們一一去學習瞭解。
另外還有一些進階的內容,好比Redis的部署方案,一般包括主從部署、集羣方案、HA方案等等,Redis官方也有Redis-cluster的高可用集羣方案。Redis也經常用於分佈式鎖,分佈式ID生成器,而這些技術的背後,其實都有不少值得咱們深挖的點。
時間關係,咱們今天就講到這裏,對於緩存和Redis的學習,就從這篇文章開始吧。
Java技術倉庫《Java程序員複習指南》
https://github.com/h2pl/Java-Tutorial
整合全網優質Java學習內容,幫助你從基礎到進階系統化複習Java
全網最熱的Java面試指南,共200多頁,很是實用,無論是用於複習仍是準備面試都是不錯的。
在公衆號【Java技術江湖】回覆「PDF」便可免費領取。
若是以爲本文對你有幫助的話,請你也不要吝嗇你的「好看」哈,轉發朋友圈就是對我最大的支持啦,大家的支持是對我最大的鼓勵。
對本系列文章有什麼建議和意見,也歡迎留言告訴我,期待你的回饋。