數(shù)據(jù)存儲和消息隊列
Redis
1. Redis 有哪些數(shù)據(jù)類型
Redis是一個開源的內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng)鹅髓。它可以用作數(shù)據(jù)庫,緩存和消息中間件陷寝。
它支持多種類型的數(shù)據(jù)結(jié)構(gòu),如字符串String星掰,散列Hashes多望,列表Lists嫩舟,集合Sets,有序集合Sorted Sets或者ZSet怀偷,Bitmaps送讲,Hyperloglogs和地理空間Geospatial索引半徑查詢胚嘲。
最常見的數(shù)據(jù)結(jié)構(gòu)類型有String仰挣,List翘簇,Set,Hash维蒙,ZSet五種掰吕。
1.1 String
Redis的String類型是一個由字節(jié)組成的序列。它和其他編程語言或者其他鍵值對存儲提供的字符串操作非常相似颅痊。
String是最常用的一種數(shù)據(jù)類型殖熟,普通的key/value存儲都可以歸為此類。value其實不僅是String斑响,也可以是數(shù)字菱属。
1.2 List
Redis的List其實就是鏈表(Redis使用雙端鏈表實現(xiàn)List)。
使用List結(jié)構(gòu)恋捆,可以輕松實現(xiàn)最新消息排行等功能照皆。List的另一個應(yīng)用就是消息隊列。
一個List結(jié)構(gòu)可以有序存儲多個字符串沸停,并且是允許元素重復(fù)的。
1.3 Set
Redis的集合和列表都可以存儲多個字符串昭卓,列表可以存儲多個相同的字符串愤钾,而集合通過使用散列表來保證自己存儲的每個字符串都是各不相同的。
Redis的集合使用的是無序的方式存儲元素候醒。
應(yīng)用場景:好友系統(tǒng)能颁;利用唯一性,統(tǒng)計訪問網(wǎng)站的所有獨立IP倒淫。
1.4 Hash散列類型
Redis的散列可以存儲多個鍵值對之間的映射伙菊。和字符串一樣,散列存儲的值既可以是字符串又可以是數(shù)字值敌土,并且用戶同樣可以對散列存儲的數(shù)字執(zhí)行自增操作或者自減操作镜硕。
一個LIst散列類型的實例,是一個包含兩個鍵值對的散列鍵返干。
1.5 有序集合ZSet
有序集合和散列一樣兴枯,用于存儲鍵值對。有序集合的鍵被稱為成員member矩欠,每一個成員都是獨一無二的财剖。而有序集合的值被稱為分值score悠夯,分值必須是浮點數(shù)。
有序集合是Redis里面唯一一個既可以根據(jù)成員訪問元素躺坟,又可以根據(jù)分值以及分值的排序來訪問元素的結(jié)構(gòu)沦补。
一個有序集合類型的實例,zset-key是一個包含兩個元素的有序集合鍵咪橙。
參考:
《Redis常見的5種不同的數(shù)據(jù)類型詳解》
2. Redis 內(nèi)部結(jié)構(gòu)
Redis內(nèi)部使用一個redisObject對象來表示所有的key和value夕膀。redisObject主要的信息包括數(shù)據(jù)類型type,編碼方式encoding匣摘,數(shù)據(jù)指針ptr店诗,虛擬內(nèi)存vm等。
type表示一個value對象具體是何種數(shù)據(jù)類型音榜,encoding是不同數(shù)據(jù)類型在redis內(nèi)部的編碼方式庞瘸。ptr指針指向?qū)ο蟮牡讓訉崿F(xiàn)數(shù)據(jù)結(jié)構(gòu)。
dict是一個用于維護key和value映射關(guān)系的數(shù)據(jù)結(jié)構(gòu)赠叼。與很多語言中的map或dictionary類似擦囊。Redis的一個database中所有key到value的映射,就是使用一個dict來維護的嘴办。
dict本質(zhì)上是為了解決算法中的查找searching問題瞬场。一般查找問題的解法分為兩大類:一個基于各種平衡樹,一個基于哈希表涧郊。dict是一個基于哈希表的算法贯被。它最顯著的一個特點就在獨特的rehashing。它采用了增量式重哈希incremental rehashing的方法妆艘。在需要擴展內(nèi)存時避免一次性對所有key進行重哈希彤灶,而是將重哈希操作分散到對于dict的各個增刪改查的操作中去。這種方法每次只對一小部分key進行rehash批旺,而每次rehash之間不影響dict的操作幌陕。這避免了rehash期間單個請求的響應(yīng)時間暴增。
Redis數(shù)據(jù)庫是真正存儲數(shù)據(jù)的地方汽煮。而數(shù)據(jù)庫本身也是存儲在內(nèi)存中的搏熄。數(shù)據(jù)由dict和expires兩個字典構(gòu)成。其中dict保存鍵值對暇赤,而expires則保存鍵的過期時間心例。
參考:
《Redis的內(nèi)部結(jié)構(gòu)》
《redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)深入淺出》
3. Redis 使用場景
Redis讀寫性能優(yōu)異,數(shù)據(jù)類型豐富翎卓,而且Redis是單進程單線程工作的契邀,所以存儲/刪除的操作不必保證原子性。其存儲數(shù)據(jù)還可以自動過期失暴。
3.1 Redis的高性能讓其非常適合作為緩存
緩存是Redis最常見的應(yīng)用場景坯门,因為其讀寫性能實在過于優(yōu)異微饥。且Redis內(nèi)部是支持事務(wù)的,在使用時候能有效保證數(shù)據(jù)的一致性古戴。
3.2 Redis支持多種數(shù)據(jù)格式欠橘,讓其應(yīng)用場景豐富。
string作為最簡單的k-v存儲现恼,短信驗證碼肃续,配置信息等都適合用其存儲。
hash一般key作為id或者唯一標(biāo)示叉袍,value存儲詳情始锚。可以用于商品詳情喳逛,新聞詳情瞧捌,個人信息等。
list是有序的润文,比較適合存儲有序且數(shù)據(jù)相對固定的數(shù)據(jù)姐呐。例如省市區(qū)表,字典等典蝌。因為其有序曙砂,適合根據(jù)寫入時間來排序,例如最新的數(shù)據(jù)骏掀,消息隊列等鸠澈。
set提供交集,并集截驮,差集等操作款侵。當(dāng)其存儲一個用戶的好友時,可以非常便捷地找出幾個用戶的共同好友等信息侧纯,非常適合用于做推送等應(yīng)用。
3.3 Redis是單線程的甲脏,可作為分布式鎖
Redis是單線程眶熬,多路復(fù)用方式提高處理效率。Redis作為分布式鎖块请,因為其性能的優(yōu)勢娜氏,不會成為瓶頸,一般會產(chǎn)生瓶頸的是真正的業(yè)務(wù)處理內(nèi)容墩新。
3.4 Redis的自動過期能提高開發(fā)效率
Redis的數(shù)據(jù)都可以設(shè)置過期時間贸弥,過期的數(shù)據(jù)清理無需開發(fā)者去關(guān)注,所以開發(fā)效率高海渊。例如短信驗證碼的過期處理不需要像數(shù)據(jù)庫一下還要查時間對比判斷是否數(shù)據(jù)已經(jīng)過期了绵疲。
參考:
《一起來聊聊最近很火的Redis常見應(yīng)用場景解析》
4. Redis 持久化機制
將redis內(nèi)存服務(wù)器中的數(shù)據(jù)持久化到硬盤等介質(zhì)中使得服務(wù)器再重啟之后還可以重用以前得數(shù)據(jù)哲鸳,也可以防止系統(tǒng)出現(xiàn)故障而導(dǎo)致數(shù)據(jù)無法恢復(fù)。
Redis提供了兩種不同方式的持久化方法:快照Snapshotting和之追加文件append-only-file
4.1 快照RDB
快照就是俗稱的備份盔憨,可以在定期內(nèi)對數(shù)據(jù)進行備份徙菠,將redis服務(wù)器中的數(shù)據(jù)持久化到硬盤中。
在創(chuàng)建快照之后郁岩,用戶可以對快照進行備份婿奔。同行情況下,為了防止單臺服務(wù)器出現(xiàn)故障而造成所有數(shù)據(jù)的丟失问慎,還可以將快照復(fù)制到其他服務(wù)器萍摊,創(chuàng)建具有相同數(shù)據(jù)的數(shù)據(jù)副本∪绲穑快照只適合數(shù)據(jù)不經(jīng)常修改或者丟失部分?jǐn)?shù)據(jù)影響不大的場景冰木。因為快照只能恢復(fù)到最近一次生成快照的數(shù)據(jù)。如果這個時間間隔過大或者數(shù)據(jù)的修改非常頻繁薇正,會導(dǎo)致快照的恢復(fù)能力并不強大片酝。
4.2 只追加文件AOF
AOF在執(zhí)行寫命令的時候,將執(zhí)行的寫命令復(fù)制到硬盤里面挖腰。后期恢復(fù)的時候雕沿,只需要重新執(zhí)行一下這個寫命令就可以了。
AOF的持久化會將被執(zhí)行的寫命令寫到AOF文件的末尾猴仑,以此來記錄數(shù)據(jù)繁盛的變化审轮。這樣,在恢復(fù)時只需要從頭到尾執(zhí)行一下AOF文件即可恢復(fù)數(shù)據(jù)辽俗。
參考:
《使用快照和AOF將Redis數(shù)據(jù)持久化到硬盤中》
5. Redis 集群方案與實現(xiàn)
這題完全超過我的能力范圍了疾渣。只給出具體鏈接,有人對此有深刻理解的話歡迎補充此題崖飘。
參考:
《Redis集群方案應(yīng)該怎么做榴捡?》
《
redis集群主流架構(gòu)方案分析》
《這可能是最全的 Redis 集群方案介紹了》
6. Redis 為什么是單線程的?
Redis采用的是基于內(nèi)存的單進程單線程模型的k-v數(shù)據(jù)庫朱浴,由C語言編寫吊圾。官方提供的數(shù)據(jù)是可以達到10萬+的每秒內(nèi)查詢次數(shù)。
Redis的速度有部分得益于它采用的是單線程翰蠢。這避免了不必要的上下文切換和競爭條件项乒,也不存在多進程或者多線程導(dǎo)致的切換而消耗CPU,不用去考慮各種鎖的問題梁沧,不存在加鎖釋放鎖操作檀何,也就不可能因為可能出現(xiàn)的死鎖而導(dǎo)致的不必要的性能消耗,
而Redis為什么會采用單線程,這點官方在FAQ上已經(jīng)給出來了答案:
因為Redis是基于內(nèi)存的操作频鉴,CPU并不是Redis的瓶頸栓辜,Redis的瓶頸最有可能是及其內(nèi)存的大小或者網(wǎng)絡(luò)帶寬。既然單線程容易實現(xiàn)砚殿,而且CPU又不會造成瓶頸啃憎,那么就自然而然使用單線程來避免采用多線程會碰到的各種麻煩。
但是相對的似炎,使用單線程的方式是無法發(fā)揮多核CPU的性能的辛萍,不過可以通過在單機開啟多個Redis實例來解決此問題。
參考:
《為什么說Redis是單線程的以及Redis為什么這么快羡藐!
》
7. 緩存雪崩贩毕、緩存穿透、緩存預(yù)熱仆嗦、緩存更新辉阶、緩存降級
7.1 緩存雪崩
由于原有緩存失效,新緩存未到期間瘩扼,所有原本應(yīng)該訪問緩存的請求都去直接查詢數(shù)據(jù)庫了谆甜,從而對數(shù)據(jù)庫CPU和內(nèi)存瞬間造成巨大壓力,嚴(yán)重的甚至?xí)斐蓴?shù)據(jù)庫宕機集绰。這一系列連鎖反應(yīng)甚至可以造成整個系統(tǒng)崩潰规辱。這就是緩存雪崩。
解決方法:當(dāng)并發(fā)量不高時栽燕,可以采用加鎖排隊來防止瞬間查詢壓力過大罕袋,然而此方法在生產(chǎn)環(huán)境中很少使用。生產(chǎn)環(huán)境中一般通過設(shè)置二級緩存碍岔,為key設(shè)置不同的緩存失效時間等方式來解決浴讯。
7.2 緩存穿透
緩存穿透指用戶所要查詢的數(shù)據(jù)在數(shù)據(jù)庫中并不存在,所以這些數(shù)據(jù)自然不可能出現(xiàn)在緩存蔼啦。而緩存中無法找到這類數(shù)據(jù)榆纽,從而導(dǎo)致每次都要去數(shù)據(jù)庫進行查詢,然后返回空捏肢。等于進行了兩次無用的查詢掠河,這也是經(jīng)常提到的緩存命中率問題。
解決方案:采用布隆過濾器猛计,將所有可能存在的數(shù)據(jù)哈希到一個足夠大的bitmap中,一個一定不存在的數(shù)據(jù)會被這個bitmap攔截掉爆捞,從而避免了對底層數(shù)據(jù)系統(tǒng)的查詢壓力奉瘤。也可以當(dāng)一個查詢返回的數(shù)據(jù)為空時,仍然將這個空結(jié)果緩存,但注意設(shè)置它的過期時間設(shè)置為較短時間盗温。
7.3 緩存預(yù)熱
緩存預(yù)熱就是在系統(tǒng)上線后藕赞,提前將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。避免用戶請求時先查數(shù)據(jù)庫再將數(shù)據(jù)緩存卖局。
7.4 緩存更新
除了Redis自帶的六種緩存失效策略外斧蜕,還可以根據(jù)具體的業(yè)務(wù)需求進行自定義的緩存淘汰。
可以定時去清理過期的緩存砚偶∨或當(dāng)有用戶請求過來時,再判斷這個請求所用到的緩存是否過期染坯。如果過期再去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存均芽。
7.5 緩存降級
當(dāng)訪問量劇增,服務(wù)出現(xiàn)問題(如響應(yīng)時間慢或不響應(yīng))或非核心服務(wù)影響到核心流程的性能時单鹿,仍然需要保證服務(wù)還是可用的掀宋,即使是有損服務(wù)。降級的最終目的是保證核心服務(wù)可用仲锄,即使是有損的劲妙。但請注意,有些服務(wù)無法被降級:例如購物車儒喊,結(jié)算等敏感操作镣奋。
降級操作可以讓系統(tǒng)根據(jù)一些關(guān)鍵數(shù)據(jù)進行自動降級,也可以配置開關(guān)實現(xiàn)人工降級澄惊。在進行降級之前要對系統(tǒng)進行梳理唆途,看看系統(tǒng)是不是可以棄車保帥,從而梳理出哪些必須保護掸驱,而哪些則需要降級肛搬。
參考:
《Redis系列十:緩存雪崩、緩存穿透毕贼、緩存預(yù)熱温赔、緩存更新、緩存降級》
8. 使用緩存的合理性問題
- 熱點數(shù)據(jù)鬼癣,緩存才有價值陶贼。
對于冷數(shù)據(jù)而言,大部分?jǐn)?shù)據(jù)可能還沒有再次訪問到就已經(jīng)被擠出內(nèi)存待秃,不僅占用內(nèi)存拜秧,而且價值不大。而針對熱點數(shù)據(jù)進行緩存章郁,緩存的命中率理論上會非常高枉氮,從而會提高很大的性能志衍。 - 頻繁修改的數(shù)據(jù),看情況考慮使用緩存聊替。
數(shù)據(jù)更新前至少讀取兩次楼肪,緩存才有意義。如果緩存還沒有起作用就失效了惹悄,那就沒有太大的價值了春叫。
但當(dāng)讀取接口對數(shù)據(jù)庫壓力很大,卻又是個熱點數(shù)據(jù)泣港,這是就可以考慮使用緩存手段來減少數(shù)據(jù)庫的壓力暂殖,哪怕它的修改頻率很高。 - 數(shù)據(jù)不一致性
一般會對緩存設(shè)置失效時間爷速,一旦超過失效時間央星,就要從數(shù)據(jù)庫重新加載。因此應(yīng)用可能需要容忍一定時間的數(shù)據(jù)不一致惫东。 - 緩存更新機制
針對數(shù)據(jù)不一致和臟讀現(xiàn)象莉给,可以通過緩存更新機制解決:采用緩存雙淘汰機制,在更新數(shù)據(jù)庫的時候淘汰緩存廉沮。此外颓遏,在超過了緩存失效時間也會淘汰掉緩存。 - 緩存可用性
緩存是提高數(shù)據(jù)讀取西能的滞时,換粗?jǐn)?shù)據(jù)丟失和緩存不可用不會影響應(yīng)用程序的處理叁幢。因此,一般的操作手段是坪稽,如果Redis出現(xiàn)異常曼玩,則手動捕獲記錄日志后,去數(shù)據(jù)庫查詢數(shù)據(jù)返回給用戶窒百。 - 采用緩存服務(wù)降級
- 采用緩存預(yù)熱
- 緩存穿透的解決
參考:
《Redis實戰(zhàn)(一) 使用緩存合理性》
9. Redis常見的回收策略
Redis內(nèi)置了六種回收策略供我們使用
- volatile-lru
從已設(shè)置過期時間的數(shù)據(jù)集server.db[i].expires中挑選最近最少使用LRU的數(shù)據(jù)淘汰 - volatile-ttl
從已設(shè)置過期時間的數(shù)據(jù)集server.db[i].expires中挑選將要過期的數(shù)據(jù)淘汰 - volatile-random
從已設(shè)置過期時間的數(shù)據(jù)集server.db[i].expires中任意選擇數(shù)據(jù)淘汰 - allkeys-lru
從數(shù)據(jù)集server.db[i].dict中挑選最近最少使用的數(shù)據(jù)淘汰 - alleys-random
從數(shù)據(jù)集server.db[i].dict中任意選擇數(shù)據(jù)淘汰 - no-enviction
禁止驅(qū)逐數(shù)據(jù)
參考:
《redis的回收策略》
10. Redis的pipeline有什么用處黍判?
Redis本身是基于Request/Response協(xié)議的,正常情況下篙梢,客戶端發(fā)送一個命令顷帖,等待Redis應(yīng)答,Redis在接收到命令渤滞,處理后應(yīng)答贬墩。在這種情況下,如果同時需要執(zhí)行大量的命令妄呕,那就是等待上一條命令應(yīng)答后再執(zhí)行陶舞,這中間不僅僅多了RTT(Round Time Trip),而且還頻繁的調(diào)用系統(tǒng)IO绪励,發(fā)送網(wǎng)絡(luò)請求吊说。
為了提升效率论咏,Pipeline出現(xiàn)了,它允許客戶端可以一次發(fā)送多條命令颁井,而不等待上一條命令執(zhí)行的結(jié)果。它不僅減少了RTT蠢护,同時也減少了IO調(diào)用次數(shù)(IO調(diào)用涉及到用戶態(tài)到內(nèi)核態(tài)之間的切換)雅宾。
客戶端這邊首先將執(zhí)行的命令寫入到緩沖中,最后再一次性發(fā)送Redis葵硕。但是有一種情況就是眉抬,緩沖區(qū)的大小是有限制的,比如Jedis懈凹,限制為8192蜀变,超過了,則刷緩存介评,發(fā)送到Redis库北,但是不去處理Redis的應(yīng)答
11. Redis過期策略是怎么實現(xiàn)的呢?
Redis有三種過期策略:
- 定時刪除
在設(shè)置key的過期時間的同時们陆,為該key創(chuàng)建一個定時器寒瓦,讓定時器在key的過期時間來臨時,對key進行刪除坪仇。
它可以保證內(nèi)存被盡快釋放杂腰,然而刪除key會占用cpu時間,且定時器的創(chuàng)建耗時椅文,大量創(chuàng)建定時器將會嚴(yán)重影響性能喂很。 - 懶漢式刪除
key過期的時候不刪除,每次通過key獲取值的時候去檢查是否過期皆刺,若過期少辣,則刪除,返回null芹橡。
刪除操作只在發(fā)生key取值的時候發(fā)生毒坛,對cpu占用較小,但若大量key在超時后都沒有被獲取過林说,可能會發(fā)生內(nèi)存泄漏:已經(jīng)過期的無用數(shù)據(jù)占用了大量內(nèi)存煎殷。 - 定期刪除
每隔一段時間執(zhí)行一次刪除過期key的操作
它是上面兩種策略的折中版。
memcached使用的是懶漢式策略腿箩,而redis同時使用了懶漢式策略與定期刪除兩種策略