Redis:從應(yīng)用到底層惠呼,一文幫你搞定

高清思維導(dǎo)圖已同步Git:https://github.com/SoWhat1412/xmindfile,關(guān)注公眾號sowhat1412獲取海量資源

圖片

總感覺哪里不對灌诅,但是又說不上來

1蒜焊、基本類型及底層實現(xiàn)

圖片

1.1、String

用途:

適用于簡單key-value存儲境蔼、setnx key value實現(xiàn)分布式鎖、計數(shù)器(原子性)伺通、分布式全局唯一ID。

底層:C語言中String用char[]數(shù)組表示逢享,源碼中用SDS(simple dynamic string)封裝char[]罐监,這是是Redis存儲的最小單元,一個SDS最大可以存儲512M信息瞒爬。

<pre data-tool="mdnice編輯器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(63, 63, 63); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">struct sdshdr{ unsigned int len; // 標(biāo)記char[]的長度 unsigned int free; //標(biāo)記char[]中未使用的元素個數(shù) char buf[]; // 存放元素的坑 } </pre>

Redis對SDS再次封裝生成了RedisObject弓柱,核心有兩個作用:

  1. 說明是5種類型哪一種。
  2. 里面有指針用來指向 SDS侧但。

當(dāng)你執(zhí)行set name sowhat的時候矢空,其實Redis會創(chuàng)建兩個RedisObject對象,鍵的RedisObject 和 值的RedisOjbect 其中它們type = REDIS_STRING禀横,而SDS分別存儲的就是 name 跟 sowhat 字符串咯屁药。

并且Redis底層對SDS有如下優(yōu)化:

  1. SDS修改后大小 > 1M時 系統(tǒng)會多分配空間來進(jìn)行空間預(yù)分配
  2. SDS是惰性釋放空間的柏锄,你free了空間酿箭,可是系統(tǒng)把數(shù)據(jù)記錄下來下次想用時候可直接使用复亏。不用新申請空間。

1.2缭嫡、List

圖片

查看源碼底層 adlist.h 會發(fā)現(xiàn)底層就是個 雙端鏈表缔御,該鏈表最大長度為2^32-1。常用就這幾個組合妇蛀。

lpush + lpop = stack 先進(jìn)后出的棧

lpush + rpop = queue 先進(jìn)先出的隊列

lpush + ltrim = capped collection 有限集合

lpush + brpop = message queue 消息隊列

一般可以用來做簡單的消息隊列耕突,并且當(dāng)數(shù)據(jù)量小的時候可能用到獨(dú)有的壓縮列表來提升性能。當(dāng)然專業(yè)點還是要 RabbitMQ评架、ActiveMQ等

1.3眷茁、Hash

散列非常適用于將一些相關(guān)的數(shù)據(jù)存儲在一起,比如用戶的購物車古程。該類型在日常用途還是挺多的蔼卡。

這里需要明確一點:Redis中只有一個K,一個V挣磨。其中 K 絕對是字符串對象雇逞,而 V 可以是String、List茁裙、Hash塘砸、Set、ZSet任意一種晤锥。

hash的底層主要是采用字典dict的結(jié)構(gòu)掉蔬,整體呈現(xiàn)層層封裝。從小到大如下:

1.3.1矾瘾、dictEntry

真正的數(shù)據(jù)節(jié)點女轿,包括key、value 和 next 節(jié)點壕翩。

圖片

1.3.2蛉迹、dictht

1、數(shù)據(jù) dictEntry 類型的數(shù)組放妈,每個數(shù)組的item可能都指向一個鏈表北救。

2、數(shù)組長度 size芜抒。

3珍策、sizemask 等于 size - 1。

4宅倒、當(dāng)前 dictEntry 數(shù)組中包含總共多少節(jié)點攘宙。

圖片
1.3.3、dict

1、dictType 類型模聋,包括一些自定義函數(shù)肩民,這些函數(shù)使得key和value能夠存儲

2、rehashidx 其實是一個標(biāo)志量链方,如果為-1說明當(dāng)前沒有擴(kuò)容持痰,如果不為 -1 則記錄擴(kuò)容位置。

3祟蚀、dictht數(shù)組工窍,兩個Hash表。

4前酿、iterators 記錄了當(dāng)前字典正在進(jìn)行中的迭代器

圖片

組合后結(jié)構(gòu)就是如下

圖片

1.3.4患雏、漸進(jìn)式擴(kuò)容

為什么 dictht ht[2]是兩個呢?目的是在擴(kuò)容的同時不影響前端的CURD罢维,慢慢的把數(shù)據(jù)從ht[0]轉(zhuǎn)移到ht[1]中淹仑,同時rehashindex來記錄轉(zhuǎn)移的情況,當(dāng)全部轉(zhuǎn)移完成肺孵,將ht[1]改成ht[0]使用匀借。

rehashidx = -1說明當(dāng)前沒有擴(kuò)容,rehashidx != -1則表示擴(kuò)容到數(shù)組中的第幾個了平窘。

擴(kuò)容之后的數(shù)組大小為大于used2的2的n次方*的最小值吓肋,跟 HashMap 類似。然后挨個遍歷數(shù)組同時調(diào)整rehashidx的值瑰艘,對每個dictEntry[i] 再挨個遍歷鏈表將數(shù)據(jù) Hash 后重新映射到 dictht[1]里面是鬼。并且 dictht[0].usedictht[1].use 是動態(tài)變化的。

圖片

整個過程的重點在于rehashidx紫新,其為第一個數(shù)組正在移動的下標(biāo)位置均蜜,如果當(dāng)前內(nèi)存不夠,或者操作系統(tǒng)繁忙芒率,擴(kuò)容的過程可以隨時停止兆龙。

停止之后如果對該對象進(jìn)行操作,那是什么樣子的呢敲董?

1、如果是新增慰安,則直接新增后第二個數(shù)組腋寨,因為如果新增到第一個數(shù)組,以后還是要移過來化焕,沒必要浪費(fèi)時間

2萄窜、如果是刪除,更新,查詢查刻,則先查找第一個數(shù)組键兜,如果沒找到,則再查詢第二個數(shù)組穗泵。

圖片

<figcaption style="margin: 5px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 12px; font-family: PingFangSC-Light;">
</figcaption>

1.4普气、Set

如果你明白Java中HashSet是HashMap的簡化版那么這個Set應(yīng)該也理解了。都是一樣的套路而已佃延。這里你可以認(rèn)為是沒有Value的Dict现诀。看源碼 t.set.c 就可以了解本質(zhì)了履肃。

<pre data-tool="mdnice編輯器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(63, 63, 63); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">int setTypeAdd(robj *subject, robj *value) { long long llval; if (subject->encoding == REDIS_ENCODING_HT) { // 看到底層調(diào)用的還是dictAdd仔沿,只不過第三個參數(shù)= NULL if (dictAdd(subject->ptr,value,NULL) == DICT_OK) { incrRefCount(value); return 1; } .... </pre>

1.5、ZSet

范圍查找 的天敵就是 有序集合尺棋,看底層 redis.h 后就會發(fā)現(xiàn) Zset用的就是可以跟二叉樹媲美的跳躍表來實現(xiàn)有序封锉。跳表就是多層鏈表的結(jié)合體,跳表分為許多層(level)膘螟,每一層都可以看作是數(shù)據(jù)的索引成福,這些索引的意義就是加快跳表查找數(shù)據(jù)速度

每一層的數(shù)據(jù)都是有序的萍鲸,上一層數(shù)據(jù)是下一層數(shù)據(jù)的子集闷叉,并且第一層(level 1)包含了全部的數(shù)據(jù);層次越高脊阴,跳躍性越大握侧,包含的數(shù)據(jù)越少。并且隨便插入一個數(shù)據(jù)該數(shù)據(jù)是否會是跳表索引完全隨機(jī)的跟玩骰子一樣嘿期。

跳表包含一個表頭品擎,它查找數(shù)據(jù)時,是從上往下备徐,從左往右進(jìn)行查找√汛現(xiàn)在找出值為37的節(jié)點為例,來對比說明跳表和普遍的鏈表蜜猾。

  1. 沒有跳表查詢 比如我查詢數(shù)據(jù)37秀菱,如果沒有上面的索引時候路線如下圖:
    圖片
  2. 有跳表查詢 有跳表查詢37的時候路線如下圖:
    圖片

    應(yīng)用場景:

積分排行榜、時間排序新聞蹭睡、延時隊列衍菱。

1.6、Redis Geo

以前寫過Redis Geo核心原理解析肩豁,想看的直接跳轉(zhuǎn)即可脊串。他的核心思想就是將地球近似為球體來看待辫呻,然后 GEO利用 GeoHash 將二維的經(jīng)緯度轉(zhuǎn)換成字符串偎血,來實現(xiàn)位置的劃分跟指定距離的查詢隘擎。

圖片

1.7刽宪、HyperLogLog

HyperLogLog :是一種概率數(shù)據(jù)結(jié)構(gòu)悬嗓,它使用概率算法來統(tǒng)計集合的近似基數(shù)份招。而它算法的最本源則是伯努利過程 + 分桶 + 調(diào)和平均數(shù)琅催。具體實現(xiàn)可看 HyperLogLog 講解虱肄。

功能:誤差允許范圍內(nèi)做基數(shù)統(tǒng)計 (基數(shù)就是指一個集合中不同值的個數(shù)) 的時候非常有用矩距,每個HyperLogLog的鍵可以計算接近2^64不同元素的基數(shù)念赶,而大小只需要12KB础钠。錯誤率大概在0.81%。所以如果用做 UV 統(tǒng)計很合適叉谜。

HyperLogLog底層 一共分了 2^14 個桶旗吁,也就是 16384 個桶。每個(registers)桶中是一個 6 bit 的數(shù)組停局,這里有個騷操作就是一般人可能直接用一個字節(jié)當(dāng)桶浪費(fèi)2個bit空間很钓,但是Redis底層只用6個然后通過前后拼接實現(xiàn)對內(nèi)存用到了極致,最終就是 16384*6/8/1024 = 12KB董栽。

1.8码倦、bitmap

BitMap 原本的含義是用一個比特位來映射某個元素的狀態(tài)。由于一個比特位只能表示 0 和 1 兩種狀態(tài)锭碳,所以 BitMap 能映射的狀態(tài)有限袁稽,但是使用比特位的優(yōu)勢是能大量的節(jié)省內(nèi)存空間。

在 Redis 中BitMap 底層是基于字符串類型實現(xiàn)的擒抛,可以把 Bitmaps 想象成一個以比特位為單位的數(shù)組推汽,數(shù)組的每個單元只能存儲0和1,數(shù)組的下標(biāo)在 Bitmaps 中叫做偏移量歧沪,BitMap 的 offset 值上限 2^32 - 1歹撒。

圖片

  1. 用戶簽到

key = 年份:用戶id offset = (今天是一年中的第幾天) % (今年的天數(shù))

  1. 統(tǒng)計活躍用戶

使用日期作為 key,然后用戶 id 為 offset 設(shè)置不同offset為0 1 即可诊胞。

PS : Redis 它的通訊協(xié)議是基于TCP的應(yīng)用層協(xié)議 RESP(REdis Serialization Protocol)暖夭。

1.9、Bloom Filter

使用布隆過濾器得到的判斷結(jié)果:不存在的一定不存在撵孤,存在的不一定存在迈着。

布隆過濾器 原理:

當(dāng)一個元素被加入集合時,通過K個散列函數(shù)將這個元素映射成一個位數(shù)組中的K個點(有效降低沖突概率)邪码,把它們置為1寥假。檢索時,我們只要看看這些點是不是都是1就知道集合中有沒有它了:如果這些點有任何一個為0霞扬,則被檢元素一定不在;如果都是1,則被檢元素很可能在喻圃。這就是布隆過濾器的基本思想萤彩。

想玩的話可以用Google的guava包玩耍一番。

圖片

1.10 發(fā)布訂閱

redis提供了發(fā)布斧拍、訂閱模式的消息機(jī)制雀扶,其中消息訂閱者與發(fā)布者不直接通信,發(fā)布者向指定的頻道(channel)發(fā)布消息肆汹,訂閱該頻道的每個客戶端都可以接收到消息愚墓。不過比專業(yè)的MQ(RabbitMQ RocketMQ ActiveMQ Kafka)相比不值一提,這個功能就算球了昂勉。

圖片

2浪册、持久化

因為Redis數(shù)據(jù)在內(nèi)存,斷電既丟岗照,因此持久化到磁盤是必須得有的村象,Redis提供了RDB跟AOF兩種模式。

2.1攒至、RDB

RDB 持久化機(jī)制厚者,是對 Redis 中的數(shù)據(jù)執(zhí)行周期性的持久化。更適合做冷備迫吐。優(yōu)點:

1库菲、壓縮后的二進(jìn)制文,適用于備份志膀、全量復(fù)制熙宇,用于災(zāi)難恢復(fù)加載RDB恢復(fù)數(shù)據(jù)遠(yuǎn)快于AOF方式,適合大規(guī)模的數(shù)據(jù)恢復(fù)梧却。

2奇颠、如果業(yè)務(wù)對數(shù)據(jù)完整性和一致性要求不高,RDB是很好的選擇放航。數(shù)據(jù)恢復(fù)比AOF快烈拒。

缺點:

1、RDB是周期間隔性的快照文件广鳍,數(shù)據(jù)的完整性和一致性不高荆几,因為RDB可能在最后一次備份時宕機(jī)了。

2赊时、備份時占用內(nèi)存吨铸,因為Redis 在備份時會獨(dú)立fork一個子進(jìn)程祖秒,將數(shù)據(jù)寫入到一個臨時文件(此時內(nèi)存中的數(shù)據(jù)是原來的兩倍哦)诞吱,最后再將臨時文件替換之前的備份文件舟奠。所以要考慮到大概兩倍的數(shù)據(jù)膨脹性。

注意手動觸發(fā)及COW:

1房维、SAVE 直接調(diào)用 rdbSave 沼瘫,阻塞 Redis 主進(jìn)程,導(dǎo)致無法提供服務(wù)咙俩。2耿戚、BGSAVE 則 fork 出一個子進(jìn)程,子進(jìn)程負(fù)責(zé)調(diào)用 rdbSave 阿趁,在保存完成后向主進(jìn)程發(fā)送信號告知完成膜蛔。在BGSAVE 執(zhí)行期間仍可以繼續(xù)處理客戶端的請求

3脖阵、Copy On Write 機(jī)制皂股,備份的是開始那個時刻內(nèi)存中的數(shù)據(jù),只復(fù)制被修改內(nèi)存頁數(shù)據(jù)独撇,不是全部內(nèi)存數(shù)據(jù)屑墨。

4、Copy On Write 時如果父子進(jìn)程大量寫操作會導(dǎo)致分頁錯誤纷铣。

圖片

<figcaption style="margin: 5px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 12px; font-family: PingFangSC-Light;">
</figcaption>

2.2卵史、AOF

AOF 機(jī)制對每條寫入命令作為日志,以 append-only 的模式寫入一個日志文件中搜立,因為這個模式是只追加的方式以躯,所以沒有任何磁盤尋址的開銷,所以很快啄踊,有點像 Mysql 中的binlog忧设。AOF更適合做熱備。

優(yōu)點:

AOF是一秒一次去通過一個后臺的線程fsync操作颠通,數(shù)據(jù)丟失不用怕址晕。

缺點:

1、對于相同數(shù)量的數(shù)據(jù)集而言顿锰,AOF文件通常要大于RDB文件谨垃。RDB 在恢復(fù)大數(shù)據(jù)集時的速度比 AOF 的恢復(fù)速度要快。

2硼控、根據(jù)同步策略的不同刘陶,AOF在運(yùn)行效率上往往會慢于RDB±魏常總之匙隔,每秒同步策略的效率是比較高的。

AOF整個流程分兩步:第一步是命令的實時寫入熏版,不同級別可能有1秒數(shù)據(jù)損失纷责。命令先追加到aof_buf然后再同步到AO磁盤捍掺,如果實時寫入磁盤會帶來非常高的磁盤IO,影響整體性能碰逸。

第二步是對aof文件的重寫乡小,目的是為了減少AOF文件的大小,可以自動觸發(fā)或者手動觸發(fā)(BGREWRITEAOF)饵史,是Fork出子進(jìn)程操作,期間Redis服務(wù)仍可用胜榔。

圖片

1胳喷、在重寫期間,由于主進(jìn)程依然在響應(yīng)命令夭织,為了保證最終備份的完整性吭露;它依然會寫入舊的AOF中,如果重寫失敗尊惰,能夠保證數(shù)據(jù)不丟失讲竿。

2、為了把重寫期間響應(yīng)的寫入信息也寫入到新的文件中弄屡,因此也會為子進(jìn)程保留一個buf题禀,防止新寫的file丟失數(shù)據(jù)。

3膀捷、重寫是直接把當(dāng)前內(nèi)存的數(shù)據(jù)生成對應(yīng)命令迈嘹,并不需要讀取老的AOF文件進(jìn)行分析、命令合并全庸。

4秀仲、無論是 RDB 還是 AOF 都是先寫入一個臨時文件,然后通過rename完成文件的替換工作壶笼。

關(guān)于Fork的建議:

1神僵、降低fork的頻率,比如可以手動來觸發(fā)RDB生成快照覆劈、與AOF重寫保礼;

2、控制Redis最大使用內(nèi)存墩崩,防止fork耗時過長氓英;

3、配置牛逼點鹦筹,合理配置Linux的內(nèi)存分配策略铝阐,避免因為物理內(nèi)存不足導(dǎo)致fork失敗。

4铐拐、Redis在執(zhí)行BGSAVEBGREWRITEAOF命令時徘键,哈希表的負(fù)載因子>=5练对,而未執(zhí)行這兩個命令時>=1。目的是盡量減少寫操作吹害,避免不必要的內(nèi)存寫入操作螟凭。

5、哈希表的擴(kuò)展因子:哈希表已保存節(jié)點數(shù)量 / 哈希表大小它呀。因子決定了是否擴(kuò)展哈希表螺男。

2.3、恢復(fù)

啟動時會先檢查AOF(數(shù)據(jù)更完整)文件是否存在纵穿,如果不存在就嘗試加載RDB下隧。
圖片

2.4、建議

既然單獨(dú)用RDB會丟失很多數(shù)據(jù)谓媒。單獨(dú)用AOF淆院,數(shù)據(jù)恢復(fù)沒RDB來的快,所以出現(xiàn)問題了第一時間用RDB恢復(fù)句惯,然后AOF做數(shù)據(jù)補(bǔ)全才說王道土辩。

3、Redis為什么那么快

3.1抢野、 基于內(nèi)存實現(xiàn):

數(shù)據(jù)都存儲在內(nèi)存里拷淘,相比磁盤IO操作快百倍,操作速率很快蒙保。

3.2辕棚、高效的數(shù)據(jù)結(jié)構(gòu):

Redis底層多種數(shù)據(jù)結(jié)構(gòu)支持不同的數(shù)據(jù)類型,比如HyperLogLog它連2個字節(jié)都不想浪費(fèi)邓厕。

3.3逝嚎、豐富而合理的編碼:

Redis底層提供了 豐富而合理的編碼 ,五種數(shù)據(jù)類型根據(jù)長度及元素的個數(shù)適配不同的編碼格式详恼。

1补君、String:自動存儲int類型,非int類型用raw編碼昧互。

2挽铁、List:字符串長度且元素個數(shù)小于一定范圍使用 ziplist 編碼,否則轉(zhuǎn)化為 linkedlist 編碼敞掘。

3叽掘、Hash:hash 對象保存的鍵值對內(nèi)的鍵和值字符串長度小于一定值及鍵值對。

4玖雁、Set:保存元素為整數(shù)及元素個數(shù)小于一定范圍使用 intset 編碼更扁,任意條件不滿足,則使用 hashtable 編碼。

5浓镜、Zset:保存的元素個數(shù)小于定值且成員長度小于定值使用 ziplist 編碼溃列,任意條件不滿足,則使用 skiplist 編碼膛薛。

3.4听隐、合適的線程模型:

I/O 多路復(fù)用模型同時監(jiān)聽客戶端連接,多線程是需要上下文切換的哄啄,對于內(nèi)存數(shù)據(jù)庫來說這點很致命雅任。

圖片

3.5、 Redis6.0后引入多線程提速:

要知道 讀寫網(wǎng)絡(luò)的read/write系統(tǒng)耗時 >> Redis運(yùn)行執(zhí)行耗時咨跌,Redis的瓶頸主要在于網(wǎng)絡(luò)的 IO 消耗, 優(yōu)化主要有兩個方向:

1椿访、提高網(wǎng)絡(luò) IO 性能,典型的實現(xiàn)比如使用 DPDK 來替代內(nèi)核網(wǎng)絡(luò)棧的方式

2虑润、使用多線程充分利用多核,典型的實現(xiàn)比如 Memcached加酵。

協(xié)議棧優(yōu)化的這種方式跟 Redis 關(guān)系不大拳喻,支持多線程是一種最有效最便捷的操作方式。所以Redis支持多線程主要就是兩個原因:

1猪腕、可以充分利用服務(wù)器 CPU 資源冗澈,目前主線程只能利用一個核

2、多線程任務(wù)可以分?jǐn)?Redis 同步 IO 讀寫負(fù)荷

關(guān)于多線程須知:

  1. Redis 6.0 版本 默認(rèn)多線程是關(guān)閉的 io-threads-do-reads no
  2. Redis 6.0 版本 開啟多線程后 線程數(shù)也要 謹(jǐn)慎設(shè)置陋葡。
  3. 多線程可以使得性能翻倍亚亲,但是多線程只是用來處理網(wǎng)絡(luò)數(shù)據(jù)的讀寫和協(xié)議解析,執(zhí)行命令仍然是單線程順序執(zhí)行腐缤。

4捌归、常見問題

4.1、緩存雪崩

圖片

雪崩定義:

Redis中大批量key在同一時間同時失效導(dǎo)致所有請求都打到了MySQL岭粤。而MySQL扛不住導(dǎo)致大面積崩塌惜索。

雪崩解決方案:

1、緩存數(shù)據(jù)的過期時間加上個隨機(jī)值剃浇,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生巾兆。

2、如果緩存數(shù)據(jù)庫是分布式部署虎囚,將熱點數(shù)據(jù)均勻分布在不同搞得緩存數(shù)據(jù)庫中角塑。

3、設(shè)置熱點數(shù)據(jù)永遠(yuǎn)不過期淘讥。

4.2圃伶、緩存穿透

穿透定義:

緩存穿透 是 指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),比如ID默認(rèn)>0,黑客一直 請求ID= -12的數(shù)據(jù)那么就會導(dǎo)致數(shù)據(jù)庫壓力過大留攒,嚴(yán)重會擊垮數(shù)據(jù)庫煤惩。

穿透解決方案:

1、后端接口層增加 用戶鑒權(quán)校驗炼邀,參數(shù)做校驗等魄揉。

2、單個IP每秒訪問次數(shù)超過閾值直接拉黑IP拭宁,關(guān)進(jìn)小黑屋1天洛退,在獲取IP代理池的時候我就被拉黑過。

3杰标、從緩存取不到的數(shù)據(jù)兵怯,在數(shù)據(jù)庫中也沒有取到,這時也可以將key-value對寫為key-null 失效時間可以為15秒防止惡意攻擊腔剂。

4媒区、用Redis提供的 Bloom Filter 特性也OK。

4.3掸犬、緩存擊穿

圖片

<figcaption style="margin: 5px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 12px; font-family: PingFangSC-Light;">
</figcaption>

擊穿定義:

現(xiàn)象:大并發(fā)集中對這一個熱點key進(jìn)行訪問袜漩,當(dāng)這個Key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存湾碎,直接請求數(shù)據(jù)庫宙攻。

擊穿解決:

設(shè)置熱點數(shù)據(jù)永遠(yuǎn)不過期 加上互斥鎖也能搞定了

4.4、雙寫一致性

雙寫:緩存數(shù)據(jù)庫均更新數(shù)據(jù)介褥,如何保證數(shù)據(jù)一致性座掘?

1、先更新數(shù)據(jù)庫柔滔,再更新緩存

安全問題:線程A更新數(shù)據(jù)庫->線程B更新數(shù)據(jù)庫->線程B更新緩存->線程A更新緩存溢陪。導(dǎo)致臟讀

業(yè)務(wù)場景:讀少寫多場景廊遍,頻繁更新數(shù)據(jù)庫而緩存根本沒用嬉愧。更何況如果緩存是疊加計算后結(jié)果更浪費(fèi)性能

2喉前、先刪緩存没酣,再更新數(shù)據(jù)庫

A 請求寫來更新緩存。

B 發(fā)現(xiàn)緩存不在去數(shù)據(jù)查詢舊值后寫入緩存卵迂。

A 將數(shù)據(jù)寫入數(shù)據(jù)庫裕便,此時緩存跟數(shù)據(jù)庫不一致

因此 FackBook 提出了 Cache Aside Pattern

失效:應(yīng)用程序先從cache取數(shù)據(jù)见咒,沒有得到偿衰,則從數(shù)據(jù)庫中取數(shù)據(jù),成功后,放到緩存中下翎。

命中:應(yīng)用程序從cache中取數(shù)據(jù)缤言,取到后返回。

更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中视事,成功后胆萧,再讓緩存失效

4.5俐东、腦裂

圖片

腦裂是指因為網(wǎng)絡(luò)原因跌穗,導(dǎo)致master節(jié)點、slave節(jié)點 和 sentinel集群處于不用的網(wǎng)絡(luò)分區(qū)虏辫,此時因為sentinel集群無法感知到master的存在蚌吸,所以將slave節(jié)點提升為master節(jié)點 此時存在兩個不同的master節(jié)點就像一個大腦分裂成了兩個。其實在Hadoop 砌庄、Spark集群中都會出現(xiàn)這樣的情況羹唠,只是解決方法不同而已(用ZK配合強(qiáng)制殺死)。

集群腦裂問題中娄昆,如果客戶端還在基于原來的master節(jié)點繼續(xù)寫入數(shù)據(jù)那么新的master節(jié)點將無法同步這些數(shù)據(jù)肉迫,當(dāng)網(wǎng)絡(luò)問題解決后sentinel集群將原先的master節(jié)點降為slave節(jié)點,此時再從新的master中同步數(shù)據(jù)將造成大量的數(shù)據(jù)丟失稿黄。

Redis處理方案是redis的配置文件中存在兩個參數(shù)

<pre data-tool="mdnice編輯器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(63, 63, 63); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">min-replicas-to-write 3 表示連接到master的最少slave數(shù)量 min-replicas-max-lag 10 表示slave連接到master的最大延遲時間 </pre>

如果連接到master的slave數(shù)量 < 第一個參數(shù) 且 ping的延遲時間 <= 第二個參數(shù)那么master就會拒絕寫請求,配置了這兩個參數(shù)后如果發(fā)生了集群腦裂則原先的master節(jié)點接收到客戶端的寫入請求會拒絕就可以減少數(shù)據(jù)同步之后的數(shù)據(jù)丟失跌造。

4.6杆怕、事務(wù)

MySQL 中的事務(wù)還是挺多道道的還要,而在Redis中的事務(wù)只要有如下三步:

圖片

關(guān)于事務(wù)具體結(jié)論:

1壳贪、redis事務(wù)就是一次性陵珍、順序性、排他性的執(zhí)行一個隊列中的一系列命令违施。

2互纯、Redis事務(wù)沒有隔離級別的概念:批量操作在發(fā)送 EXEC 命令前被放入隊列緩存,并不會被實際執(zhí)行磕蒲,也就不存在事務(wù)內(nèi)的查詢要看到事務(wù)里的更新留潦,事務(wù)外查詢不能看到

3辣往、Redis不保證原子性:Redis中單條命令是原子性執(zhí)行的兔院,但事務(wù)不保證原子性。

4站削、Redis編譯型錯誤事務(wù)中所有代碼均不執(zhí)行坊萝,指令使用錯誤。運(yùn)行時異常是錯誤命令導(dǎo)致異常,其他命令可正常執(zhí)行十偶。

5菩鲜、watch指令類似于樂觀鎖,在事務(wù)提交時惦积,如果watch監(jiān)控的多個KEY中任何KEY的值已經(jīng)被其他客戶端更改接校,則使用EXEC執(zhí)行事務(wù)時,事務(wù)隊列將不會被執(zhí)行荣刑。

4.7馅笙、正確開發(fā)步驟

上線前:Redis 高可用,主從+哨兵厉亏,Redis cluster董习,避免全盤崩潰。

上線時:本地 ehcache 緩存 + Hystrix 限流 + 降級爱只,避免MySQL扛不住皿淋。上線后:Redis 持久化采用 RDB + AOF 來保證斷點后自動從磁盤上加載數(shù)據(jù),快速恢復(fù)緩存數(shù)據(jù)恬试。

5窝趣、分布式鎖

日常開發(fā)中我們可以用 synchronizedLock 實現(xiàn)并發(fā)編程训柴。但是Java中的鎖只能保證在同一個JVM進(jìn)程內(nèi)中執(zhí)行哑舒。如果在分布式集群環(huán)境下用鎖呢?日常一般有兩種選擇方案幻馁。

5.1洗鸵、 Zookeeper實現(xiàn)分布式鎖

你需要知道一點基本zookeeper知識:

1、持久節(jié)點:客戶端斷開連接zk不刪除persistent類型節(jié)點 2仗嗦、臨時節(jié)點:客戶端斷開連接zk刪除ephemeral類型節(jié)點 3膘滨、順序節(jié)點:節(jié)點后面會自動生成類似0000001的數(shù)字表示順序 4、節(jié)點變化的通知:客戶端注冊了監(jiān)聽節(jié)點變化的時候稀拐,會調(diào)用回調(diào)方法

大致流程如下火邓,其中注意每個節(jié)點監(jiān)控它前面那個節(jié)點狀態(tài),從而避免羊群效應(yīng)德撬。關(guān)于模板代碼百度即可铲咨。

圖片

缺點:

頻繁的創(chuàng)建刪除節(jié)點,加上注冊watch事件蜓洪,對于zookeeper集群的壓力比較大鸣驱,性能也比不上Redis實現(xiàn)的分布式鎖。

5.2蝠咆、 Redis實現(xiàn)分布式鎖

本身原理也比較簡單踊东,Redis 自身就是一個單線程處理器北滥,具備互斥的特性,通過setNX闸翅,exist等命令就可以完成簡單的分布式鎖再芋,處理好超時釋放鎖的邏輯即可。

SETNX

SETNX 是SET if Not eXists的簡寫坚冀,日常指令是SETNX key value济赎,如果 key 不存在則set成功返回 1,如果這個key已經(jīng)存在了返回0。

SETEX

SETEX key seconds value 表達(dá)的意思是 將值 value 關(guān)聯(lián)到 key ,并將 key 的生存時間設(shè)為多少秒耘子。如果 key 已經(jīng)存在,setex命令將覆寫舊值壳猜。并且 setex是一個原子性(atomic)操作。

加鎖:

一般就是用一個標(biāo)識唯一性的字符串比如UUID 配合 SETNX 實現(xiàn)加鎖滑凉。

解鎖:

這里用到了LUA腳本统扳,LUA可以保證是原子性的,思路就是判斷一下Key和入?yún)⑹欠裣嗟瘸╂ⅲ堑脑捑蛣h除咒钟,返回成功1,0就是失敗若未。

缺點:

這個鎖是無法重入的朱嘴,且自己實心的話各種邊邊角角都要考慮到,所以了解個大致思路流程即可粗合,工程化還是用開源工具包就行腕够。

5.3、 Redisson實現(xiàn)分布式鎖

Redisson 是在Redis基礎(chǔ)上的一個服務(wù)舌劳,采用了基于NIO的Netty框架,不僅能作為Redis底層驅(qū)動客戶端玫荣,還能將原生的RedisHash甚淡,List,Set捅厂,String贯卦,Geo,HyperLogLog等數(shù)據(jù)結(jié)構(gòu)封裝為Java里大家最熟悉的映射(Map)焙贷,列表(List)撵割,集(Set),通用對象桶(Object Bucket)辙芍,地理空間對象桶(Geospatial Bucket)啡彬,基數(shù)估計算法(HyperLogLog)等結(jié)構(gòu)羹与。

這里我們只是用到了關(guān)于分布式鎖的幾個指令,他的大致底層原理:
圖片

Redisson加鎖解鎖 大致流程圖如下:

圖片

6庶灿、Redis 過期策略和內(nèi)存淘汰策略

6.1纵搁、Redis的過期策略

Redis中 過期策略 通常有以下三種:

1、定時過期

每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器往踢,到過期時間就會立即對key進(jìn)行清除腾誉。該策略可以立即清除過期的數(shù)據(jù),對內(nèi)存很友好峻呕;但是會占用大量的CPU資源去處理過期的數(shù)據(jù)利职,從而影響緩存的響應(yīng)時間和吞吐量。

2瘦癌、惰性過期

只有當(dāng)訪問一個key時猪贪,才會判斷該key是否已過期,過期則清除佩憾。該策略可以最大化地節(jié)省CPU資源哮伟,卻對內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問妄帘,從而不會被清除楞黄,占用大量內(nèi)存。

3抡驼、定期過期

每隔一定的時間鬼廓,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key致盟。該策略是前兩者的一個折中方案碎税。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內(nèi)存資源達(dá)到最優(yōu)的平衡效果馏锡。

expires字典會保存所有設(shè)置了過期時間的key的過期時間數(shù)據(jù)雷蹂,其中 key 是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間杯道。鍵空間是指該Redis集群中保存的所有鍵匪煌。

Redis采用的過期策略:惰性刪除 + 定期刪除。memcached采用的過期策略:惰性刪除党巾。

6.2萎庭、6種內(nèi)存淘汰策略

Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時,怎么處理需要新寫入且需要申請額外空間的數(shù)據(jù)齿拂。

1驳规、volatile-lru:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰

2、volatile-ttl:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過期的數(shù)據(jù)淘汰

3署海、volatile-random:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰

4吗购、allkeys-lru:從數(shù)據(jù)集(server.db[i].dict)中挑選最近最少使用的數(shù)據(jù)淘汰

5医男、allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰 6、no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)巩搏,不刪除的意思昨登。

面試常問常考的也就是LRU了贯底,大家熟悉的LinkedHashMap中也實現(xiàn)了LRU算法的丰辣,實現(xiàn)如下:

<pre data-tool="mdnice編輯器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(63, 63, 63); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">class SelfLRUCache<K, V> extends LinkedHashMap<K, V> { private final int CACHE_SIZE; /** * 傳遞進(jìn)來最多能緩存多少數(shù)據(jù) * @param cacheSize 緩存大小 */ public SelfLRUCache(int cacheSize) { // true 表示讓 linkedHashMap 按照訪問順序來進(jìn)行排序,最近訪問的放在頭部禽捆,最老訪問的放在尾部笙什。 super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { // 當(dāng) map中的數(shù)據(jù)量大于指定的緩存?zhèn)€數(shù)的時候,就自動刪除最老的數(shù)據(jù)胚想。 return size() > CACHE_SIZE; } } </pre>

6.2琐凭、總結(jié)

Redis的內(nèi)存淘汰策略的選取并不會影響過期的key的處理。內(nèi)存淘汰策略用于處理內(nèi)存不足時的需要申請額外空間的數(shù)據(jù)浊服,過期策略用于處理過期的緩存數(shù)據(jù)统屈。

7、Redis 集群高可用

單機(jī)問題有機(jī)器故障牙躺、容量瓶頸愁憔、QPS瓶頸。在實際應(yīng)用中孽拷,Redis的多機(jī)部署時候會涉及到redis主從復(fù)制吨掌、Sentinel哨兵模式Redis Cluster脓恕。

模式 優(yōu)點 缺點
單機(jī)版 架構(gòu)簡單膜宋,部署方便 機(jī)器故障、容量瓶頸炼幔、QPS瓶頸
主從復(fù)制 高可靠性秋茫,讀寫分離 故障恢復(fù)復(fù)雜,主庫的寫跟存受單機(jī)限制
Sentinel 哨兵 集群部署簡單乃秀,HA 原理繁瑣肛著,slave存在資源浪費(fèi),不能解決讀寫分離問題
Redis Cluster 數(shù)據(jù)動態(tài)存儲solt环形,可擴(kuò)展,高可用 客戶端動態(tài)感知后端變更衙傀,批量操作支持查

7.1抬吟、redis主從復(fù)制

該模式下 具有高可用性且讀寫分離, 會采用 增量同步全量同步 兩種機(jī)制统抬。

7.1.1火本、全量同步
圖片

Redis全量復(fù)制一般發(fā)生在Slave初始化階段危队,這時Slave需要將Master上的所有數(shù)據(jù)都復(fù)制一份:

1、slave連接master钙畔,發(fā)送psync命令茫陆。

2、master接收到psync命名后擎析,開始執(zhí)行bgsave命令生成RDB文件并使用緩沖區(qū)記錄此后執(zhí)行的所有寫命令簿盅。

3、master發(fā)送快照文件到slave揍魂,并在發(fā)送期間繼續(xù)記錄被執(zhí)行的寫命令桨醋。4、slave收到快照文件后丟棄所有舊數(shù)據(jù)现斋,載入收到的快照喜最。

5、master快照發(fā)送完畢后開始向slave發(fā)送緩沖區(qū)中的寫命令庄蹋。

6瞬内、slave完成對快照的載入,開始接收命令請求限书,并執(zhí)行來自master緩沖區(qū)的寫命令虫蝶。

7.1.2、增量同步

也叫指令同步蔗包,就是從庫重放在主庫中進(jìn)行的指令秉扑。Redis會把指令存放在一個環(huán)形隊列當(dāng)中,因為內(nèi)存容量有限调限,如果備機(jī)一直起不來舟陆,不可能把所有的內(nèi)存都去存指令,也就是說耻矮,如果備機(jī)一直未同步秦躯,指令可能會被覆蓋掉。

Redis增量復(fù)制是指Slave初始化后開始正常工作時master發(fā)生的寫操作同步到slave的過程裆装。增量復(fù)制的過程主要是master每執(zhí)行一個寫命令就會向slave發(fā)送相同的寫命令踱承。

圖片

<figcaption style="margin: 5px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; text-align: center; color: rgb(136, 136, 136); font-size: 12px; font-family: PingFangSC-Light;">
</figcaption>

7.1.3、Redis主從同步策略:

1哨免、主從剛剛連接的時候茎活,進(jìn)行全量同步;全同步結(jié)束后琢唾,進(jìn)行增量同步载荔。當(dāng)然,如果有需要采桃,slave 在任何時候都可以發(fā)起全量同步懒熙。redis 策略是丘损,無論如何,首先會嘗試進(jìn)行增量同步工扎,如不成功徘钥,要求從機(jī)進(jìn)行全量同步。2肢娘、slave在同步master數(shù)據(jù)時候如果slave丟失連接不用怕呈础,slave在重新連接之后丟失重補(bǔ)

3蔬浙、一般通過主從來實現(xiàn)讀寫分離猪落,但是如果master掛掉后如何保證Redis的 HA呢?引入Sentinel進(jìn)行master的選擇畴博。

7.2笨忌、高可用之哨兵模式

圖片

Redis-sentinel 本身是一個獨(dú)立運(yùn)行的進(jìn)程,一般sentinel集群 節(jié)點數(shù)至少三個且奇數(shù)個俱病,它能監(jiān)控多個master-slave集群官疲,sentinel節(jié)點發(fā)現(xiàn)master宕機(jī)后能進(jìn)行自動切換。Sentinel可以監(jiān)視任意多個主服務(wù)器以及主服務(wù)器屬下的從服務(wù)器亮隙,并在被監(jiān)視的主服務(wù)器下線時途凫,自動執(zhí)行故障轉(zhuǎn)移操作。這里需注意sentinel也有single-point-of-failure問題溢吻。大致羅列下哨兵用途:

集群監(jiān)控:循環(huán)監(jiān)控master跟slave節(jié)點维费。

消息通知:當(dāng)它發(fā)現(xiàn)有redis實例有故障的話,就會發(fā)送消息給管理員

故障轉(zhuǎn)移:這里分為主觀下線(單獨(dú)一個哨兵發(fā)現(xiàn)master故障了)促王∠耍客觀下線(多個哨兵進(jìn)行抉擇發(fā)現(xiàn)達(dá)到quorum數(shù)時候開始進(jìn)行切換)。

配置中心:如果發(fā)生了故障轉(zhuǎn)移蝇狼,它會通知將master的新地址寫在配置中心告訴客戶端阅畴。

7.3、Redis Cluster

RedisCluster是Redis的分布式解決方案迅耘,在3.0版本后推出的方案贱枣,有效地解決了Redis分布式的需求。
圖片
7.3.1颤专、分區(qū)規(guī)則
圖片

常見的分區(qū)規(guī)則

  1. 節(jié)點取余:hash(key) % N
  2. 一致性哈希:一致性哈希環(huán)
  3. 虛擬槽哈希:CRC16[key] & 16383

RedisCluster采用了虛擬槽分區(qū)方式纽哥,具題的實現(xiàn)細(xì)節(jié)如下:

1、采用去中心化的思想栖秕,它使用虛擬槽solt分區(qū)覆蓋到所有節(jié)點上春塌,取數(shù)據(jù)一樣的流程,節(jié)點之間使用輕量協(xié)議通信Gossip來減少帶寬占用所以性能很高,

2摔笤、自動實現(xiàn)負(fù)載均衡與高可用,自動實現(xiàn)failover并且支持動態(tài)擴(kuò)展垦写,官方已經(jīng)玩到可以1000個節(jié)點 實現(xiàn)的復(fù)雜度低吕世。

3、每個Master也需要配置主從梯投,并且內(nèi)部也是采用哨兵模式命辖,如果有半數(shù)節(jié)點發(fā)現(xiàn)某個異常節(jié)點會共同決定更改異常節(jié)點的狀態(tài)。

4分蓖、如果集群中的master沒有slave節(jié)點尔艇,則master掛掉后整個集群就會進(jìn)入fail狀態(tài),因為集群的slot映射不完整么鹤。如果集群超過半數(shù)以上的master掛掉终娃,集群都會進(jìn)入fail狀態(tài)

5蒸甜、官方推薦 集群部署至少要3臺以上的master節(jié)點棠耕。

8、Redis 限流

經(jīng)常乘坐北京西二旗地鐵或者在北京西站乘坐的時候經(jīng)常會遇到一種情況就是如果人很多柠新,地鐵的工作人員拿個小牌前面一檔讓你等會兒再檢票窍荧,這就是實際生活應(yīng)對人流量巨大的措施。

在開發(fā)高并發(fā)系統(tǒng)時恨憎,有三把利器用來保護(hù)系統(tǒng):緩存蕊退、降級限流。那么何為限流呢憔恳?顧名思義瓤荔,限流就是限制流量,就像你寬帶包了1個G的流量喇嘱,用完了就沒了茉贡。通過限流,我們可以很好地控制系統(tǒng)的qps者铜,從而達(dá)到保護(hù)系統(tǒng)的目的腔丧。

1、基于Redis的setnx作烟、zset

1.2愉粤、setnx

比如我們需要在10秒內(nèi)限定20個請求,那么我們在setnx的時候可以設(shè)置過期時間10拿撩,當(dāng)請求的setnx數(shù)量達(dá)到20時候即達(dá)到了限流效果衣厘。

缺點:比如當(dāng)統(tǒng)計1-10秒的時候,無法統(tǒng)計2-11秒之內(nèi),如果需要統(tǒng)計N秒內(nèi)的M個請求影暴,那么我們的Redis中需要保持N個key等等問題错邦。

1.3、zset

其實限流涉及的最主要的就是滑動窗口型宙,上面也提到1-10怎么變成2-11撬呢。其實也就是起始值和末端值都各+1即可。我們可以將請求打造成一個zset數(shù)組妆兑,當(dāng)每一次請求進(jìn)來的時候魂拦,value保持唯一,可以用UUID生成搁嗓,而score可以用當(dāng)前時間戳表示芯勘,因為score我們可以用來計算當(dāng)前時間戳之內(nèi)有多少的請求數(shù)量。而zset數(shù)據(jù)結(jié)構(gòu)也提供了range方法讓我們可以很輕易的獲取到2個時間戳內(nèi)有多少請求腺逛,

缺點:就是zset的數(shù)據(jù)結(jié)構(gòu)會越來越大荷愕。

2、漏桶算法

漏桶算法思路:把水比作是請求棍矛,漏桶比作是系統(tǒng)處理能力極限路翻,水先進(jìn)入到漏桶里,漏桶里的水按一定速率流出茄靠,當(dāng)流出的速率小于流入的速率時茂契,由于漏桶容量有限,后續(xù)進(jìn)入的水直接溢出(拒絕請求)慨绳,以此實現(xiàn)限流掉冶。

圖片

3、令牌桶算法

令牌桶算法的原理:可以理解成醫(yī)院的掛號看病脐雪,只有拿到號以后才可以進(jìn)行診病厌小。

圖片

細(xì)節(jié)流程大致:

1、所有的請求在處理之前都需要拿到一個可用的令牌才會被處理战秋。

2璧亚、根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌脂信。

3癣蟋、設(shè)置桶最大可容納值,當(dāng)桶滿時新添加的令牌就被丟棄或者拒絕狰闪。

4疯搅、請求達(dá)到后首先要獲取令牌桶中的令牌,拿著令牌才可以進(jìn)行其他的業(yè)務(wù)邏輯埋泵,處理完業(yè)務(wù)邏輯之后幔欧,將令牌直接刪除罪治。

5、令牌桶有最低限額礁蔗,當(dāng)桶中的令牌達(dá)到最低限額的時候觉义,請求處理完之后將不會刪除令牌,以此保證足夠的限流浴井。

工程化:

1谁撼、自定義注解、aop滋饲、Redis + Lua 實現(xiàn)限流。

2喊巍、推薦 guavaRateLimiter實現(xiàn)屠缭。

9、常見知識點

  1. 字符串模糊查詢時用Keys可能導(dǎo)致線程阻塞崭参,盡量用scan指令進(jìn)行無阻塞的取出數(shù)據(jù)然后去重下即可呵曹。
  2. 多個操作的情況下記得用pipeLine把所有的命令一次發(fā)過去,避免頻繁的發(fā)送何暮、接收帶來的網(wǎng)絡(luò)開銷奄喂,提升性能。
  3. bigkeys可以掃描redis中的大key海洼,底層是使用scan命令去遍歷所有的鍵跨新,對每個鍵根據(jù)其類型執(zhí)行STRLEN、LLEN坏逢、SCARD域帐、HLEN、ZCARD這些命令獲取其長度或者元素個數(shù)是整。缺陷是線上試用并且個數(shù)多不一定空間大肖揣,
  4. 線上應(yīng)用記得開啟Redis慢查詢?nèi)罩九叮舅悸犯鶰ySQL類似浮入。
  5. Redis中因為內(nèi)存分配策略跟增刪數(shù)據(jù)是會導(dǎo)致內(nèi)存碎片龙优,你可以重啟服務(wù)也可以執(zhí)行activedefrag yes進(jìn)行內(nèi)存重新整理來解決此問題。
    圖片

1事秀、Ratio >1 表明有內(nèi)存碎片彤断,越大表明越多嚴(yán)重。

2易迹、Ratio < 1 表明正在使用虛擬內(nèi)存瓦糟,虛擬內(nèi)存其實就是硬盤,性能比內(nèi)存低得多赴蝇,這是應(yīng)該增強(qiáng)機(jī)器的內(nèi)存以提高性能菩浙。

3、一般來說,mem_fragmentation_ratio的數(shù)值在1 ~ 1.5之間是比較健康的劲蜻。

10陆淀、End

關(guān)于Redis先吹逼這么多(本來想寫秒殺的,怕寫太長先嬉,估計能看到這就算是認(rèn)真閱讀者了)轧苫,如果你感覺沒看夠那得價錢

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疫蔓,一起剝皮案震驚了整個濱河市含懊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衅胀,老刑警劉巖岔乔,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滚躯,居然都是意外死亡雏门,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門掸掏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茁影,“玉大人,你說我怎么就攤上這事丧凤∧枷校” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵愿待,是天一觀的道長蝇更。 經(jīng)常有香客問我,道長呼盆,這世上最難降的妖魔是什么年扩? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮访圃,結(jié)果婚禮上厨幻,老公的妹妹穿的比我還像新娘。我一直安慰自己腿时,他們只是感情好况脆,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著批糟,像睡著了一般格了。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上徽鼎,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天盛末,我揣著相機(jī)與錄音弹惦,去河邊找鬼。 笑死悄但,一個胖子當(dāng)著我的面吹牛棠隐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播檐嚣,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼助泽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嚎京?” 一聲冷哼從身側(cè)響起嗡贺,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鞍帝,沒想到半個月后诫睬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膜眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了溜嗜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宵膨。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炸宵,靈堂內(nèi)的尸體忽然破棺而出辟躏,到底是詐尸還是另有隱情,我是刑警寧澤土全,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布捎琐,位于F島的核電站,受9級特大地震影響裹匙,放射性物質(zhì)發(fā)生泄漏瑞凑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一概页、第九天 我趴在偏房一處隱蔽的房頂上張望籽御。 院中可真熱鬧,春花似錦惰匙、人聲如沸技掏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哑梳。三九已至,卻和暖如春绘盟,著一層夾襖步出監(jiān)牢的瞬間鸠真,已是汗流浹背悯仙。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留弧哎,地道東北人雁比。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像撤嫩,于是被迫代替她去往敵國和親偎捎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容