1. 程式人生 > >Redis學習筆記(三)---物件

Redis學習筆記(三)---物件

Redis 物件

簡介

Redis是一種key/value型資料庫.Redis並沒有直接使用前面提到的簡單動態字串、雙端連結串列、字典、壓縮列表、整數集合.而是基於這些資料結構建立一個物件系統,這個系統包括字串物件、列表物件、雜湊物件、集合物件和有序物件這個五種物件.每種物件都用到了至少一種我們前面所介紹的資料結構.

物件型別

Redis共有五種物件的型別,分別是:

型別常量 物件的名稱
REDIS_STRING 字串物件
REDIS_LIST 列表物件
REDIS_HASH 雜湊物件
REDIS_SET 集合物件
REDIS_ZSET 有序集合物件

物件定義

Redis中物件的資料結構定義:

typedef struct redisObject {  

    // 型別  
    unsigned type:4;          

    // 不使用(對齊位)  
    unsigned notused:2;  

    // 編碼方式  
    unsigned encoding:4;  

    // LRU 時間(相對於 server.lruclock)  
    unsigned lru:22;  

    // 引用計數  
    int refcount;  

    // 指向物件的值  
void *ptr; } robj;

type表示了該物件的物件型別,即上面五個中的一個。但為了提高儲存效率與程式執行效率,每種物件的底層資料結構實現都可能不止一種。encoding就表示了物件底層所使用的編碼。下面先介紹每種底層資料結構的實現,再介紹每種物件型別都用了什麼底層結構並分析他們之間的關係。

物件底層的資料結構

編碼常量 編碼鎖對應的底層資料結構
REDIS_ENCODING_INT long型別的整數
REDIS_ENCODING_EMBSTR embstr編碼的簡單動態字串
REDIS_ENCODING_RAW 簡單動態字串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 雙端連結串列
REDIS_ENCODING_ZIPLIST 壓縮列表
REDIS_ENCODING_INTSET 整數集合
REDIS_ENCODING_SKIPLIST 跳躍表和字典

字串物件

字串物件的編碼可以是int、raw或者embstr;
如果一個字串的內容可以轉換為long,那麼該字串就會被轉換成為long型別,物件的ptr就會指向該long,並且物件型別也用int型別表示。
普通的字串有兩種,embstr和raw。embstr應該是Redis 3.0新增的資料結構,在2.8中是沒有的。如果字串物件的長度小於39位元組,就用embstr物件。否則用傳統的raw物件。可以從下面這段程式碼看出:

#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39  
robj *createStringObject(char *ptr, size_t len) {  
    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)  
        return createEmbeddedStringObject(ptr,len);  
    else  
        return createRawStringObject(ptr,len);  
}  

embstr的好處有如下幾點:
embstr的建立只需分配一次記憶體,而raw為兩次(一次為sds分配物件,另一次為objet分配物件,embstr省去了第一次)。
相對地,釋放記憶體的次數也由兩次變為一次。
embstr的objet和sds放在一起,更好地利用快取帶來的優勢。
需要注意的是,redis並未提供任何修改embstr的方式,即embstr是隻讀的形式。對embstr的修改實際上是先轉換為raw再進行修改。

大家看上面的巨集定義:
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
為什麼是39呢?
知乎上一個人的回答解決了我的疑惑:
點選檢視原因
raw和embstr的區別可以用下面兩幅圖所示:
這裡寫圖片描述
https://img-blog.csdn.net/20161222132621025?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hvYW1peWFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast” alt=”這裡寫圖片描述” title=”” />t/20161222132351833?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hvYW1peWFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
編碼轉換:
embstr沒有提供修改的程式,當要修改時,先轉換為raw,再進行修改.

列表物件

列表物件的編碼可以是ziplist或者linkedlist。
ziplist是一種壓縮連結串列,它的好處是更能節省記憶體空間,因為它所儲存的內容都是在連續的記憶體區域當中的。當列表物件元素不大,每個元素也不大的時候,就採用ziplist儲存。但當資料量過大時就ziplist就不是那麼好用了。因為為了保證他儲存內容在記憶體中的連續性,插入的複雜度是O(N),即每次插入都會重新進行realloc。如下圖所示,物件結構中ptr所指向的就是一個ziplist。整個ziplist只需要malloc一次,它們在記憶體中是一塊連續的區域。

這裡寫圖片描述
linkedlist是一種雙向連結串列。它的結構比較簡單,節點中存放pre和next兩個指標,還有節點相關的資訊。當每增加一個node的時候,就需要重新malloc一塊記憶體。

這裡寫圖片描述
編碼轉換
當列表物件可以同時滿足以下兩個條件時,列表物件使用ziplist編碼:
1.列表物件儲存的所有字串元素的長度都小於64個位元組.
2.列表物件儲存的元素數量小於512個,不能滿足這兩個條件的列表物件需要使用linkedlist編碼.

雜湊物件

雜湊物件的底層實現可以是ziplist或者hashtable。

ziplist中的雜湊物件是按照key1,value1,key2,value2這樣的順序存放來儲存的。當物件數目不多且內容不大時,這種方式效率是很高的。
hashtable的是由dict這個結構來實現的:

typedef struct dict {  
    dictType *type;  
    void *privdata;  
    dictht ht[2];  
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */  
    int iterators; /* number of iterators currently running */  
} dict;  

dict是一個字典,其中的指標dicht ht[2] 指向了兩個雜湊表


typedef struct dictht {  
    dictEntry **table;  
    unsigned long size;  
    unsigned long sizemask;  
    unsigned long used;  
} dictht;  

dicht[0] 是用於真正存放資料,dicht[1]一般在雜湊表元素過多進行rehash的時候用於中轉資料。
dictht中的table用語真正存放元素了,每個key/value對用一個dictEntry表示,放在dictEntry陣列中。
這裡寫圖片描述
編碼轉換
當列表物件可以同時滿足以下兩個條件時,列表物件使用ziplist編碼:
1.列表物件儲存的所有字串元素的長度都小於64個位元組.
2.列表物件儲存的元素數量小於512個,不能滿足這兩個條件的列表物件需要使用hashtable編碼.

集合物件

集合物件的編碼可以是intset或者hashtable。
intset是一個整數集合,裡面存的為某種同一型別的整數,支援如下三種長度的整數:

#define INTSET_ENC_INT16 (sizeof(int16_t))  
#define INTSET_ENC_INT32 (sizeof(int32_t))  
#define INTSET_ENC_INT64 (sizeof(int64_t))  

intset是一個有序集合,查詢元素的複雜度為O(logN),但插入時不一定為O(logN),因為有可能涉及到升級操作。比如當集合裡全是int16_t型的整數,這時要插入一個int32_t,那麼為了維持集合中資料型別的一致,那麼所有的資料都會被轉換成int32_t型別,涉及到記憶體的重新分配,這時插入的複雜度就為O(N)了。是intset不支援降級操作。如下圖:
這裡寫圖片描述
hashtable編碼的集合物件使用字典作為底層實現,字典的每個鍵都是一個字串物件,每個字串物件包含一個集合元素,而字典的值則全部設定為NULL。如下圖:
這裡寫圖片描述
編碼轉換
當集合可以同時滿足以下兩個條件時,物件使用intset編碼
1.集合物件儲存的所有元素都是整數值
2.集合物件儲存的元素的數量不超過512個.
不滿足這兩個條件使用hashtable編碼.

有序集合物件

有序集合的編碼可能兩種,一種是ziplist,另一種是skiplist與dict的結合。

ziplist作為集合和作為雜湊物件是一樣的,member和score順序存放。按照score從小到大順序排列。它的結構不再複述。

skiplist是一種跳躍表,它實現了有序集合中的快速查詢,在大多數情況下它的速度都可以和平衡樹差不多。但它的實現比較簡單,可以作為平衡樹的替代品。它的結構比較特殊。下面分別是跳躍表skiplist和它內部的節點:

/* 
 * 跳躍表 
 */  
typedef struct zskiplist {  
    // 頭節點,尾節點  
    struct zskiplistNode *header, *tail;  
    // 節點數量  
    unsigned long length;  
    // 目前表內節點的最大層數  
    int level;  
} zskiplist;  
/* ZSETs use a specialized version of Skiplists */  
/* 
 * 跳躍表節點 
 */  
typedef struct zskiplistNode {  
    // member 物件  
    robj *obj;  
    // 分值  
    double score;  
    // 後退指標  
    struct zskiplistNode *backward;  
    // 層  
    struct zskiplistLevel {  
        // 前進指標  
        struct zskiplistNode *forward;  
        // 這個層跨越的節點數量  
        unsigned int span;  
    } level[];  
} zskiplistNode; 

跳躍表的實現及介紹點選這裡 
ziplist編碼的有序集合物件使用壓縮列表作為底層實現,每個集合元素使用兩個緊挨在一起的壓縮列表節點儲存,第一個節點儲存元素的成員,第二個元素則儲存元素的分值.如下圖:
這裡寫圖片描述
skiplist編碼的有序集合物件使用zset結構作為底層實現,一個zset結構同時包含一個字典和一個跳躍表:

typedef struct zset{
    zskiplist *zs1;
    dict *dict; 
}zset;

這裡寫圖片描述
為什麼有序集合需要同時使用跳躍表和字典來實現呢?
在理論上,有序集合可以單獨使用字典或跳躍表的任何一個數據結構來實現,但是,單獨使用字典還是跳躍表,在效能上對比起同時使用字典和跳躍表都會有所降低.
如果只使用字典來實現有序集合,雖然查詢是O(1),但是字典以無序儲存集合元素,所以在每次執行範圍操作時都要對字典進行排序.
如果只使用跳躍表,範圍查詢快。但是根據成員查詢複雜度將變成O(N).
因此,為了讓有序集合的查詢和範圍操作都儘可能的快。Redis 同時使用字典和跳躍表.

注意:
字典和跳躍表會共享成員和分值,並不會造成任何資料重複,也會因此浪費記憶體.
編碼轉換
當有序集合物件可以同時滿足下面兩個條件時,物件使用ziplist編碼:
1.有序集合爆粗會呢的元素數量小於128個.
2.有序集合的所有成員長度小於64.
不能滿足任何一個使用skiplist編碼.

總結

介紹了Redis的五種物件型別和它們的底層實現。事實上,Redis的高效性和靈活性正是得益於對於同一個物件型別採取不同的底層結構,並在必要的時候對二者進行轉換;以及各種底層結構對記憶體的合理利用。

相關推薦

Redis學習筆記()---物件

Redis 物件 簡介 Redis是一種key/value型資料庫.Redis並沒有直接使用前面提到的簡單動態字串、雙端連結串列、字典、壓縮列表、整數集合.而是基於這些資料結構建立一個物件系統,這個系統包括字串物件、列表物件、雜湊物件、集合物件和有序物件

redis 學習筆記

緩解 實時 代理 水平擴展 命令連接 事件 都沒有 分數 能力 一、redis 復制 數據庫復制指的是發生在不同數據庫實例之間,單向的信息傳播的行為,通常由被復制方和復制方組成,被復制方和復制方之間建立網絡連接,復制方式通常為被復制方主動將數據發送到復制方,復制方接收到數據

Redis學習筆記() Redis主從架構和主從從架構 (1)

準備 修改pidfile 為下面做準備 關閉RDB持久化修改持久化檔案的儲存位置 啟動Redis redis-server /etc/redis.conf 使用客戶端連線Redisredis-cli 連線成功,接下來就可以愉快的玩耍啦~~~ 主從複製(讀寫分離)

redis 學習筆記(佇列功能)

Redis佇列功能介紹 List 常用命令: Blpop刪除,並獲得該列表中的第一元素,或阻塞,直到有一個可用 Brpop刪除,並獲得該列表中的最後一個元素,或阻塞,直到有一個可用 Brpoplpush Lindex獲取一個元素,通過其索引列表 Linsert在列表中的另一個元素之前或之後插入一個元素 Ll

Redis學習筆記】慢查詢、pipeline、釋出訂閱、Bitmap、HyperLogLog、GEO

目錄 慢查詢 pipeline 釋出訂閱 Bitmap HyperLogLog GEO 慢查詢 生命週期 傳送命令 --> 排隊 --> 執行命令 --> 返回結

Redis學習筆記)常用命令整理

mes ember nbsp end 插入 學習筆記 頻道 hash value Redis 常用命令 1.DEL key 刪除key2.EXISTS key 檢查key是否存在3.KEYS * 查看所有的key4.EXPIRE key seconds 設置key的過期時

Redis學習筆記--Redis客戶端(

本機 -c trace 圖形 tro cli family 毫秒 ati 1.Redis客戶端 1.1 Redis自帶的客戶端   (1)啟動   啟動客戶端命令:[root@kwredis bin]# ./redis-cli -h 127.0.0.1 -p 6379

2018/08/14 《Redis 入門指南》 學習筆記()

大於 tsa 16px 什麽 get 執行 returns 圖片 font 讀   第五章《實踐》   第七章《持久化》 總結 PS:   在實踐章節涉及到了很多語言 Node/Python等。因為這些語言目前還沒有涉及。所以只挑選有關的 PHP 章節來做講解。 1

redis學習筆記

lis 記錄 筆記 style pytho font 通過 num 獲取 列表類型 List,可以存儲一個有序的字符中列表,列表內元素非唯一,可以向兩端加入元素,或者獲得列表的一個片段 內部使用雙向鏈表實現,兩端添加元素負責度O(1),通過索引訪問的速度較慢 可以用在分頁,

JSP學習筆記之response物件和request物件

接著上一篇,我們接著講JSP中的內建隱式物件。這篇部落格介紹的是request和response物件。 A. request物件      request物件是javax.servlet.http.HttpServletReq

[Scala]學習筆記——面向物件

一、面向物件的概 (Object Oriented——OO) 1.封裝: 將屬性、方法封裝到類中 2.繼承: 父類和子類之間的關係 3.多型: 父類引用指向子類物件 多型是面向物件程式設計的精髓所在,是開發框架的基礎 二、類的定義和使用 //main方法 def main(ar

Objective-C基礎學習筆記()-面向物件的三大特性之封裝

面向物件的三大特性:封裝(成員變數)、繼承、多型; 一、 封裝 1. 封裝的理解:     在開發過程中,考慮到安全性要求,我們通常不讓外界直接修改我們類的成員變數,而讓外界使用我們提供的方法來修改,這樣類 的成員變數就           封裝起來了。 2. 封裝的目的就

Redis學習筆記之一---- Redis種啟動方式

原文地址:http://futeng.iteye.com/blog/2071867?utm_source=tuicool redis原始碼下載地址:http://download.csdn.net/detail/haitunxiaomo/8647255 Part I.

【黑馬程式設計師】Objective-C語言學習筆記物件的建立、使用和方法呼叫(

--------------------------------------------IOS期待與您交流!-------------------------------------------- 一、物件的建立 物件是由類建立,我們使用上一文章用到的類來建立物件。 說明:

Redis學習筆記(六) 物件

前面我們看了Redis用到的主要資料結構,如簡單動態字串(SDS)、雙向連結串列、字典、壓縮列表、整數集合等。 但是Redis並沒有直接使用這些資料結構來實現鍵值對,而是基於這些資料結構建立了一個物件系統,這個系統包括字串物件、列表物件、雜湊物件、集合物件、有序集合物件,除此之外,redis的物件系統還實現了

Linux學習筆記():系統執行級與執行級的切換

查看 用戶操作 回車 water hat ntsysv tde 文件表 config 1.Linux系統與其它的操作系統不同,它設有執行級別。該執行級指定操作系統所處的狀態。Linux系統在不論什麽時候都執行於某個執行級上,且在不同的執行級上執行的程序和服務都不同,所要

Redis學習筆記~Twenproxy所起到的作用

out arm mdb ntp ddd pin alq odi mib 回到目錄 Twenproxy除了可以作為redis的代理,它同樣支持memerycached。我這裏主要了解Twemproxy在redis集群上的解決方案。Twemproxy除了完美的解決了分片,路由

【Unity 3D】學習筆記十:遊戲元素——遊戲地形

nbsp 3d遊戲 strong 直觀 分辨率 == 摩擦力 fill 世界 遊戲地形 在遊戲的世界中,必然會有非常多豐富多彩的遊戲元素融合當中。它們種類繁多。作用也不大同樣。一般對於遊戲元素可分為兩種:經經常使用。不經經常使用。經常使用的元素是遊戲中比較重要的元素。一

redis學習教程《發送訂閱、事務、連接》

微軟雅黑 pin 發布者 tail 順序 mil visitor 模式 b- redis學習教程三《發送訂閱、事務、連接》 一:發送訂閱 Redis發布訂閱(pub/sub)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。Redis 發

Redis學習筆記3-Redis5個可運行程序命令的使用

運行程序 檢查 mil 數據文件 img usr pre text mod 在redis安裝文章中,說到安裝好redis後,在/usr/local/bin下有5個關於redis的可運行程序。以下關於這5個可運行程序命令的具體說明。 redis-server Redi