高清思維導(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
弓柱,核心有兩個作用:
- 說明是5種類型哪一種。
- 里面有指針用來指向 SDS侧但。
當(dāng)你執(zhí)行set name sowhat
的時候矢空,其實Redis會創(chuàng)建兩個RedisObject對象,鍵的RedisObject 和 值的RedisOjbect 其中它們type = REDIS_STRING禀横,而SDS分別存儲的就是 name 跟 sowhat 字符串咯屁药。
并且Redis底層對SDS有如下優(yōu)化:
- SDS修改后大小 > 1M時 系統(tǒng)會多分配空間來進(jìn)行
空間預(yù)分配
。- 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].use 跟 dictht[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é)點為例,來對比說明跳表和普遍的鏈表蜜猾。
-
沒有跳表查詢 比如我查詢數(shù)據(jù)37秀菱,如果沒有上面的索引時候路線如下圖:圖片
-
有跳表查詢 有跳表查詢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歹撒。
- 用戶簽到
key = 年份:用戶id offset = (今天是一年中的第幾天) % (今年的天數(shù))
- 統(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í)行
BGSAVE
和BGREWRITEAOF
命令時徘键,哈希表的負(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)于多線程須知:
- Redis 6.0 版本 默認(rèn)多線程是關(guān)閉的 io-threads-do-reads no
- Redis 6.0 版本 開啟多線程后 線程數(shù)也要 謹(jǐn)慎設(shè)置陋葡。
- 多線程可以使得性能翻倍亚亲,但是多線程只是用來處理網(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ā)中我們可以用 synchronized 、Lock 實現(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ī)則
節(jié)點取余
:hash(key) % N一致性哈希
:一致性哈希環(huán)虛擬槽哈希
: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喊巍、推薦 guava 的RateLimiter實現(xiàn)屠缭。
9、常見知識點
- 字符串模糊查詢時用
Keys
可能導(dǎo)致線程阻塞崭参,盡量用scan
指令進(jìn)行無阻塞的取出數(shù)據(jù)然后去重下即可呵曹。 - 多個操作的情況下記得用
pipeLine
把所有的命令一次發(fā)過去,避免頻繁的發(fā)送何暮、接收帶來的網(wǎng)絡(luò)開銷奄喂,提升性能。 - bigkeys可以掃描redis中的大key海洼,底層是使用scan命令去遍歷所有的鍵跨新,對每個鍵根據(jù)其類型執(zhí)行STRLEN、LLEN坏逢、SCARD域帐、HLEN、ZCARD這些命令獲取其長度或者元素個數(shù)是整。缺陷是線上試用并且個數(shù)多不一定空間大肖揣,
- 線上應(yīng)用記得開啟Redis慢查詢?nèi)罩九叮舅悸犯鶰ySQL類似浮入。
- 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)真閱讀者了)轧苫,如果你感覺沒看夠那得價錢
。