主頁 >  其他 > 犧牲速度來節省記憶體,Redis是覺得自己太快了嗎

犧牲速度來節省記憶體,Redis是覺得自己太快了嗎

2021-01-17 12:15:24 其他

犧牲速度來節省記憶體,Redis是覺得自己太快了嗎

  • 前言
  • 什么是壓縮串列
  • ziplist 的存盤結構
    • entry 存盤結構
      • prevlen
      • encoding
      • entry-data
    • ziplist 資料示例
    • ziplist 連鎖更新問題
  • 總結

前言

本文GitHub已收錄:https://zhouwenxing.github.io/

正常情況下我們選擇使用 Redis 就是為了提升查詢速度,然而讓人意外的是,Redis 當中卻有一種比較有意思的資料結構,這種資料結構通過犧牲部分讀寫速度來達到節省記憶體的目的,這就是 ziplist(壓縮串列),Redis 為什么要這么做呢?難道真的是覺得自己的速度太快了,犧牲一點速度也不影響嗎?

什么是壓縮串列

ziplist 是為了節省記憶體而設計出來的一種資料結構,ziplist 是由一系列特殊編碼組成的連續記憶體塊的順序型資料結構,一個 ziplist 可以包含任意多個 entry,而每一個 entry 又可以保存一個位元組陣列或者一個整數值,

ziplist 作為一種串列,其和普通的雙端串列,如 linkedlist 的最大區別就是 ziplist 并不存盤前后節點的指標,而 linkedlist 一般每個節點都會維護一個指向前置節點和一個指向后置節點的指標,那么 ziplist 不維護前后節點的指標,它又是如何尋找前后節點的呢?

ziplist 雖然不維護前后節點的指標,但是它卻維護了上一個節點的長度和當前節點的長度,然后每次通過長度來計算出前后節點的位置,既然涉及到了計算,那么相對于直接存盤指標的方式肯定有性能上的損耗,這就是一種典型的用時間來換取空間的做法,因為每次讀取前后節點都需要經過計算才能得到前后節點的位置,所以會消耗更多的時間,而在 Redis 中,一個指標是占了 8 個位元組,但是大部分情況下,如果直接存盤長度是達不到 8 個位元組的,所以采用存盤長度的設計方式在大部分場景下是可以節省記憶體空間的,

ziplist 的存盤結構

ziplist 的組成結構為:

<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>

其中 zlbyteszltailzllenziplisthead 部分,entryziplistentries 部分,每一個 entry 代表一個資料,最后 zlend 表示 ziplistend 部分,如下圖所示:
在這里插入圖片描述

ziplist 中每個屬性代表的含義如下表格所示:

屬性型別長 度說明
zlbytesuint32_t4位元組記錄壓縮串列占用記憶體位元組數(包括本身所占用的 4 個位元組),
zltailuint32_t4位元組記錄壓縮串列尾節點距離壓縮串列的起始地址有多少個位元組(通過這個值可以計算出尾節點的地址)
zllenuint16_t2位元組記錄壓縮串列中包含的節點數量,當串列值超過可以存盤的最大值(65535)時,此值固定存盤 65535(即 216 次方 減 1),因此此時需要遍歷整個壓縮串列才能計算出真實節點數,
entry節點-壓縮串列中的各個節點,長度由存盤的實際資料決定,
zlenduint8_t1位元組特殊字符 0xFF(即十進制 255),用來標記壓縮串列的末端(其他正常的節點沒有被標記為255的,因為255用來標識末尾,后面可以看到,正常節點都是標記為254),

entry 存盤結構

ziplistheadend 存的都是長度和標記,而 entry 存盤的是具體元素,這又是經過特殊的設計的一種存盤格式,每個 entry 都以包含兩段資訊的元資料作為前綴,每一個 entry 的組成結構為:

<prevlen> <encoding> <entry-data>

prevlen

prevlen 屬性存盤了前一個 entry 的長度,通過此屬性能夠從后到前遍歷串列, prevlen 屬性的長度可能是 1 位元組也可能是 5 位元組:

  • 當鏈表的前一個 entry 占用位元組數小于 254,此時 prevlen 只用 1 個位元組進行表示,
<prevlen from 0 to 253> <encoding> <entry>
  • 當鏈表的前一個 entry 占用位元組數大于等于 254,此時 prevlen5 個位元組來表示,其中第 1 個位元組的值固定是 254(相當于是一個標記,代表后面跟了一個更大的值),后面 4 個位元組才是真正存盤前一個 entry 的占用位元組數,
0xFE <4 bytes unsigned little endian prevlen> <encoding> <entry>

注意:1 個位元組完全你能存盤 255 的大小,之所以只取到 254 是因為 zlend 就是固定的 255,所以 255 這個數要用來判斷是否是 ziplist 的結尾

encoding

encoding 屬性存盤了當前 entry 所保存資料的型別以及長度,encoding 長度為 1 位元組,2 位元組或者 5 位元組長,前面我們提到,每一個 entry 中可以保存位元組陣列和整數,而 encoding 屬性的第 1 個位元組就是用來確定當前 entry 存盤的是整數還是位元組陣列,當存盤整數時,第 1 個位元組的前兩位總是 11,而存盤位元組陣列時,則可能是 000110 三種中的一種,

  • 當存盤整數時,第 1 個位元組的前 2 位固定為 11,其他位則用來記錄整數值的型別或者整數值(下表所示的編碼中前兩位均為 11):
編碼長度entry保存的資料
110000001位元組int16_t型別整數
110100001位元組int32_t型別整數
111000001位元組int64_t型別整數
111100001位元組24位有符號整數
111111101位元組8位有符號整數
1111xxxx1位元組xxxx 代表區間 0001-1101,存盤了一個介于 0-12 之間的整數,此時 entry-data 屬性被省略

注意:xxxx 四位編碼范圍是 0000-1111,但是 000011111110 已經被表格中前面表示的資料型別占用了,所以實際上的范圍是 0001-1101,此時能保存資料 1-13,再減去 1 之后范圍就是 0-12,至于為什么要減去 1 是從使用習慣來說 0 是一個非常常用的資料,所以才會選擇統一減去 1 來存盤一個 0-12 的區間而不是直接存盤 1-13 的區間,

  • 當存盤位元組陣列時,第 1 個位元組的前 2 位為 0001 或者 10,其他位則用來記錄位元組陣列的長度:
編碼長度entry保存的資料
00pppppp1位元組長度小于等于 63 位元組(6 位)的位元組陣列
01pppppp qqqqqqqq2位元組長度小于等于 16383 位元組(14 位)的位元組陣列
10000000 qqqqqqqq rrrrrrrr ssssssss tttttttt5位元組長度小于等于 232 次方減 132 位)的位元組陣列,其中第 1 個位元組的后 6 位設定為 0,暫時沒有用到,后面的 32 位(4 個位元組)存盤了資料

entry-data

entry-data 存盤的是具體資料,當存盤小整數(0-12)時,因為 encoding 就是資料本身,此時 entry-data 部分會被省略,省略了 entry-data 部分之后的 ziplist 中的 entry 結構如下:

<prevlen> <encoding>

壓縮串列中 entry 的資料結構定義如下(原始碼 ziplist.c 檔案內),當然實際存盤并沒有直接使用到這個結構定義,這個結構只是用來接收資料,所以大家了解一下就可以了:

typedef struct zlentry {
    unsigned int prevrawlensize;//存盤prevrawlen所占用的位元組數
    unsigned int prevrawlen;//存盤上一個鏈表節點需要的位元組數
    unsigned int lensize;//存盤len所占用的位元組數
    unsigned int len;//存盤鏈表當前節點的位元組數
    unsigned int headersize;//當前鏈表節點的頭部大小(prevrawlensize + lensize)即非資料域的大小
    unsigned char encoding;//編碼方式
    unsigned char *p;//指向當前節點的起始位置(因為串列內的資料也是一個字串物件)
} zlentry;

ziplist 資料示例

上面講解了大半天,可能大家都覺得枯燥無味了,也可能會覺得云里霧里,這個沒有關系,這些只要心里有個概念,用到的時候再查詢對應資料就可以了,并不需要全部記住,接下來讓我們一起通過兩個例子來體會一下 ziplist 到底是如何來組織存盤資料的,

下面就是一個壓縮串列的存盤示例,這個壓縮串列里面存盤了 2 個節點,節點中存盤的是整數 25

[0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]
      |             |          |       |       |     |
   zlbytes        zltail     zllen    "2"     "5"   end
  1. 第一組 4 個位元組為 zlbytes 部分,0f 轉成二進制就是 1111 也就是 15,代表整個 ziplist 長度是 15 個位元組,
  2. 第二組 4 個位元組 zltail 部分,0c 轉成二進制就是 1100 也就是 12,這里記錄的是壓縮串列尾節點距離起始地址有多少個位元組,也就是就是說 [02 f6] 這個尾節點距離起始位置有 12 個位元組,
  3. 第三組 2 個位元組就是記錄了當前 ziplistentry 的數量,02 轉成二進制就是 10,也就是說當前 ziplist2 個節點,
  4. 第四組 2 個位元組 [00 f3] 就是第一個 entry00 表示 0,因為這是第 1 個節點,所以前一個節點長度為 0f3 轉成二進制就是 11110011,剛好對應了表格中的編碼 1111xxxx,所以后面四位就是存盤了一個 0-12位的整數,0011 轉成十進制就是 3,減去 1 得到 2,所以第一個 entry 存盤的資料就是 2
  5. 第五組 2 個位元組 [02 f6] 就是第二個 entry02 即為 2,表示前一個節點的長度為 2,注意,因為這里算出來的結果是小于 254,所以就代表了這里只用到了 1 個位元組來存盤上一個節點的長度(如果等于 254,這說明接下來 4 個位元組才存盤的是長度),所以后面的 f6 就是當前節點的資料,轉換成二進制為 11110110,對應了表格中的編碼 1111xxxx,同樣的后四位 0110 存盤的是真實資料,計算之后得出是5,
  6. 最后一組1個位元組[ff]轉成二進制就是 11111111,代表這是整個 ziplist 的結尾,

假如這時候又添加了一個 Hello World 字串到串列中,那么就會新增一個 entry,如下所示:

[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]
  1. 第一組的 1 個位元組 02 轉成十進制就是 2 ,表示前一個節點(即上面示例中的 [02 f6])長度是 2
  2. 第 二組的2 個位元組 0b 轉成二進制為 00001011,以 00 開頭,符合編碼 00pppppp,而除掉最開始的兩位 00,計算之后得到十進制 11,這就說明后面位元組陣列的長度是 11
  3. 第三組剛好是 11 個位元組,對應了上面的長度,所以這里就是真正存盤了 Hello World 的位元組陣列,

ziplist 連鎖更新問題

上面提到 entry 中的 prevlen 屬性可能是 1 個位元組也可能是 5 個位元組,那么我們來設想這么一種場景:假設一個 ziplist 中,連續多個 entry 的長度都是一個接近但是又不到 254 的值(介于 250~253 之間),那么這時候 ziplist 中每個節點都只用了 1 個位元組來存盤上一個節點的長度,假如這時候添加了一個新節點,如 entry1 ,其長度大于 254 個位元組,此時 entry1 的下一個節點 entry2prelen 屬性就必須要由 1 個位元組變為 5 個位元組,也就是需要執行空間重分配,而此時 entry2 因為增加了 4 個位元組,導致長度又大于 254 個位元組了,那么它的下一個節點 entry3prelen 屬性也會被改變為 5 個位元組,依此類推,這種產生連續多次空間重分配的現象就稱之為連鎖更新,同樣的,不僅僅是新增節點,執行洗掉節點操作同樣可能會發生連鎖更新現象,

雖然 ziplist 可能會出現這種連鎖更新的場景,但是一般如果只是發生在少數幾個節點之間,那么并不會嚴重影響性能,而且這種場景發生的概率也比較低,所以實際使用時不用過于擔心,

總結

本文主要講解了 Redis 當中的 ziplist(壓縮串列),一種用時間換取空間的資料結構,在介紹壓縮串列存盤結構的同時通過一個存盤示例來分析了 ziplist 是如何存盤資料的,最后介紹了 ziplist 中可能發生的連鎖更新問題,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/249832.html

標籤:其他

上一篇:5G時代很火的音視頻高級開發學習路線及知識點總結

下一篇:linux

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more