一. Redis簡(jiǎn)介

前言

最近,我從Redis入手,對(duì)分布式緩存進(jìn)行了學(xué)習(xí)探究。為了記錄我的學(xué)習(xí)成果,我將會(huì)寫一系列博客來介紹Redis和分布式緩存。在學(xué)習(xí)過程中,我在網(wǎng)上查閱了一些關(guān)于Redis的資料,發(fā)現(xiàn)網(wǎng)上的資料比較零散,而且良莠不齊,讓人很難縱觀Redis的全局。同時(shí),網(wǎng)上也有很多課程和專欄,它們也講得很系統(tǒng),但是不同專欄和課程間形成了一座座信息孤島,有些知識(shí)點(diǎn)只是某些課程或者專欄有講到,很難做到面面俱到。因此,我希望用一系列博客來縱觀Redis,既總覽Redis又不放過每一個(gè)重要原理和細(xì)枝末節(jié)。廢話不多說,讓我們一起開始Redis之旅吧^_^

1. Redis是什么

Redis是“Remote Dictionary Service”(即遠(yuǎn)程字典服務(wù))的縮寫,它是一個(gè)由ANSI C 語言開發(fā)的,開放源代碼(BSD許可)的key-value存儲(chǔ)組建。它的所有數(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,針對(duì)每種數(shù)據(jù)類型,Redis提供了不同的操作指令進(jìn)行數(shù)據(jù)操作。大多數(shù)人可能只對(duì)前五種比較熟悉,我在這里先簡(jiǎn)述下后面三種,接下來會(huì)有專門的博客來詳細(xì)介紹各種數(shù)據(jù)類型。

Bitmap即位圖,每一bit只能取值為為0或1,底層通過字節(jié)數(shù)組來存儲(chǔ),每個(gè)字節(jié)有8個(gè)bit。我們可以通過Bitmap來存儲(chǔ)一系列YN標(biāo)志,比如記錄用戶每天的簽到情況,每個(gè)bit代表一天,0代表沒簽到,1代表已簽到。


bitmap.png

Geo是Redis的地理位置模塊,底層通過GeoHash算法將二維的地理坐標(biāo)轉(zhuǎn)換為一維的數(shù)值,再存儲(chǔ)在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ù)存儲(chǔ)依然采用字節(jié)數(shù)組的形式,每6bit代表一個(gè)桶,用于存儲(chǔ)這個(gè)桶的元素個(gè)數(shù),Redis采用16384( 2^14 )個(gè)桶實(shí)現(xiàn),因此一共占用2^14*8/6=12k的存儲(chǔ)空間。Hyperloglog的存儲(chǔ)方式跟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)之路。對(duì)于Redis而言,它的應(yīng)用處取決于它的特性,以及它向我們提供了什么數(shù)據(jù)類型和操作指令。

  1. 緩存數(shù)據(jù)。從廣義上來說,緩存是用于數(shù)據(jù)快速交換的存儲(chǔ)介質(zhì)。基于Redis的內(nèi)存存儲(chǔ)特性,我們可以使用它來緩存熱點(diǎn)數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)的快速存取。

  2. 分布式鎖。在分布式系統(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,從而釋放鎖。


    分布式鎖.png
  3. 延時(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í)行一次。

  4. 去重計(jì)數(shù)器。我們可以通過Redis的Hyperloglog實(shí)現(xiàn)去重計(jì)數(shù)器,使用pfadd指令往Redis里面添加元素,再通過pfcount指令獲取元素個(gè)數(shù)。

  5. 布隆過濾器。Redis提供了布隆過濾器,我們可以通過bf.add指令往Redis里面添加元素,再通過bf.exists指令來判斷某個(gè)元素是否存在,從而實(shí)現(xiàn)過濾的效果,比如過濾垃圾郵件。

  6. 限流。用Redis實(shí)現(xiàn)限流有兩種方式:一種是可以通過ZSet數(shù)據(jù)類型實(shí)現(xiàn)簡(jiǎn)單限流;另一種是可以直接使用Redis-Cell模塊,里面提供了漏斗限流的實(shí)現(xiàn)方式,我們可以直接使用指令cl.throttle [key] [capacity] [allowed_ops_num] [period] [now_ops_num]來判斷操作是否允許執(zhí)行,進(jìn)而實(shí)現(xiàn)限流的效果。其中capacity為漏斗容量,periodallowed_ops_num代表時(shí)間段內(nèi)允許的操作數(shù)(即漏水速率rate),now_ops_num代表本次執(zhí)行的操作數(shù)。

    限流圖解.png

  7. 消息隊(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的處理量級(jí)與它優(yōu)雅的設(shè)計(jì)密不可分:

  1. Redis采用單線程來處理用戶請(qǐng)求,省去了加鎖和上下文切換的開銷。
  2. Redis的數(shù)據(jù)讀寫操作都在內(nèi)存中,省去了持久化時(shí)磁盤尋址和磁盤讀寫的開銷,相對(duì)硬盤而言,內(nèi)存讀寫的開銷低到可忽略不計(jì)。
  3. 處理網(wǎng)絡(luò)IO時(shí)采用了IO多路復(fù)用機(jī)制,非阻塞地讀寫網(wǎng)絡(luò)數(shù)據(jù),單個(gè)線程就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)通道的事件。
  4. 處理重負(fù)荷任務(wù)時(shí),Redis會(huì)fork一個(gè)子進(jìn)程進(jìn)行處理,如執(zhí)行bgrewriteaof操作重寫AOF文件,執(zhí)行bgsave命令進(jìn)行快照,以及master全量復(fù)制。除此之外,Redis服務(wù)器在啟動(dòng)時(shí)會(huì)啟動(dòng)三個(gè)BIO進(jìn)程,分別處理文件關(guān)閉、AOF 緩沖數(shù)據(jù)刷盤,以及清理對(duì)象的操作。

5. Redis與Memcache

Redis和Memcache都是常用的緩存組件,那么Redis和Memcache相比,又有啥區(qū)別和優(yōu)勢(shì)劣勢(shì)呢?只有明白各自的適用場(chǎng)合,才能更好地選擇緩存組件:

  1. 網(wǎng)絡(luò)IO模型:Redis和Memcache都采用了多路復(fù)用網(wǎng)絡(luò)IO模型,但是Memcache將處理線程分為負(fù)責(zé)監(jiān)聽的主線程和負(fù)責(zé)請(qǐng)求處理的worker子線程,可以發(fā)揮多核計(jì)算機(jī)的優(yōu)勢(shì),Redis的單線程處理模型能省去加鎖和線程上下文切換的開銷。
  2. 數(shù)據(jù)類型:Memcache只支持以key-value形式存儲(chǔ)和訪問數(shù)據(jù),而Redis支持多種數(shù)據(jù)類型,如String、List、Set、ZSet和Hash。
  3. 內(nèi)存管理機(jī)制:Memcached默認(rèn)使用Slab Allocation機(jī)制管理內(nèi)存,預(yù)分配一大塊內(nèi)存作為內(nèi)存池,將其分為大小不同的chunk來管理內(nèi)存,存儲(chǔ)數(shù)據(jù)時(shí)根據(jù)數(shù)據(jù)大小選擇合適的chunk來存儲(chǔ),這種方式雖然避免了內(nèi)存碎片,但是會(huì)造成一定的空間浪費(fèi);Redis使用現(xiàn)場(chǎng)申請(qǐng)內(nèi)存的方式來管理內(nèi)存,會(huì)在一定程度上造成內(nèi)存碎片。
  4. 數(shù)據(jù)持久化:Memcache不支持?jǐn)?shù)據(jù)持久化,而Redis可采用RDB和AOF這兩種方式持久化數(shù)據(jù)。
  5. 集群管理方式:Memcached本身并不支持分布式,只能在客戶端通過像一致性哈希這樣的分布式算法來實(shí)現(xiàn)Memcached的分布式存儲(chǔ)。Redis則在服務(wù)器端構(gòu)建分布式存儲(chǔ),它的Cluster集群管理方案將所有數(shù)據(jù)劃分為16384個(gè)slots,每個(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè)slot,每個(gè)slot對(duì)應(yīng)多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都含有slot與所有節(jié)點(diǎn)的映射信息,客戶端訪問任何節(jié)點(diǎn)都可以重定向到正確的節(jié)點(diǎn)。

Redis學(xué)習(xí)都包含哪些知識(shí)點(diǎn)

到這里,這一節(jié)關(guān)于Redis的簡(jiǎn)介已經(jīng)介紹完了。這里繼續(xù)介紹一下Redis的學(xué)習(xí)脈絡(luò),同時(shí)為后續(xù)博客打一個(gè)預(yù)告:

  1. 詳細(xì)學(xué)習(xí)Redis的數(shù)據(jù)類型以及內(nèi)部的存儲(chǔ)方式:Redis中的字符串采用SDS的結(jié)構(gòu)來存儲(chǔ),對(duì)于String類型,當(dāng)它為數(shù)值時(shí)采用int編碼,當(dāng)它為短字符串時(shí)采用embstr編碼,只需一次內(nèi)存申請(qǐng),當(dāng)它為長(zhǎng)字符串時(shí)采用raw編碼,需多次申請(qǐng)內(nèi)存;對(duì)于List而言,其底層數(shù)據(jù)采用quicklist結(jié)構(gòu)來存儲(chǔ),當(dāng)元素多時(shí)采用鏈表實(shí)現(xiàn);對(duì)于Set而言,它的底層采用hashtable實(shí)現(xiàn);對(duì)于ZSet而言,當(dāng)元素少時(shí)采用ziplist實(shí)現(xiàn),元素多時(shí)采用hashtable+skiplist的方式實(shí)現(xiàn);對(duì)于Hash而言,元素少時(shí)采用ziplist實(shí)現(xiàn),元素多時(shí)采用hashtable實(shí)現(xiàn)。
  2. Redis的高級(jí)數(shù)據(jù)結(jié)構(gòu),如緊湊列表和基數(shù)樹,及其在Redis中的應(yīng)用。
  3. 跳出數(shù)據(jù)類型,縱觀Redis數(shù)據(jù)庫的整體存儲(chǔ)方式,Redis總共有16個(gè)db,每個(gè)db包含兩個(gè)dict,一個(gè)存儲(chǔ)數(shù)據(jù),一個(gè)存儲(chǔ)過期時(shí)間。每個(gè)dict中包含兩個(gè)dictht(即hashtable),一個(gè)用于日常存儲(chǔ)數(shù)據(jù),另一個(gè)在漸進(jìn)式rehash時(shí)用到。再緊接著就是每個(gè)dictht中存儲(chǔ)Redis中的key-value數(shù)據(jù)了,其中value可指向不同的Redis數(shù)據(jù)類型。
  4. Redis的過期鍵刪除、內(nèi)存回收、內(nèi)存超限淘汰機(jī)制,是fork子進(jìn)程異步執(zhí)行還是在主線程中執(zhí)行,是立即執(zhí)行還是通過定時(shí)任務(wù)執(zhí)行。
  5. Redis中的持久化機(jī)制,包括RDB和AOF的執(zhí)行策略(主進(jìn)程執(zhí)行還是子進(jìn)程執(zhí)行,定時(shí)還是非定時(shí)、刷盤策略),持久化時(shí)對(duì)過期鍵的處理。
  6. Redis的客戶端與服務(wù)器管理,安全使用Redis。
  7. Redis中的多機(jī)數(shù)據(jù)庫的實(shí)現(xiàn),包括主從同步(全量/增量以及實(shí)現(xiàn)方式)、Sentinel監(jiān)控和故障轉(zhuǎn)移、Cluster集群管理方案。
  8. Redis的高級(jí)功能及其實(shí)現(xiàn),如發(fā)布訂閱機(jī)制、Stream、事務(wù)、管道。
  9. 其他:Lua腳本、key操作、對(duì)象操作、數(shù)據(jù)庫操作、排序、慢查詢?nèi)罩尽⒈O(jiān)視器。
  10. 詳細(xì)解說Redis的應(yīng)用,如分布式鎖、布隆過濾器...
  11. 從Redis到分布式緩存,解析分布式緩存的重點(diǎn)難點(diǎn):如緩存失效、緩存穿透、緩存雪崩等等

讀者如果有什么疑問或者建議可以在下方留言,我將會(huì)在看到的第一時(shí)間給予解答或者完善文章,提升自己也惠及他人

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容