Redis的各項功能解決了哪些問題?

来源:http://www.cnblogs.com/linianhui/archive/2017/11/06/what-problem-does-redis-solve.html
-Advertisement-
Play Games

先看一下Redis是一個什麼東西。官方簡介解釋到:Redis是一個基於BSD開源的項目,是一個把結構化的數據放在記憶體中的一個存儲系統,你可以把它作為資料庫,緩存和消息中間件來使用。同時支持strings,lists,hashes,sets,sorted sets,bitmaps,hyperloglo ...


先看一下Redis是一個什麼東西。官方簡介解釋到:Redis是一個基於BSD開源的項目,是一個把結構化的數據放在記憶體中的一個存儲系統,你可以把它作為資料庫,緩存和消息中間件來使用。同時支持strings,lists,hashes,sets,sorted sets,bitmaps,hyperloglogs和geospatial indexes等數據類型。它還內建了複製,lua腳本,LRU,事務等功能,通過redis sentinel實現高可用,通過redis cluster實現了自動分片。以及事務,發佈/訂閱,自動故障轉移等等。

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

1 從零開始

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

隨後我們就著手於如何提升一下api消費者感知的性能,很快最簡單粗暴的第一個方案就出來了:為API的響應加上基於HTTP的緩存控制 cache-control:max-age=600 ,即讓消費者可以緩存這個響應十分鐘。如果api消費者如果有效的利用了響應中的緩存控制信息,則可以有效的改善其感知的性能(10分鐘以內)。但是還有2個弊端:第一個是在緩存生效的10分鐘內,api消費者可能會得到舊的數據;第二個是如果api的客戶端無視緩存直接訪問API依然是需要2秒,治標不治本吶。

2 基於本機記憶體的緩存

為瞭解決調用API依然需要2秒的問題,經過排查,其主要原因在於使用SQL獲取熱點新聞的過程中消耗了將近2秒的時間,於是乎,我們又想到了一個簡單粗暴的解決方案,即把SQL查詢的結果直接緩存在當前api伺服器的記憶體中(設置緩存有效時間為1分鐘)。後續1分鐘內的請求直接讀緩存,不再花費2秒去執行SQL了。假如這個api每秒接收到的請求時100個,那麼一分鐘就是6000個,也就是只有前2秒擁擠過來的請求會耗時2秒,後續的58秒中的所有請求都可以做到即使響應,而無需再等2秒的時間。

其他API的小伙伴發現這是個好辦法,於是很快我們就發現API伺服器的記憶體要爆滿了。。。

3 服務端的Redis

在API伺服器的記憶體都被緩存塞滿的時候,我們發現不得不另想解決方案了。最直接的想法就是我們把這些緩存都丟到一個專門的伺服器上吧,把它的記憶體配置的大大的。然後我們就盯上了redis。。。至於如何配置部署redis這裡不解釋了,redis官方有詳細的介紹。隨後我們就用上了一臺單獨的伺服器作為Redis的伺服器,API伺服器的記憶體壓力得以解決。

3.1 持久化(Persistence)

單台的Redis伺服器一個月總有那麼幾天心情不好,心情不好就罷工了,導致所有的緩存都丟失了(redis的數據是存儲在記憶體的嘛)。雖然可以把Redis伺服器重新上線,但是由於記憶體的數據丟失,造成了緩存雪崩,API伺服器和資料庫的壓力還是一下子就上來了。所以這個時候Redis的持久化功能就派上用場了,可以緩解一下緩存雪崩帶來的影響。redis的持久化指的是redis會把記憶體的中的數據寫入到硬碟中,在redis重新啟動的時候載入這些數據,從而最大限度的降低緩存丟失帶來的影響。

3.2 哨兵(Sentinel)和複製(Replication)

Redis伺服器毫無徵兆的罷工是個麻煩事。那麼怎辦辦?答曰:備份一臺,你掛了它上。那麼如何得知某一臺redis伺服器掛了,如何切換,如何保證備份的機器是原始伺服器的完整備份呢?這時候就需要Sentinel和Replication出場了。Sentinel可以管理多個Redis伺服器,它提供了監控,提醒以及自動的故障轉移的功能;Replication則是負責讓一個Redis伺服器可以配備多個備份的伺服器。Redis也是利用這兩個功能來保證Redis的高可用的。此外,Sentinel功能則是對Redis的發佈和訂閱功能的一個利用。

3.3 集群(Cluster)

單台伺服器資源的總是有上限的,CPU資源和IO資源我們可以通過主從複製,進行讀寫分離,把一部分CPU和IO的壓力轉移到從伺服器上。但是記憶體資源怎麼辦,主從模式做到的只是相同數據的備份,並不能橫向擴充記憶體;單台機器的記憶體也只能進行加大處理,但是總有上限的。所以我們就需要一種解決方案,可以讓我們橫向擴展。最終的目的既是把每台伺服器只負責其中的一部分,讓這些所有的伺服器構成一個整體,對外界的消費者而言,這一組分散式的伺服器就像是一個集中式的伺服器一樣(之前在解讀REST的博客中解釋過分散式於基於網路的差異:基於網路應用的架構)。

在Redis官方的分散式方案出來之前,有twemproxy和codis兩種方案,這兩個方案總體上來說都是依賴proxy來進行分散式的,也就是說redis本身並不關心分散式的事情,而是交由twemproxy和codis來負責。而redis官方給出的cluster方案則是把分散式的這部分事情做到了每一個redis伺服器中,使其不再需要其他的組件就可以獨立的完成分散式的要求。我們這裡不關心這些方案的優略,我們關註一下這裡的分散式到底是要處理那些事情?也就是twemproxy和codis獨立處理的處理分散式的這部分邏輯和cluster集成到redis服務的這部分邏輯到底在解決什麼問題?

如我們前面所說的,一個分散式的服務在外界看來就像是一個集中式的服務一樣。那麼要做到這一點就面臨著有一個問題需要解決:既是增加或減少分散式服務中的伺服器的數量,對消費這個服務的客戶端而言應該是無感的;那麼也就意味著客戶端不能穿透分散式服務,把自己綁死到某一個台的伺服器上去,因為一旦如此,你就再也無法新增伺服器,也無法進行故障替換。解決這個問題有兩個路子:第一個路子最直接,那就是我加一個中間層來隔離這種具體的依賴,即twemproxy採用的方式,讓所有的客戶端只能通過它來消費redsi服務,通過它來隔離這種依賴(但是你會發現twermproxy會成為一個單點),這種情況下每台redis伺服器都是獨立的,它們之間彼此不知對方的存在;第二個路子是讓redis伺服器知道彼此的存在,通過重定向的機制來引導客戶端來完成自己所需要的操作,比如客戶端鏈接到了某一個redis伺服器,說我要執行這個操作,redis伺服器發現自己無法完成這個操作,那麼就把能完成這個操作的伺服器的信息給到客戶端,讓客戶端去請求另外的一個伺服器,這時候你就會發現每一個redis伺服器都需要保持一份完整的分散式伺服器信息的一份資料,不然它怎麼知道讓客戶端去找其他的哪個伺服器來執行客戶端想要的操作呢。

上面這一大段解釋了這麼多,不知有沒有發現不管是第一個路子還是第二個路子,都有一個共同的東西存在,那就是分散式服務中所有伺服器以及其能提供的服務的信息。這些信息無論如何也是要存在的,區別在於第一個路子是把這部分信息單獨來管理,用這些信息來協調後端的多個獨立的redis伺服器;第二個路子則是讓每一個redis伺服器都持有這份信息,彼此知道對方的存在,來達成和第一個路子一樣的目的,優點是不再需要一個額外的組件來處理這部分事情。

Redis Cluster的具體實現細節則是採用了Hash槽的概念,即預先分配出來16384個槽:在客戶端通過對Key進行CRC16(key)% 16384運算得到對應的槽是哪一個;在redis服務端則是每個伺服器負責一部分槽,當有新的伺服器加入或者移除的時候,再來遷移這些槽以及其對應的數據,同時每個伺服器都持有完整的槽和其對應的伺服器的信息,這就使得伺服器端可以進行對客戶端的請求進行重定向處理。

4 客戶端的Redis

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

4.1 數據類型

redis支持豐富的數據類型,從最基礎的string到複雜的常用到的數據結構都有支持:

  1. string:最基本的數據類型,二進位安全的字元串,最大512M。
  2. list:按照添加順序保持順序的字元串列表。
  3. set:無序的字元串集合,不存在重覆的元素。
  4. sorted set:已排序的字元串集合。
  5. hash:key-value對的一種集合。
  6. bitmap:更細化的一種操作,以bit為單位。
  7. hyperloglog:基於概率的數據結構。

這些眾多的數據類型,主要是為了支持各種場景的需要,當然每種類型都有不同的時間複雜度。其實這些複雜的數據結構相當於之前我在《解讀REST》這個系列博客基於網路應用的架構風格中介紹到的遠程數據訪問(Remote Data Access = RDA)的具體實現,即通過在伺服器上執行一組標準的操作命令,在服務端之間得到想要的縮小後的結果集,從而簡化客戶端的使用,也可以提高網路性能。比如如果沒有list這種數據結構,你就只能把list存成一個string,客戶端拿到完整的list,操作後再完整的提交給redis,會產生很大的浪費。

4.2 事務

上述數據類型中,每一個數據類型都有獨立的命令來進行操作,很多情況下我們需要一次執行不止一個命令,而且需要其同時成功或者失敗。redis對事務的支持也是源自於這部分需求,即支持一次性按順序執行多個命令的能力,並保證其原子性。

4.3 Lua腳本

在事務的基礎上,如果我們需要在服務端一次性的執行更複雜的操作(包含一些邏輯判斷),則lua就可以排上用場了(比如在獲取某一個緩存的時候,同時延長其過期時間)。redis保證lua腳本的原子性,一定的場景下,是可以代替redis提供的事務相關的命令的。相當於基於網路應用的架構風格中介紹到的遠程求值(Remote Evluation = REV)的具體實現。

4.4 管道

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

4.5 分散式鎖

官方推薦採用Redlock演算法,即使用string類型,加鎖的時候給的一個具體的key,然後設置一個隨機的值;取消鎖的時候用使用lua腳本來先執行獲取比較,然後再刪除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 0
end

總結

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

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

參考

Redis 文檔:https://github.com/antirez/redis-doc

Redis 簡介:https://redis.io/topics/introduction

Redis 持久化(Persistence):https://redis.io/topics/persistence

Redis 發佈/訂閱(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 數據類型(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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 服務端字元集修改 1、確認服務端字元集 1 select userenv('language') from dual; 2、修改服務端字元集 首先以 DBA 身份登錄 Oracle。Windows 系統下直接在命令行下運行命令 sqlplus /as sysdba 或在 SQL PLUS 下運行命令 ...
  • 創建三台虛擬機,IP地址為:192.168.169.101,192.168.169.102,192.168.169.103 將192.168.169.102為namenode,192.168.169.101,192.168.169.103為datanode 關閉防火牆,安裝JDK1.8,設置SSH無 ...
  • --場景1: A B a 1 a 2 a 3 b 1 b 2 b 3 希望實現如下效果: a 1,2,3 b 4,5 create table tmp as select 'a' A, 1 B from dual union all select 'a' A, 2 B from dual union... ...
  • 安裝Redis 下載redis安裝包http://download.redis.io/redis-stable.tar.gz 解壓安裝包tar xzf redis-stable.tar.gz 安裝cd redis-stable/srcmake 或者直接從網上下載redis的壓縮包,然後解壓,再用ma ...
  • Redis的7個應用場景 一:緩存——熱數據 熱點數據(經常會被查詢,但是不經常被修改或者刪除的數據),首選是使用redis緩存,畢竟強大到冒泡的QPS和極強的穩定性不是所有類似工具都有的,而且相比於memcached還提供了豐富的數據類型可以使用,另外,記憶體中的數據也提供了AOF和RDB等持久化機 ...
  • db.getCollection('product').update({status:"offline"},{$set:{status:"online"}},false,true) update更新,把所有status為offline的都改為online,註意加引號 db.collection.up... ...
  • MySQL資料庫的性能的影響分析及其優化 MySQL資料庫的性能的影響 一. 伺服器的硬體的限制 二. 伺服器所使用的操作系統 三. 伺服器的所配置的參數設置不同 四. 資料庫存儲引擎的選擇 五. 資料庫的參數配置的不同 六. (重點)資料庫的結構的設計和SQL語句 1). 伺服器的配置和設置(cp ...
  • 避免自己遺忘,在這裡做個筆記: SET XACT_ABORT ON:強制事務回滾,如果不加這句的話事務有可能回滾失敗。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...