Redis設計思路學習與總結

来源:http://www.cnblogs.com/qcloud1001/archive/2017/03/22/6600200.html
-Advertisement-
Play Games

Redis是業界普遍應用的緩存組件,研究一個組件框架,最直觀的辦法就是從應用方的角度出發,將每個步驟的考慮一番,從這些步驟入手去研究往往能夠最快的體會到一個組件框架的設計哲學。以Redis為例,每當發起一條請求時,redis是如何管理管理網路請求,收到請求後又是通過什麼樣的數據結構進行組織並操作記憶體... ...


下半年利用空餘時間研究和分析了部分Redis源碼,本文從網路模型、數據結構和記憶體管理、持久化和多機協作四個角度對redis的設計思路進行了分析,若有不正確之處,希望各路大神指出。

Redis是業界普遍應用的緩存組件,研究一個組件框架,最直觀的辦法就是從應用方的角度出發,將每個步驟的考慮一番,從這些步驟入手去研究往往能夠最快的體會到一個組件框架的設計哲學。以Redis為例,每當發起一條請求時,Redis是如何管理管理網路請求,收到請求後又是通過什麼樣的數據結構進行組織並操作記憶體,這些數據又是如何dump到磁碟實現持久化,再到多機環境下如何同步和保證一致性……本文就是從網路模型、數據結構設計與記憶體管理、持久化方法和多機四個角度簡要描述了redis的設計和自己的一點體會。

一.網路模型

Redis是典型的基於Reactor的事件驅動模型,單進程單線程,高效的框架總是類似的。網路模型與spp的非同步模型幾乎一致。

Redis流程上整體分為接受請求處理器、響應處理器和應答處理器三個同步模塊,每一個請求都是要經歷這三個部分。

Redis集成了libevent/epoll/kqueue/select等多種事件管理機制,可以根據操作系統版本自由選擇合適的管理機制,其中libevent是最優選擇的機制。

Redis的網路模型有著所有事件驅動模型的優點,高效低耗。但是面對耗時較長的操作的時候,同樣無法處理請求,只能等到事件處理完畢才能響應,之前在業務中也遇到過這樣的場景,刪除redis中全量的key-value,整個操作時間較長,操作期間所有的請求都無法響應。所以瞭解清楚網路模型有助於在業務中揚長避短,減少長耗時的請求,儘可能多一些簡單的短耗時請求發揮非同步模型的最大的威力,事實上在Redis的設計中也多次體現這一點。

二.數據結構和記憶體管理

1.字元串

1.1 結構

Redis的字元串是對C語言原始字元串的二次封裝,結構如下:

struct sdshdr {
    long len;
    long free;
    char buf[];
};

可以看出,每當定義一個字元串時,除了保存字元的空間,Redis還分配了額外的空間用於管理屬性欄位。

1.2 記憶體管理方式

動態記憶體管理方式,動態方式最大的好處就是能夠較為充分的利用記憶體空間,減少記憶體碎片化,與此同時帶來的劣勢就是容易引起頻繁的記憶體抖動,通常採用“空間預分配”和“惰性空間釋放”兩種優化策略來減少記憶體抖動,redis也不例外。

每次修改字元串內容時,首先檢查記憶體空間是否符合要求,否則就擴大2倍或者按M增長;減少字元串內容時,記憶體並不會立刻回收,而是按需回收。

關於記憶體管理的優化,最基本的出發點就是浪費一點空間還是犧牲一些時間的權衡,像STL、tcmalloc、protobuf3的arena機制等採用的核心思路都是“預分配遲回收”,Redis也是一樣的。

1.3 二進位安全

判斷字元串結束與否的標識是len欄位,而不是C語言的'\0',因此是二進位安全的。
放心的將pb序列化後的二進位字元串存入redis。
簡而言之,通過redis的簡單封裝,redis的字元串的操作更加方便,性能更友好,並且屏蔽了C語言字元串的一些需要用戶關心的問題。

2.字典(哈希)

字典的底層一定是hash,涉及到hash一定會涉及到hash演算法、衝突的解決方法和hash表擴容和縮容。

2.1 hash演算法

Redis使用的就是常用的Murmurhash2,Murmurhash演算法能夠給出在任意輸入序列下的散列分佈性,並且計算速度很快。之前做共用記憶體的Local-Cache的需求時也正是利用了Murmurhash的優勢,解決了原有結構的hash函數散列分佈性差的問題。

2.2 hash衝突解決方法

鏈地址法解決hash衝突,通用解決方案沒什麼特殊的。多說一句,如果選用鏈地址解決衝突,那麼勢必要有一個散列性非常好的hash函數,否則hash的性能將會大大折扣。Redis選用了Murmurhash,所以可以放心大膽的採用鏈地址方案。

2.3 hash擴容和縮容

維持hash表在一個合理的負載範圍之內,簡稱為rehash過程。
rehash的過程也是一個權衡的過程,在做評估之前首先明確一點,不管中間採用什麼樣的rehash策略,rehash在巨集觀上看一定是:分配一個新的記憶體塊,老數據搬到新的記憶體塊上,釋放舊記憶體塊。
老數據何時搬?怎麼搬?就變成了一個需要權衡的問題。
第一部分的網路模型上明確的指出Redis的事件驅動模型特點,不適合玩長耗時操作。如果一個hashtable非常大,需要進行擴容就一次性把老數據copy過去,那就會非常耗時,違背事件驅動的特點。所以Redis依舊採用了一種惰性的方案:
新空間分配完畢後,啟動rehashidx標識符表明rehash過程的開始;之後所有增刪改查涉及的操作時都會將數據遷移到新空間,直到老空間數據大小為0表明數據已經全部在新空間,將rehashidx禁用,表明rehash結束。
將一次性的集中問題分而治之,在Redis的設計哲學中體現的淋漓盡致,主要是為了避免大耗時操作,影響Redis響應客戶請求。

3.整數集合

變長整數存儲,整數分為16/32/64三個變長尺度,根據存入的數據所屬的類型,進行規劃。
每次插入新元素都有可能導致尺度升級(例如由16位漲到32位),因此插入整數的時間複雜度為O(n)。這裡也是一個權衡,記憶體空間和時間的一個折中,儘可能節省記憶體。

4.跳躍表

Redis的skilplist和普通的skiplist沒什麼不同,都是冗餘數據實現的從粗到細的多層次鏈表,Redis中應用跳錶的地方不多,常見的就是有序集合。
Redis的跳錶和普通skiplist沒有什麼特殊之處。

5.鏈表

Redis的鏈表是雙向非迴圈鏈表,擁有表頭和表尾指針,對於首尾的操作時間複雜度是O(1),查找時間複雜度O(n),插入時間複雜度O(1)。
Redis的鏈表和普通鏈表沒有什麼特殊之處。

三.AOF和RDB持久化

AOF持久化日誌,RDB持久化實體數據,AOF優先順序大於RDB。

1.AOF持久化

機制:通過定時事件將aof緩衝區內的數據定時寫到磁碟上。

2.AOF重寫

為了減少AOF大小,Redis提供了AOF重寫功能,這個重寫功能做的工作就是創建一個新AOF文件代替老的AOF,並且這個新的AOF文件沒有一條冗餘指令。(例如對list先插入A/B/C,後刪除B/C,再插入D共6條指令,最終狀態為A/D,只需1條指令就可以)
實現原理就是讀現有資料庫的狀態,根據狀態反推指令,跟之前的AOF無關。同樣,為了避免長時間耗時,重寫工作放在子進程進行。

3.RDB持久化

SAVE和BGSAVE兩個命令都是用於生成RDB文件,區別在於BGSAVE會fork出一個子進程單獨進行,不影響Redis處理正常請求。
定時和定次數後進行持久化操作。
簡而言之,RDB的過程其實是比較簡單的,滿足條件後直接去寫RDB文件就結束了。

四.多機和集群

1.主從伺服器

避免單點是所有服務的通用問題,Redis也不例外。解決單點就要有備機,有備機就要解決固有的數據同步問題。

1.1 sync——原始版主從同步

Redis最初的同步做法是sync指令,通過sync每次都會全量數據,顯然每次都全量複製的設計比較消耗資源。改進思路也是常規邏輯,第一次全量,剩下的增量,這就是現在的psync指令的活。

1.2 psync

部分重同步實現的技術手段是“偏移序號+積壓緩衝區”,具體做法如下:
(1)主從分別維護一個seq,主每次完成一個請求便seq+1,從每同步完後更新自己seq;
(2)從每次打算同步時都是攜帶著自己的seq到主,主將自身的seq與從做差結果與積壓緩衝區大小比較,如果小於積壓緩衝區大小,直接從積壓緩衝區取相應的操作進行部分重同步;
(3)否則說明積壓緩衝區不能夠cover掉主從不一致的數據,進行全量同步。
本質做法用空間換時間,顯然在這裡犧牲部分空間換回高效的部分重同步,收益比很大。

2.Sentinel

本質:多主從伺服器的Redis系統,多台主從上加了管理監控,以保證系統高可用性。

3.集群

Redis的官方版集群尚未在工業界普及起來,下麵主要介紹一下集群的管理體系和運轉體系。

2.1 slot-集群單位

集群的數據區由slot組成,每個節點負責的slot是在集群啟動時分配的。

2.2 客戶請求

客戶請求時如果相應數據hash後不屬於請求節點所管理的slots,會給客戶返回MOVED錯誤,並給出正確的slots。
從這個層面看,redis的集群還不夠友好,集群內部的狀態必須由客戶感知。

2.3 容災

主從伺服器,從用於備份主,一旦主故障,從代替主。

通過Redis的研究,深刻體會到的一點就是:所有設計的過程都是權衡和割捨的過程。同樣放到日常的工作和開發中也是如此,一句代碼寫的好不好,一個模塊設計的是否科學,就從速度和記憶體的角度去衡量看是否需要優化,並去評估每一種優化會收益到什麼,同時會損失什麼,收益遠大於損失的就是好的優化,這樣往往對於開發和提升更有針對性,更能提高效率。

 

相關推薦

高性能伺服器架構思路(1)——緩衝策略
系統架構評審方法和思路總結
淺析海量用戶的分散式系統設計(1)


 

此文已由作者授權騰訊雲技術社區發佈,轉載請註明文章出處,獲取更多雲計算技術乾貨,可請前往騰訊雲技術社區
歡迎大家關註騰訊雲技術社區-博客園官方主頁,我們將持續在博客園為大家推薦技術精品文章哦~

 


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

-Advertisement-
Play Games
更多相關文章
  • 一、基本概述 單件模式:確保一個類只有一個實例,並提供一個全局訪問點。 解析如下: 1)首先,該Singleton的構造函數必須是私有的,以保證客戶程式不會通過new()操作產生一個實例,達到實現單例的目的; 2)因為靜態變數的生命周期跟整個應用程式的生命周期是一樣的,所以可以定義一個私有的靜態全局 ...
  • 系統可用率 多級緩存 動態分組切換 DB物理隔離 服務分組隔離 跨機房隔離 漏斗模型 DB限流 系統一般可以分為前端應用系統和後端資料庫系統,前端應用系統實施分散式集群部署技術上是... ...
  • MVVM回顧 經過上一篇文章的介紹,相信你對MVVM的設計思想有所瞭解。MVVM的核心思想就是解耦,View與ViewModel應該感受不到彼此的存在。 View只關心怎樣渲染,而ViewModel只關心怎麼處理邏輯,整個架構由數據進行驅動。不僅View與ViewModel彼此解耦,ViewMode ...
  • Facebook 在2015.9.15發佈了 React Native for Android,把JavaScript 開發技術擴展到了Android平臺。React Native 讓開發者使用 JavaScript 和 React 編寫應用,利用相同的核心代碼就可以創建 基於Web,iOS 和 A... ...
  • 什麼是Maven倉庫 在不用Maven的時候,比如說以前我們用Ant構建項目,在項目目錄下,往往會看到一個名為/lib的子目錄,那裡存放著各類第三方依賴jar文件,如 log4j.jar,junit.jar等等。每建立一個項目,你都需要建立這樣的一個/lib目錄,然後複製一對jar文件,這是很明顯的 ...
  • 在面向對象的軟體設計過程中,一切皆抽象成對象,在一個複雜的軟體系統中有成千上萬甚至百萬千萬級的對象,那麼這麼多對象怎麼去有效的管理?怎麼樣去重覆利用?怎麼去科學合理的組合使其能夠融為一體並且可以順利的工作,這是在面向對象設計中著重需要考慮的問題,那麼我們的前輩有沒有給我們留下來一些科學的指導原則來處 ...
  • 單例模式 一、定義 定義:確保一個類只有一個實例,並提供一個全局訪問點。 理解:保證一個類只有一個實例的一種實現方法(該方法就是為了降低對象之間的耦合度)。 二、代碼塊 1、舉個慄子,拿多線程為慄子,具體看代碼註釋。上代碼~ 二、總結 1、單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提 ...
  • 為什麼要對接改造? 我們公司是做增值稅管理系統的,增值稅系統涉及到開發票的業務,需要與不同的供應商對接開票介面,供應商提供的開票介面,包括四種:A1供應商有兩種,第一種是開票伺服器,第二種是稅盒 A2供應商也是一種是開票伺服器,一種稅盒。 客戶有用稅盒開票的,也有用開票伺服器的,各種情況都有 我們的 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...