前言
最近,我從Redis入手营罢,對分布式緩存進(jìn)行了學(xué)習(xí)探究。為了記錄我的學(xué)習(xí)成果饼齿,我將會寫一系列博客來介紹Redis和分布式緩存饲漾。在學(xué)習(xí)過程中蝙搔,我在網(wǎng)上查閱了一些關(guān)于Redis的資料,發(fā)現(xiàn)網(wǎng)上的資料比較零散考传,而且良莠不齊吃型,讓人很難縱觀Redis的全局。同時(shí)伙菊,網(wǎng)上也有很多課程和專欄败玉,它們也講得很系統(tǒng),但是不同專欄和課程間形成了一座座信息孤島镜硕,有些知識點(diǎn)只是某些課程或者專欄有講到运翼,很難做到面面俱到。因此兴枯,我希望用一系列博客來縱觀Redis血淌,既總覽Redis又不放過每一個(gè)重要原理和細(xì)枝末節(jié)。廢話不多說财剖,讓我們一起開始Redis之旅吧^_^
1. Redis是什么
Redis是“Remote Dictionary Service”(即遠(yuǎn)程字典服務(wù))的縮寫悠夯,它是一個(gè)由ANSI C 語言開發(fā)的,開放源代碼(BSD許可)的key-value存儲組建躺坟。它的所有數(shù)據(jù)結(jié)構(gòu)都在內(nèi)存中沦补,并且支持?jǐn)?shù)據(jù)持久化,因此可用來做緩存咪橙、數(shù)據(jù)庫和消息中間件夕膀。
2. Redis的數(shù)據(jù)類型
目前,Redis支持8種數(shù)據(jù)類型:String美侦、List产舞、Set、ZSet菠剩、Hash易猫、Bitmap、Geo具壮、Hyperloglog准颓,針對每種數(shù)據(jù)類型,Redis提供了不同的操作指令進(jìn)行數(shù)據(jù)操作棺妓。大多數(shù)人可能只對前五種比較熟悉瞬场,我在這里先簡述下后面三種,接下來會有專門的博客來詳細(xì)介紹各種數(shù)據(jù)類型涧郊。
Bitmap即位圖,每一bit只能取值為為0或1眼五,底層通過字節(jié)數(shù)組來存儲妆艘,每個(gè)字節(jié)有8個(gè)bit彤灶。我們可以通過Bitmap來存儲一系列YN標(biāo)志,比如記錄用戶每天的簽到情況批旺,每個(gè)bit代表一天幌陕,0代表沒簽到,1代表已簽到汽煮。
Geo是Redis的地理位置模塊搏熄,底層通過GeoHash算法將二維的地理坐標(biāo)轉(zhuǎn)換為一維的數(shù)值,再存儲在ZSet中進(jìn)行排序暇赤。我們可以通過Geo來計(jì)算兩地距離心例、實(shí)現(xiàn)搜索附近的功能。
Hyperloglog是一個(gè)去重計(jì)數(shù)器鞋囊,可以用于去重地統(tǒng)計(jì)數(shù)量止后,比如頁面UV(用戶瀏覽數(shù)量,每個(gè)用戶多次瀏覽頁面只算1個(gè)UV)溜腐。Hyperloglog的底層數(shù)據(jù)存儲依然采用字節(jié)數(shù)組的形式译株,每6bit代表一個(gè)桶,用于存儲這個(gè)桶的元素個(gè)數(shù)挺益,Redis采用16384( 2^14 )個(gè)桶實(shí)現(xiàn)歉糜,因此一共占用2^14*8/6=12k
的存儲空間。Hyperloglog的存儲方式跟String和Bitmap一樣望众,之所以能實(shí)現(xiàn)去重統(tǒng)計(jì)匪补,特殊之處在于它在計(jì)數(shù)時(shí)用到的Hyperloglog算法。
3. Redis能干什么
學(xué)習(xí)一門技術(shù)黍檩,最重要的是學(xué)以致用叉袍,我覺得了解一項(xiàng)技術(shù)的背景和作用是打開這項(xiàng)技術(shù)大門的必經(jīng)之路。對于Redis而言刽酱,它的應(yīng)用處取決于它的特性喳逛,以及它向我們提供了什么數(shù)據(jù)類型和操作指令。
緩存數(shù)據(jù)棵里。從廣義上來說润文,緩存是用于數(shù)據(jù)快速交換的存儲介質(zhì)〉盍基于Redis的內(nèi)存存儲特性典蝌,我們可以使用它來緩存熱點(diǎn)數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)的快速存取头谜。
-
分布式鎖骏掀。在分布式系統(tǒng)中,我們可以通過加鎖的方式來限制共享資源的訪問,同一時(shí)間允許一個(gè)系統(tǒng)來訪問共享資源截驮,因?yàn)樵诜植际较到y(tǒng)中各個(gè)機(jī)器的內(nèi)存隔離笑陈,所以需要一個(gè)中間件來協(xié)調(diào)資源訪問,而Redis就可以充當(dāng)這樣一個(gè)角色葵袭。Redis提供了setnx和del指令涵妥,setnx指令可以原子性地實(shí)現(xiàn)“如果key不存在,就set這個(gè)key”的功能坡锡,如果set成功蓬网,就可以去訪問共享資源;否則不斷重試搶占鎖鹉勒。而當(dāng)資源訪問完畢后通過del指令來刪除之前set的key帆锋,從而釋放鎖。
延時(shí)隊(duì)列贸弥。延時(shí)隊(duì)列中的元素要到指定的事件后才能訪問窟坐,因此可以實(shí)現(xiàn)定時(shí)任務(wù)。延時(shí)隊(duì)列可以通過Redis的ZSet數(shù)據(jù)類型來實(shí)現(xiàn)绵疲,使用
zadd(key, 目的執(zhí)行時(shí)間, value)
指令往延時(shí)隊(duì)列添加任務(wù)哲鸳,再通過zrangebyscore(key, 0, 當(dāng)前時(shí)間)
指令得到目的執(zhí)行時(shí)間超過當(dāng)前時(shí)間的任務(wù),執(zhí)行完后通過zrem(key, value)
指令來刪除已經(jīng)執(zhí)行的任務(wù)盔憨,從而保證延時(shí)隊(duì)列里的每個(gè)任務(wù)只執(zhí)行一次徙菠。去重計(jì)數(shù)器。我們可以通過Redis的Hyperloglog實(shí)現(xiàn)去重計(jì)數(shù)器郁岩,使用
pfadd
指令往Redis里面添加元素婿奔,再通過pfcount
指令獲取元素個(gè)數(shù)。布隆過濾器问慎。Redis提供了布隆過濾器萍摊,我們可以通過
bf.add
指令往Redis里面添加元素,再通過bf.exists
指令來判斷某個(gè)元素是否存在如叼,從而實(shí)現(xiàn)過濾的效果冰木,比如過濾垃圾郵件。-
限流笼恰。用Redis實(shí)現(xiàn)限流有兩種方式:一種是可以通過ZSet數(shù)據(jù)類型實(shí)現(xiàn)簡單限流踊沸;另一種是可以直接使用Redis-Cell模塊,里面提供了漏斗限流的實(shí)現(xiàn)方式社证,我們可以直接使用指令
cl.throttle [key] [capacity] [allowed_ops_num] [period] [now_ops_num]
來判斷操作是否允許執(zhí)行逼龟,進(jìn)而實(shí)現(xiàn)限流的效果。其中capacity
為漏斗容量追葡,period
和allowed_ops_num
代表時(shí)間段內(nèi)允許的操作數(shù)(即漏水速率rate
)腺律,now_ops_num
代表本次執(zhí)行的操作數(shù)奕短。
消息隊(duì)列。普通隊(duì)列可通過Redis的list數(shù)據(jù)類型實(shí)現(xiàn)匀钧,
lpush
生產(chǎn)消息篡诽,rpop
消費(fèi)消息。消息多播可通過Redis的PubSub模塊實(shí)現(xiàn)榴捡,publish
指令發(fā)布消息,subscribe
指令訂閱消息朱浴,相比list實(shí)現(xiàn)而言吊圾,發(fā)布訂閱機(jī)制支持多個(gè)消費(fèi)者消費(fèi)同一條消息。相比發(fā)布訂閱機(jī)制而言翰蠢,Redis的Stream模塊更有消息持久化和消費(fèi)組的特性项乒,可通過xadd
指令生產(chǎn)消息,通過xread
指令消費(fèi)消息梁沧。
4. Redis高性能
Redis一般被看做單線程組件檀何,因?yàn)樗木W(wǎng)絡(luò)IO和指令處理都在單個(gè)線程中執(zhí)行,之所以Redis能達(dá)到單機(jī)10w TPS的處理量級與它優(yōu)雅的設(shè)計(jì)密不可分:
- Redis采用單線程來處理用戶請求廷支,省去了加鎖和上下文切換的開銷频鉴。
- Redis的數(shù)據(jù)讀寫操作都在內(nèi)存中,省去了持久化時(shí)磁盤尋址和磁盤讀寫的開銷恋拍,相對硬盤而言垛孔,內(nèi)存讀寫的開銷低到可忽略不計(jì)。
- 處理網(wǎng)絡(luò)IO時(shí)采用了IO多路復(fù)用機(jī)制施敢,非阻塞地讀寫網(wǎng)絡(luò)數(shù)據(jù)周荐,單個(gè)線程就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)通道的事件。
- 處理重負(fù)荷任務(wù)時(shí)僵娃,Redis會fork一個(gè)子進(jìn)程進(jìn)行處理概作,如執(zhí)行
bgrewriteaof
操作重寫AOF文件,執(zhí)行bgsave
命令進(jìn)行快照默怨,以及master全量復(fù)制讯榕。除此之外,Redis服務(wù)器在啟動時(shí)會啟動三個(gè)BIO進(jìn)程先壕,分別處理文件關(guān)閉瘩扼、AOF 緩沖數(shù)據(jù)刷盤,以及清理對象的操作垃僚。
5. Redis與Memcache
Redis和Memcache都是常用的緩存組件集绰,那么Redis和Memcache相比,又有啥區(qū)別和優(yōu)勢劣勢呢谆棺?只有明白各自的適用場合栽燕,才能更好地選擇緩存組件:
- 網(wǎng)絡(luò)IO模型:Redis和Memcache都采用了多路復(fù)用網(wǎng)絡(luò)IO模型罕袋,但是Memcache將處理線程分為負(fù)責(zé)監(jiān)聽的主線程和負(fù)責(zé)請求處理的worker子線程,可以發(fā)揮多核計(jì)算機(jī)的優(yōu)勢碍岔,Redis的單線程處理模型能省去加鎖和線程上下文切換的開銷浴讯。
- 數(shù)據(jù)類型:Memcache只支持以key-value形式存儲和訪問數(shù)據(jù),而Redis支持多種數(shù)據(jù)類型蔼啦,如String榆纽、List、Set捏肢、ZSet和Hash奈籽。
- 內(nèi)存管理機(jī)制:Memcached默認(rèn)使用Slab Allocation機(jī)制管理內(nèi)存,預(yù)分配一大塊內(nèi)存作為內(nèi)存池鸵赫,將其分為大小不同的chunk來管理內(nèi)存衣屏,存儲數(shù)據(jù)時(shí)根據(jù)數(shù)據(jù)大小選擇合適的chunk來存儲,這種方式雖然避免了內(nèi)存碎片辩棒,但是會造成一定的空間浪費(fèi)狼忱;Redis使用現(xiàn)場申請內(nèi)存的方式來管理內(nèi)存,會在一定程度上造成內(nèi)存碎片一睁。
- 數(shù)據(jù)持久化:Memcache不支持?jǐn)?shù)據(jù)持久化钻弄,而Redis可采用RDB和AOF這兩種方式持久化數(shù)據(jù)。
- 集群管理方式:Memcached本身并不支持分布式卖局,只能在客戶端通過像一致性哈希這樣的分布式算法來實(shí)現(xiàn)Memcached的分布式存儲斧蜕。Redis則在服務(wù)器端構(gòu)建分布式存儲,它的Cluster集群管理方案將所有數(shù)據(jù)劃分為16384個(gè)slots砚偶,每個(gè)節(jié)點(diǎn)對應(yīng)一個(gè)slot批销,每個(gè)slot對應(yīng)多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都含有slot與所有節(jié)點(diǎn)的映射信息染坯,客戶端訪問任何節(jié)點(diǎn)都可以重定向到正確的節(jié)點(diǎn)均芽。
Redis學(xué)習(xí)都包含哪些知識點(diǎn)
到這里,這一節(jié)關(guān)于Redis的簡介已經(jīng)介紹完了单鹿。這里繼續(xù)介紹一下Redis的學(xué)習(xí)脈絡(luò)掀宋,同時(shí)為后續(xù)博客打一個(gè)預(yù)告:
- 詳細(xì)學(xué)習(xí)Redis的數(shù)據(jù)類型以及內(nèi)部的存儲方式:Redis中的字符串采用SDS的結(jié)構(gòu)來存儲,對于String類型仲锄,當(dāng)它為數(shù)值時(shí)采用int編碼劲妙,當(dāng)它為短字符串時(shí)采用embstr編碼,只需一次內(nèi)存申請儒喊,當(dāng)它為長字符串時(shí)采用raw編碼镣奋,需多次申請內(nèi)存;對于List而言怀愧,其底層數(shù)據(jù)采用quicklist結(jié)構(gòu)來存儲侨颈,當(dāng)元素多時(shí)采用鏈表實(shí)現(xiàn)余赢;對于Set而言,它的底層采用hashtable實(shí)現(xiàn)哈垢;對于ZSet而言妻柒,當(dāng)元素少時(shí)采用ziplist實(shí)現(xiàn),元素多時(shí)采用hashtable+skiplist的方式實(shí)現(xiàn)耘分;對于Hash而言举塔,元素少時(shí)采用ziplist實(shí)現(xiàn),元素多時(shí)采用hashtable實(shí)現(xiàn)求泰。
- Redis的高級數(shù)據(jù)結(jié)構(gòu)啤贩,如緊湊列表和基數(shù)樹,及其在Redis中的應(yīng)用拜秧。
- 跳出數(shù)據(jù)類型,縱觀Redis數(shù)據(jù)庫的整體存儲方式章郁,Redis總共有16個(gè)db枉氮,每個(gè)db包含兩個(gè)dict,一個(gè)存儲數(shù)據(jù)暖庄,一個(gè)存儲過期時(shí)間聊替。每個(gè)dict中包含兩個(gè)dictht(即hashtable),一個(gè)用于日常存儲數(shù)據(jù)培廓,另一個(gè)在漸進(jìn)式rehash時(shí)用到惹悄。再緊接著就是每個(gè)dictht中存儲Redis中的key-value數(shù)據(jù)了,其中value可指向不同的Redis數(shù)據(jù)類型肩钠。
- Redis的過期鍵刪除泣港、內(nèi)存回收、內(nèi)存超限淘汰機(jī)制价匠,是fork子進(jìn)程異步執(zhí)行還是在主線程中執(zhí)行当纱,是立即執(zhí)行還是通過定時(shí)任務(wù)執(zhí)行。
- Redis中的持久化機(jī)制踩窖,包括RDB和AOF的執(zhí)行策略(主進(jìn)程執(zhí)行還是子進(jìn)程執(zhí)行坡氯,定時(shí)還是非定時(shí)、刷盤策略)洋腮,持久化時(shí)對過期鍵的處理箫柳。
- Redis的客戶端與服務(wù)器管理,安全使用Redis啥供。
- Redis中的多機(jī)數(shù)據(jù)庫的實(shí)現(xiàn)悯恍,包括主從同步(全量/增量以及實(shí)現(xiàn)方式)、Sentinel監(jiān)控和故障轉(zhuǎn)移滤灯、Cluster集群管理方案坪稽。
- Redis的高級功能及其實(shí)現(xiàn)曼玩,如發(fā)布訂閱機(jī)制、Stream窒百、事務(wù)黍判、管道。
- 其他:Lua腳本篙梢、key操作顷帖、對象操作、數(shù)據(jù)庫操作渤滞、排序贬墩、慢查詢?nèi)罩尽⒈O(jiān)視器妄呕。
- 詳細(xì)解說Redis的應(yīng)用陶舞,如分布式鎖、布隆過濾器...
- 從Redis到分布式緩存绪励,解析分布式緩存的重點(diǎn)難點(diǎn):如緩存失效肿孵、緩存穿透、緩存雪崩等等
讀者如果有什么疑問或者建議可以在下方留言疏魏,我將會在看到的第一時(shí)間給予解答或者完善文章停做,提升自己也惠及他人