1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Redis 的各項功能解決了哪些問題?(1024程序員節(jié)快樂)

        共 5717字,需瀏覽 12分鐘

         ·

        2021-10-26 12:42

        ??

        先看一下 Redis 是一個什么東西。官方簡介解釋到:
        Redis 是一個基于 BSD 開源的項目,是一個把結(jié)構(gòu)化的數(shù)據(jù)放在內(nèi)存中的一個存儲系統(tǒng),你可以把它作為數(shù)據(jù)庫,緩存和消息中間件來使用。同時支持 strings,lists,hashes,sets,sorted sets,bitmaps,hyperloglogs 和 geospatial indexes 等數(shù)據(jù)類型。它還內(nèi)建了復制,lua 腳本,LRU,事務等功能,通過redis sentinel 實現(xiàn)高可用,通過 redis cluster 實現(xiàn)了自動分片。以及事務,發(fā)布/訂閱,自動故障轉(zhuǎn)移等等。

        綜上所述,Redis 提供了豐富的功能,初次見到可能會感覺眼花繚亂,這些功能都是干嘛用的?都解決了什么問題?什么情況下才會用到相應的功能?那么下面從零開始,一步一步的演進來粗略的解釋下。

        1. 從零開始

        最初的需求非常簡單,我們有一個提供熱點新聞列表的 api:http://api.xxx.com/hot-news,api 的消費者抱怨說每次請求都要2秒左右才能返回結(jié)果。

        隨后我們就著手于如何提升一下 api 消費者感知的性能,很快最簡單粗暴的第一個方案就出來了:為 API 的響應加上基于 HTTP 的緩存控制 cache-control:max-age=600?,即讓消費者可以緩存這個響應十分鐘。

        如果 api 消費者如果有效的利用了響應中的緩存控制信息,則可以有效的改善其感知的性能(10分鐘以內(nèi))。

        但是還有2個弊端:第一個是在緩存生效的10分鐘內(nèi),api 消費者可能會得到舊的數(shù)據(jù);第二個是如果 api 的客戶端無視緩存直接訪問 API 依然是需要2秒,治標不治本吶。

        2. 基于本機內(nèi)存的緩存

        為了解決調(diào)用 API 依然需要2秒的問題,經(jīng)過排查,其主要原因在于使用 SQL 獲取熱點新聞的過程中消耗了將近2秒的時間,于是乎,我們又想到了一個簡單粗暴的解決方案,即把 SQL 查詢的結(jié)果直接緩存在當前 api 服務器的內(nèi)存中(設置緩存有效時間為1分鐘)。

        后續(xù)1分鐘內(nèi)的請求直接讀緩存,不再花費2秒去執(zhí)行 SQL 了。假如這個 api 每秒接收到的請求是100個,那么一分鐘就是6000個,也就是只有前2秒擁擠過來的請求會耗時2秒,后續(xù)的58秒中的所有請求都可以做到即使響應,而無需再等2秒的時間。

        其他 API 的小伙伴發(fā)現(xiàn)這是個好辦法,于是很快我們就發(fā)現(xiàn) API 服務器的內(nèi)存要爆滿了。。。

        3.?服務端的 Redis

        在 API 服務器的內(nèi)存都被緩存塞滿的時候,我們發(fā)現(xiàn)不得不另想解決方案了。最直接的想法就是我們把這些緩存都丟到一個專門的服務器上吧,把它的內(nèi)存配置的大大的。然后我們就盯上了redis。。。至于如何配置部署 redis 這里不解釋了,redis 官方有詳細的介紹。

        隨后我們就用上了一臺單獨的服務器作為 Redis 的服務器,API 服務器的內(nèi)存壓力得以解決。

        3.1 持久化(Persistence)

        單臺的 Redis 服務器一個月總有那么幾天心情不好,心情不好就罷工了,導致所有的緩存都丟失了(redis 的數(shù)據(jù)是存儲在內(nèi)存的嘛)。

        雖然可以把 Redis 服務器重新上線,但是由于內(nèi)存的數(shù)據(jù)丟失,造成了緩存雪崩,API 服務器和數(shù)據(jù)庫的壓力還是一下子就上來了。
        所以這個時候 Redis 的持久化功能就派上用場了,可以緩解一下緩存雪崩帶來的影響。

        redis 的持久化指的是 redis 會把內(nèi)存中的數(shù)據(jù)寫入到硬盤中,在 redis 重新啟動的時候加載這些數(shù)據(jù),從而最大限度的降低緩存丟失帶來的影響。

        3.2 哨兵(Sentinel)和復制(Replication)

        Redis 服務器毫無征兆的罷工是個麻煩事。那么怎辦辦?答曰:備份一臺,你掛了它上。

        那么如何得知某一臺 redis 服務器掛了,如何切換,如何保證備份的機器是原始服務器的完整備份呢?這時候就需要 Sentinel 和 Replication 出場了。

        Sentinel 可以管理多個 Redis 服務器,它提供了監(jiān)控,提醒以及自動的故障轉(zhuǎn)移的功能;Replication 則是負責讓一個 Redis 服務器可以配備多個備份的服務器。

        Redis 也是利用這兩個功能來保證 Redis 的高可用的。此外,Sentinel 功能則是對 Redis 的發(fā)布和訂閱功能的一個利用。

        3.3 集群(Cluster)

        單臺服務器資源的總是有上限的,CPU 資源和 IO 資源我們可以通過主從復制,進行讀寫分離,把一部分 CPU 和 IO 的壓力轉(zhuǎn)移到從服務器上。

        但是內(nèi)存資源怎么辦,主從模式做到的只是相同數(shù)據(jù)的備份,并不能橫向擴充內(nèi)存;單臺機器的內(nèi)存也只能進行加大處理,但是總有上限的。

        所以我們就需要一種解決方案,可以讓我們橫向擴展。最終的目的既是把每臺服務器只負責其中的一部分,讓這些所有的服務器構(gòu)成一個整體,對外界的消費者而言,這一組分布式的服務器就像是一個集中式的服務器一樣(之前在解讀 REST 的博客中解釋過分布式于基于網(wǎng)絡的差異:基于網(wǎng)絡應用的架構(gòu))。

        在 Redis 官方的分布式方案出來之前,有 twemproxy 和 codis 兩種方案,這兩個方案總體上來說都是依賴 proxy 來進行分布式的,也就是說 redis 本身并不關(guān)心分布式的事情,而是交由 twemproxy 和 codis 來負責。

        而 redis 官方給出的 cluster 方案則是把分布式的這部分事情做到了每一個 redis 服務器中,使其不再需要其他的組件就可以獨立的完成分布式的要求。

        我們這里不關(guān)心這些方案的優(yōu)略,我們關(guān)注一下這里的分布式到底是要處理哪些事情。也就是 twemproxy 和 codis 獨立處理的處理分布式的這部分邏輯和 cluster 集成到 redis 服務的這部分邏輯到底在解決什么問題?

        如我們前面所說的,一個分布式的服務在外界看來就像是一個集中式的服務一樣。

        那么要做到這一點就面臨著有一個問題需要解決:既是增加或減少分布式服務中的服務器的數(shù)量,對消費這個服務的客戶端而言應該是無感的;那么也就意味著客戶端不能穿透分布式服務,把自己綁死到某一個臺的服務器上去,因為一旦如此,你就再也無法新增服務器,也無法進行故障替換。

        解決這個問題有兩個路子:
        第一個路子最直接,那就是我加一個中間層來隔離這種具體的依賴,即 twemproxy 采用的方式,讓所有的客戶端只能通過它來消費 redis 服務,通過它來隔離這種依賴(但是你會發(fā)現(xiàn) twermproxy 會成為一個單點),這種情況下每臺 redis 服務器都是獨立的,它們之間彼此不知對方的存在;

        第二個路子是讓 redis 服務器知道彼此的存在,通過重定向的機制來引導客戶端來完成自己所需要的操作,比如客戶端鏈接到了某一個 redis 服務器,說我要執(zhí)行這個操作,redis 服務器發(fā)現(xiàn)自己無法完成這個操作,那么就把能完成這個操作的服務器的信息給到客戶端,讓客戶端去請求另外的一個服務器,這時候你就會發(fā)現(xiàn)每一個 redis 服務器都需要保持一份完整的分布式服務器信息的一份資料,不然它怎么知道讓客戶端去找其他的哪個服務器來執(zhí)行客戶端想要的操作呢。

        上面這一大段解釋了這么多,不知有沒有發(fā)現(xiàn)不管是第一個路子還是第二個路子,都有一個共同的東西存在,那就是分布式服務中所有服務器以及其能提供的服務的信息。

        這些信息無論如何也是要存在的,區(qū)別在于第一個路子是把這部分信息單獨來管理,用這些信息來協(xié)調(diào)后端的多個獨立的 redis 服務器;第二個路子則是讓每一個 redis 服務器都持有這份信息,彼此知道對方的存在,來達成和第一個路子一樣的目的,優(yōu)點是不再需要一個額外的組件來處理這部分事情。

        Redis Cluster 的具體實現(xiàn)細節(jié)則是采用了 Hash 槽的概念,即預先分配出來16384個槽:在客戶端通過對 Key 進行 CRC16(key)% 16384運算得到對應的槽是哪一個;

        在 redis 服務端則是每個服務器負責一部分槽,當有新的服務器加入或者移除的時候,再來遷移這些槽以及其對應的數(shù)據(jù),同時每個服務器都持有完整的槽和其對應的服務器的信息,這就使得服務器端可以進行對客戶端的請求進行重定向處理。

        4.?客戶端的 Redis

        上面的第三小節(jié)主要介紹的是 Redis 服務端的演進步驟,解釋了 Redis 如何從一個單機的服務,進化為一個高可用的、去中心化的、分布式的存儲系統(tǒng)。這一小節(jié)則是關(guān)注下客戶端可以消費的 redis 服務。

        4.1 數(shù)據(jù)類型

        redis 支持豐富的數(shù)據(jù)類型,從最基礎的 string 到復雜的常用到的數(shù)據(jù)結(jié)構(gòu)都有支持:
        1. string:最基本的數(shù)據(jù)類型,二進制安全的字符串,最大512M。

        2. list:按照添加順序保持順序的字符串列表。

        3. set:無序的字符串集合,不存在重復的元素。

        4. sorted set:已排序的字符串集合。

        5. hash:key-value 對的一種集合。

        6. bitmap:更細化的一種操作,以 bit 為單位。

        7. hyperloglog:基于概率的數(shù)據(jù)結(jié)構(gòu)。


        這些眾多的數(shù)據(jù)類型,主要是為了支持各種場景的需要,當然每種類型都有不同的時間復雜度。

        其實這些復雜的數(shù)據(jù)結(jié)構(gòu)相當于之前我在《解讀REST》這個系列博客基于網(wǎng)絡應用的架構(gòu)風格中介紹到的遠程數(shù)據(jù)訪問(Remote Data Access = RDA)的具體實現(xiàn),即通過在服務器上執(zhí)行一組標準的操作命令,在服務端之間得到想要的縮小后的結(jié)果集,從而簡化客戶端的使用,也可以提高網(wǎng)絡性能。

        比如如果沒有 list 這種數(shù)據(jù)結(jié)構(gòu),你就只能把 list 存成一個 string,客戶端拿到完整的 list,操作后再完整的提交給 redis,會產(chǎn)生很大的浪費。

        4.2 事務

        上述數(shù)據(jù)類型中,每一個數(shù)據(jù)類型都有獨立的命令來進行操作,很多情況下我們需要一次執(zhí)行不止一個命令,而且需要其同時成功或者失敗。

        redis 對事務的支持也是源自于這部分需求,即支持一次性按順序執(zhí)行多個命令的能力,并保證其原子性。

        4.3 Lua 腳本

        在事務的基礎上,如果我們需要在服務端一次性的執(zhí)行更復雜的操作(包含一些邏輯判斷),則 lua 就可以派上用場了(比如在獲取某一個緩存的時候,同時延長其過期時間)。

        redis 保證 lua 腳本的原子性,一定的場景下,是可以代替 redis 提供的事務相關(guān)的命令的。相當于基于網(wǎng)絡應用的架構(gòu)風格中介紹到的遠程求值(Remote Evluation = REV)的具體實現(xiàn)。

        4.4 管道

        因為 redis 的客戶端和服務器的連接時基于 TCP 的, 默認每次連接都時只能執(zhí)行一個命令。管道則是允許利用一次連接來處理多條命令,從而可以節(jié)省一些 tcp 連接的開銷。管道和事務的差異在于管道是為了節(jié)省通信的開銷,但是并不會保證原子性。

        4.5 分布式鎖

        官方推薦采用 Redlock 算法,即使用 string 類型,加鎖的時候給的一個具體的 key,然后設置一個隨機的值;取消鎖的時候用使用 lua 腳本來先執(zhí)行獲取比較,然后再刪除 key。

        具體的命令如下:
        SET resource_name my_random_value NX PX 30000
        if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end

        總結(jié)

        本篇著重從抽象層面來解釋下 redis 的各項功能以及其存在的目的,而沒有關(guān)心其具體的細節(jié)是什么。從而可以聚焦于其解決的問題,依據(jù)抽象層面的概念可以使得我們在特定的場景下選擇更合適的方案,而非局限于其技術(shù)細節(jié)。

        以上均是筆者個人的一些理解,如果不當之處,歡迎指正。


        參考

        Redis 文檔:https://github.com/antirez/redis-doc
        Redis 簡介:https://redis.io/topics/introduction
        Redis 持久化(Persistence):https://redis.io/topics/persistence
        Redis 發(fā)布/訂閱(Pub/Sub):https://redis.io/topics/pubsub
        Redis 哨兵(Sentinel):https://redis.io/topics/sentinel
        Redis 復制(Replication):https://redis.io/topics/replication
        Redis 集群(cluster):https://redis.io/topics/cluster-tutorial
        RedIs 事務(Transaction):https://redis.io/topics/transactions
        Redis 數(shù)據(jù)類型(data types):https://redis.io/topics/data-types-intro
        Redis 分布式鎖:https://redis.io/topics/distlock
        Redis 管道(pipelining ):https://redis.io/topics/pipelining
        Redis Lua Script:https://redis.io/commands/eval
        作者:blackheart
        地址:cnblogs.com/linianhui/p/7792512.html
        猜你喜歡
        深入理解HBase Memstore
        Hadoop 數(shù)據(jù)遷移用法詳解
        Hbase修復工具Hbck
        數(shù)倉建模分層理論
        一文搞懂Hive的數(shù)據(jù)存儲與壓縮
        大數(shù)據(jù)組件重點學習這幾個
        ?

        ?

        瀏覽 39
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            不卡视频在线观看 | 日韩精品一区二区三区费 | 久久久三级 | 丁香五月开心五月 | 小坏蛋轻点阿受不了丝袜 | 台湾无码在线播放 | 做爰 视频毛片视频 | 我吃哺乳老师的奶水 | 操逼12p | 日日噜噜夜夜狠狠爱视频免费樱桃 |