分布式緩存Redis

為什么要用緩存梅尤?

高性能

假設這么個場景澜汤,你有個操作,一個請求過來放妈,吭哧吭哧你各種亂七八糟操作mysql骂倘,半天查出來一個結果眼滤,耗時600ms。但是這個結果可能接下來幾個小時都不會變了历涝,或者變了也可以不用立即反饋給用戶诅需。那么此時咋辦漾唉?

緩存啊,折騰600ms查出來的結果堰塌,扔緩存里赵刑,一個key對應一個value,下次再有人查场刑,別走mysql折騰600ms了般此。直接從緩存里,通過一個key查出來一個value牵现,2ms搞定铐懊。性能提升300倍。

這就是所謂的高性能瞎疼。

就是把你一些復雜操作耗時查出來的結果科乎,如果確定后面不咋變了,然后但是馬上還有很多讀請求贼急,那么直接結果放緩存茅茂,后面直接讀緩存就好了。


高并發(fā)

mysql這么重的數(shù)據(jù)庫太抓,壓根兒設計不是讓你玩兒高并發(fā)的空闲,雖然也可以玩兒,但是天然支持不好走敌。mysql單機支撐到2000qps也開始容易報警了碴倾。


所以要是你有個系統(tǒng),高峰期一秒鐘過來的請求有1萬掉丽,那一個mysql單機絕對會死掉影斑。你這個時候就只能上緩存,把很多數(shù)據(jù)放緩存机打,別放mysql。緩存功能簡單片迅,說白了就是key-value式操作残邀,單機支撐的并發(fā)量輕松一秒幾萬十幾萬,支撐高并發(fā)so easy柑蛇。單機承載并發(fā)量是mysql單機的幾十倍芥挣。



所以你要結合這倆場景考慮一下,你為啥要用緩存耻台?


一般很多同學項目里沒啥高并發(fā)場景空免,那就別折騰了,直接用高性能那個場景吧盆耽,就思考有沒有可以緩存結果的復雜查詢場景蹋砚,后續(xù)可以大幅度提升性能扼菠,優(yōu)化用戶體驗,有坝咐,就說這個理由循榆,沒有?墨坚?那你也得編一個出來吧秧饮,不然你不是在搞笑么

(1) redis 和 memcached 的區(qū)別

redis支持更豐富的數(shù)據(jù)類型(支持更復雜的應用場景):Redis不僅僅支持簡單的k/v類型的數(shù)據(jù),同時還提供list泽篮,set盗尸,zset,hash等數(shù)據(jù)結構的存儲帽撑。memcache支持簡單的數(shù)據(jù)類型泼各,String。

Redis支持數(shù)據(jù)的持久化油狂,可以將內存中的數(shù)據(jù)保持在磁盤中历恐,重啟的時候可以再次加載進行使用,而Memecache把數(shù)據(jù)全部存在內存之中。

集群模式:memcached沒有原生的集群模式专筷,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù)弱贼;但是 redis 目前是原生支持 cluster 模式的.

Memcached是多線程,非阻塞IO復用的網(wǎng)絡模型磷蛹;Redis使用單線程的多路 IO 復用模型

(2)redis的線程模型


1)文件事件處理器

redis基于reactor模式開發(fā)了網(wǎng)絡事件處理器吮旅,這個處理器叫做文件事件處理器,file event handler味咳。這個文件事件處理器庇勃,是單線程的,redis才叫做單線程的模型槽驶,采用IO多路復用機制同時監(jiān)聽多個socket责嚷,根據(jù)socket上的事件來選擇對應的事件處理器來處理這個事件。


如果被監(jiān)聽的socket準備好執(zhí)行accept掂铐、read罕拂、write、close等操作的時候全陨,跟操作對應的文件事件就會產(chǎn)生爆班,這個時候文件事件處理器就會調用之前關聯(lián)好的事件處理器來處理這個事件。


文件事件處理器是單線程模式運行的辱姨,但是通過IO多路復用機制監(jiān)聽多個socket柿菩,可以實現(xiàn)高性能的網(wǎng)絡通信模型,又可以跟內部其他單線程的模塊進行對接雨涛,保證了redis內部的線程模型的簡單性枢舶。


文件事件處理器的結構包含4個部分:多個socket懦胞,IO多路復用程序,文件事件分派器祟辟,事件處理器(命令請求處理器医瘫、命令回復處理器、連接應答處理器旧困,等等)醇份。


多個socket可能并發(fā)的產(chǎn)生不同的操作,每個操作對應不同的文件事件吼具,但是IO多路復用程序會監(jiān)聽多個socket僚纷,但是會將socket放入一個隊列中排隊,每次從隊列中取出一個socket給事件分派器拗盒,事件分派器把socket給對應的事件處理器怖竭。


然后一個socket的事件處理完之后,IO多路復用程序才會將隊列中的下一個socket給事件分派器陡蝇。文件事件分派器會根據(jù)每個socket當前產(chǎn)生的事件痊臭,來選擇對應的事件處理器來處理。


2)文件事件


當socket變得可讀時(比如客戶端對redis執(zhí)行write操作登夫,或者close操作)广匙,或者有新的可以應答的sccket出現(xiàn)時(客戶端對redis執(zhí)行connect操作),socket就會產(chǎn)生一個AE_READABLE事件恼策。


當socket變得可寫的時候(客戶端對redis執(zhí)行read操作)鸦致,socket會產(chǎn)生一個AE_WRITABLE事件。


IO多路復用程序可以同時監(jiān)聽AE_REABLE和AE_WRITABLE兩種事件涣楷,要是一個socket同時產(chǎn)生了AE_READABLE和AE_WRITABLE兩種事件分唾,那么文件事件分派器優(yōu)先處理AE_REABLE事件,然后才是AE_WRITABLE事件狮斗。


3)文件事件處理器


如果是客戶端要連接redis绽乔,那么會為socket關聯(lián)連接應答處理器

如果是客戶端要寫數(shù)據(jù)到redis,那么會為socket關聯(lián)命令請求處理器

如果是客戶端要從redis讀數(shù)據(jù)碳褒,那么會為socket關聯(lián)命令回復處理器


4)客戶端與redis通信的一次流程


在redis啟動初始化的時候迄汛,redis會將連接應答處理器跟AE_READABLE事件關聯(lián)起來,接著如果一個客戶端跟redis發(fā)起連接骤视,此時會產(chǎn)生一個AE_READABLE事件,然后由連接應答處理器來處理跟客戶端建立連接鹃觉,創(chuàng)建客戶端對應的socket专酗,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯(lián)起來。


當客戶端向redis發(fā)起請求的時候(不管是讀請求還是寫請求盗扇,都一樣)祷肯,首先就會在socket產(chǎn)生一個AE_READABLE事件沉填,然后由對應的命令請求處理器來處理。這個命令請求處理器就會從socket中讀取請求相關數(shù)據(jù)佑笋,然后進行執(zhí)行和處理翼闹。

接著redis這邊準備好了給客戶端的響應數(shù)據(jù)之后,就會將socket的AE_WRITABLE事件跟命令回復處理器關聯(lián)起來蒋纬,當客戶端這邊準備好讀取響應數(shù)據(jù)時猎荠,就會在socket上產(chǎn)生一個AE_WRITABLE事件,會由對應的命令回復處理器來處理蜀备,就是將準備好的響應數(shù)據(jù)寫入socket关摇,供客戶端來讀取。


命令回復處理器寫完之后碾阁,就會刪除這個socket的AE_WRITABLE事件和命令回復處理器的關聯(lián)關系输虱。


(3)為啥redis單線程模型也能效率這么高?


1)純內存操作

2)核心是基于非阻塞的IO多路復用機制

3)單線程反而避免了多線程的頻繁上下文切換問題(百度)


redis都有哪些數(shù)據(jù)類型脂凶?分別在哪些場景下使用比較合適宪睹?

(1)string

這是最基本的類型了,沒啥可說的蚕钦,就是普通的set和get亭病,做簡單的kv緩存

(2)hash

這個是類似map的一種結構,這個一般就是可以將結構化的數(shù)據(jù)冠桃,比如一個對象(前提是這個對象沒嵌套其他的對象)給緩存在redis里命贴,然后每次讀寫緩存的時候,可以就操作hash里的某個字段食听。

key=150

value={

??“id”: 150,

??“name”: “zhangsan”,

??“age”: 20

}

hash類的數(shù)據(jù)結構胸蛛,主要是用來存放一些對象,把一些簡單的對象給緩存起來樱报,后續(xù)操作的時候葬项,你可以直接僅僅修改這個對象中的某個字段的值

value={

??“id”: 150,

??“name”: “zhangsan”,

??“age”: 21

}


(3)list

有序列表,這個是可以玩兒出很多花樣的

微博迹蛤,某個大v的粉絲民珍,就可以以list的格式放在redis里去緩存

key=某大v

value=[zhangsan, lisi, wangwu]

比如可以通過list存儲一些列表型的數(shù)據(jù)結構,類似粉絲列表了盗飒、文章的評論列表了之類的東西

比如可以通過lrange命令嚷量,就是從某個元素開始讀取多少個元素,可以基于list實現(xiàn)分頁查詢逆趣,這個很棒的一個功能蝶溶,基于redis實現(xiàn)簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高抖所,就一頁一頁走

比如可以搞個簡單的消息隊列梨州,從list頭懟進去,從list尾巴那里弄出來

(4)set

無序集合田轧,自動去重

直接基于set將系統(tǒng)里需要去重的數(shù)據(jù)扔進去暴匠,自動就給去重了,如果你需要對一些數(shù)據(jù)進行快速的全局去重傻粘,你當然也可以基于jvm內存里的HashSet進行去重每窖,但是如果你的某個系統(tǒng)部署在多臺機器上呢?

得基于redis進行全局的set去重

可以基于set玩兒交集抹腿、并集岛请、差集的操作,比如交集吧警绩,可以把兩個人的粉絲列表整一個交集崇败,看看倆人的共同好友是誰?對吧

把兩個大v的粉絲都放在兩個set中肩祥,對兩個set做交集

(5)sorted set

排序的set后室,去重但是可以排序,寫進去的時候給一個分數(shù)混狠,自動根據(jù)分數(shù)排序岸霹,這個可以玩兒很多的花樣,最大的特點是有個分數(shù)可以自定義排序規(guī)則

比如說你要是想根據(jù)時間對數(shù)據(jù)排序将饺,那么可以寫入進去的時候用某個時間作為分數(shù)贡避,人家自動給你按照時間排序了

排行榜:將每個用戶以及其對應的什么分數(shù)寫入進去,zadd board score username予弧,接著zrevrange board 0 99刮吧,就可以獲取排名前100的用戶;zrank board username掖蛤,可以看到用戶在排行榜里的排名


redis的過期策略都有哪些杀捻?內存淘汰機制都有哪些?手寫一下LRU代碼實現(xiàn)蚓庭?

定期刪除+惰性刪除

所謂定期刪除致讥,指的是redis默認是每隔100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期器赞,如果過期就刪除垢袱。假設redis里放了10萬個key,都設置了過期時間港柜,你每隔幾百毫秒惶桐,就檢查10萬個key,那redis基本上就死了,cpu負載會很高的姚糊,消耗在你的檢查過期key上了。注意授舟,這里可不是每隔100ms就遍歷所有的設置過期時間的key救恨,那樣就是一場性能上的災難。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的释树。

但是問題是肠槽,定期刪除可能會導致很多過期key到了時間并沒有被刪除掉,那咋整呢奢啥?所以就是惰性刪除了秸仙。這就是說,在你獲取某個key的時候桩盲,redis會檢查一下 寂纪,這個key如果設置了過期時間那么是否過期了?如果過期了此時就會刪除赌结,不會給你返回任何東西捞蛋。

并不是key到時間就被刪除掉,而是你查詢這個key的時候柬姚,redis再懶惰的檢查一下

通過上述兩種手段結合起來拟杉,保證過期的key一定會被干掉。

很簡單量承,就是說搬设,你的過期key,靠定期刪除沒有被刪除掉撕捍,還停留在內存里拿穴,占用著你的內存呢,除非你的系統(tǒng)去查一下那個key卦洽,才會被redis給刪除掉贞言。

但是實際上這還是有問題的,如果定期刪除漏掉了很多過期key阀蒂,然后你也沒及時去查该窗,也就沒走惰性刪除,此時會怎么樣蚤霞?如果大量過期key堆積在內存里酗失,導致redis內存塊耗盡了,咋整昧绣?

答案是:走內存淘汰機制规肴。


(2)內存淘汰

如果redis的內存占用過多的時候,此時會進行內存淘汰,有如下一些策略:

redis 10個key拖刃,現(xiàn)在已經(jīng)滿了删壮,redis需要刪除掉5個key

1個key,最近1分鐘被查詢了100次

1個key兑牡,最近10分鐘被查詢了50次

1個key央碟,最近1個小時倍查詢了1次


1)noeviction:當內存不足以容納新寫入數(shù)據(jù)時,新寫入操作會報錯均函,這個一般沒人用吧亿虽,實在是太惡心了

2)allkeys-lru:當內存不足以容納新寫入數(shù)據(jù)時,在鍵空間中苞也,移除最近最少使用的key(這個是最常用的)

3)allkeys-random:當內存不足以容納新寫入數(shù)據(jù)時洛勉,在鍵空間中,隨機移除某個key如迟,這個一般沒人用吧收毫,為啥要隨機,肯定是把最近最少使用的key給干掉啊

4)volatile-lru:當內存不足以容納新寫入數(shù)據(jù)時氓涣,在設置了過期時間的鍵空間中牛哺,移除最近最少使用的key(這個一般不太合適)

5)volatile-random:當內存不足以容納新寫入數(shù)據(jù)時,在設置了過期時間的鍵空間中劳吠,隨機移除某個key

6)volatile-ttl:當內存不足以容納新寫入數(shù)據(jù)時引润,在設置了過期時間的鍵空間中,有更早過期時間的key優(yōu)先移除

要不你手寫一個LRU算法痒玩?


我確實有時會問這個淳附,因為有些候選人如果確實過五關斬六將,前面的問題都答的很好蠢古,那么其實讓他寫一下LRU算法奴曙,可以考察一下編碼功底

你可以現(xiàn)場手寫最原始的LRU算法,那個代碼量太大了草讶,我覺得不太現(xiàn)實

public class LRUCache<K, V> extends LinkedHashMap<K, V> {

private final int CACHE_SIZE;

//這里就是傳遞進來最多能緩存多少數(shù)據(jù)

????public LRUCache(int cacheSize) {

????????super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);//這塊就是設置一個hashmap的初始大小洽糟,同時最后一個true指的是讓linkedhashmap按照訪問順序來進行排序,最近訪問的放在頭堕战,最老訪問的就在尾

????????CACHE_SIZE = cacheSize;

????}

????@Override

????protected boolean removeEldestEntry(Map.Entry eldest) {

????????return size() > CACHE_SIZE;//這個意思就是說當map中的數(shù)據(jù)量大于指定的緩存?zhèn)€數(shù)的時候坤溃,就自動刪除最老的數(shù)據(jù)

????}

}

我給你看上面的代碼,是告訴你最起碼你也得寫出來上面那種代碼嘱丢,不求自己純手工從底層開始打造出自己的LRU薪介,但是起碼知道如何利用已有的jdk數(shù)據(jù)結構實現(xiàn)一個java版的LRU




?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市越驻,隨后出現(xiàn)的幾起案子汁政,更是在濱河造成了極大的恐慌道偷,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件记劈,死亡現(xiàn)場離奇詭異勺鸦,居然都是意外死亡,警方通過查閱死者的電腦和手機目木,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門祝旷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘶窄,你說我怎么就攤上這事【啻” “怎么了柄冲?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忠蝗。 經(jīng)常有香客問我现横,道長,這世上最難降的妖魔是什么阁最? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任戒祠,我火速辦了婚禮,結果婚禮上速种,老公的妹妹穿的比我還像新娘姜盈。我一直安慰自己,他們只是感情好配阵,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布馏颂。 她就那樣靜靜地躺著,像睡著了一般棋傍。 火紅的嫁衣襯著肌膚如雪救拉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天瘫拣,我揣著相機與錄音亿絮,去河邊找鬼。 笑死麸拄,一個胖子當著我的面吹牛派昧,可吹牛的內容都是我干的。 我是一名探鬼主播感帅,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼斗锭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了失球?” 一聲冷哼從身側響起岖是,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤帮毁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后豺撑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烈疚,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年聪轿,在試婚紗的時候發(fā)現(xiàn)自己被綠了爷肝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡陆错,死狀恐怖灯抛,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情音瓷,我是刑警寧澤对嚼,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站绳慎,受9級特大地震影響纵竖,放射性物質發(fā)生泄漏。R本人自食惡果不足惜杏愤,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一靡砌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧珊楼,春花似錦通殃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至媳瞪,卻和暖如春骗炉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛇受。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工句葵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兢仰。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓乍丈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親把将。 傳聞我的和親對象是個殘疾皇子轻专,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容