日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

redis学习之数据结构与对象(一)

發布時間:2024/2/28 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis学习之数据结构与对象(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.SDS

簡介:Redis沒有采用C語言的以空字符串結尾的字符數組,而是構建一種簡單動態字符串(Simple dynamic string,SDS),并將它作為string的表示。

struct sdshdr {// buf 中已占用空間的長度int len;// buf 中剩余可用空間的長度int free;// 數據空間char buf[]; // 依然以’\0’結尾 };

SDS與C語言的字符串的區別

  • 常數復雜度獲取字符串長度。
  • 通過free杜絕緩存區溢出
  • 空間預分配(如果len小于1MB,那么分配的free將等于len,如果len大于1MB那么分配free1MB)和惰性空間釋放(縮短SDS時不立即回收內存而是記錄到free中)
  • 二進制安全,所以能保存圖片,視頻,音頻文件
    常用操作:

    • get:sdsrange—O(n)
    • set:sdscpy—O(n)
    • create:sdsnew—O(1)
    • len:sdslen—O(1)
        
  • 2.鏈表

    typedef struct listNode {// 前置節點struct listNode *prev;// 后置節點struct listNode *next;// 節點的值void *value;} listNode;typedef struct list {// 表頭節點listNode *head;// 表尾節點listNode *tail;// 節點值復制函數void *(*dup)(void *ptr);// 節點值釋放函數void (*free)(void *ptr);// 節點值對比函數int (*match)(void *ptr, void *key);// 鏈表所包含的節點數量unsigned long len;} list;

    常用操作:

    • rpush: listAddNodeHead —O(1)
    • lpush: listAddNodeTail —O(1)
    • push:listInsertNode —O(1)
    • index : listIndex —O(N)
    • pop:ListFirst/listLast —O(1)
    • llen:listLength —O(N)

    3.字典

    字典在底層是由哈希表實現的,所以我們從哈希表開始入手。

    (1)哈希表

    typedef struct dictht {// 哈希表數組dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩碼,用于計算索引值// 總是等于 size - 1unsigned long sizemask;// 該哈希表已有節點的數量unsigned long used; } dictht;

    (2)哈希節點

    typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;} v;// 指向下個哈希表節點,形成鏈表struct dictEntry *next;

    (3)字典

    typedef struct dict {// 類型特定函數dictType *type;// 私有數據void *privdata;// 哈希表dictht ht[2];// rehash 索引// 當 rehash 不在進行時,值為 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */// 目前正在運行的安全迭代器的數量int iterators; /* number of iterators currently running */} dict;

    (4)字典特定函數

    /** 字典類型特定函數*/ typedef struct dictType {// 計算哈希值的函數unsigned int (*hashFunction)(const void *key);// 復制鍵的函數void *(*keyDup)(void *privdata, const void *key);// 復制值的函數void *(*valDup)(void *privdata, const void *obj);// 對比鍵的函數int (*keyCompare)(void *privdata, const void *key1, const void *key2);// 銷毀鍵的函數void (*keyDestructor)(void *privdata, void *key);// 銷毀值的函數void (*valDestructor)(void *privdata, void *obj);} dictType;

    總結:字典這種數據結構看起來十分復雜,其實十分簡單,只要看過Java的HashMap的源代碼都應該很容易懂。

    注意:
    1. 哈希表有一個數據結構,unsigned long sizemask;有什么用呢,就是讓本來的 “哈希節點的key % size” 計算出索引變成 “哈希節點的key & sizemask”,位運算更加高效,當然前提是哈希節點是2的x次冪。
    2. rehash也是跟Java相似,為什么dict的ht[2]有兩個哈希表呢,原來dict[0]是原來的哈希表,dict[1]是擴容后的哈希表。

    常用操作:

    • Hset: dictAdd/dictReplace —-O(1)
    • Hget: dictFetchValue—O(1)
    • Randomkey:dictGetRandomKey—O(1)
    • Hdel/del: dictDelete—O(1)

    4.整數集合

    整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,并且這個集合的元素數量不多時,Redis就會使用整數集合作為集合鍵的底層實現。

    typedef struct intset {// 編碼方式uint32_t encoding;// 集合包含的元素數量uint32_t length;// 保存元素的數組int8_t contents[];} intset;

    常用操作:

    • sadd:intsetAdd—O(1)
    • smembers:intsetGetO(1)—O(N)
    • srem:intsetRemove—O(N)
    • slen:intsetlen —O(1)

    5.壓縮列表

    壓縮列表是列表鍵和hash鍵的底層實現之一。當一個列表鍵只包含少量列表項,并且每個列表項要么就是小整數值,要么就是長度比較短的字符串,那么redis就會使用壓縮列表來做列表鍵的底層實現。

    zlbytes代表整個壓縮列表占用的內存字節數。
    zltail代表壓縮列表起始地址到尾節點的偏移量。
    zllen節點數量
    entry 節點
    zlend 0xFF標記壓縮列表末端。

    常用操作:

    • Push/hset:ziplistPush/ziplistInsert 平均O(N), 最壞O(N2)
    • Llen/hlen:ziplislen平均O(N), 最壞O(N2)
    • Hget:ziplistGet —-O(1)
    • Index:ziplistIndex—-O(N)
    • Hdel/pop:ZiplistDelete—平均O(N), 最壞O(N2)
    • 遍歷:ziplistPrev/ziplistNext—-O(1)

    6. 對象

    字符串對象

    字符串對象的編碼可以是int,raw,embstr
    如果是整數值,則編碼是int,如果是小于32字節的字符串,則編碼是embstr,如果是大于32字節的字符串,則編碼是raw。

    下面是他們的對象結構:

    embstr和raw的區別是 raw編碼調用了兩次內存分配函數來分配redisObject結構和sdshdr結構,而embstr只需要調用一次分配一段連續的空間。

    列表對象

    列表對象的編碼可以是ziplist或者linkedlist。

    當列表對象可以同時滿足一下兩個條件時,列表對象使用ziplist編碼:
    - 列表保存的所有字符串元素的長度都小于64字節;
    - 列表對象保存的元素數量小于512個。

    不能滿足這兩個條件的都是用LinkedList編碼。

    下面就是這個對象的兩種編碼的結構:

    每個StringObject實際上就是一個sdshdr。

    哈希對象

    哈希對象的編碼可以是ziplist或者是hashtable。

    當哈希對象同時滿足以下兩個條件時,哈希對象使用ziplist對象:
    - 哈希對象保存的所有鍵和值的字符串長度都小于64個字節;
    - 哈希對象保存的鍵值對數量小于512個

    不能滿足這兩個條件的哈希對象需要使用hashtable編碼。

    下面就是這個對象的兩種編碼的結構:

    集合對象

    集合對象的編碼可以是intset和hashtable

    當集合對象同時滿足以下條件時,對象使用intset編碼:
    - 集合對象保存的所有元素都是整數值
    - 集合對象保存的元素數量不超過512個

    不能滿足這兩個條件的集合對象需要使用hashtable編碼。

    下面就是這個對象的兩種編碼的結構:

    有序集合對象

    有序集合對象的編碼可以是ziplist和skiplist

    當集合對象同時滿足以下條件時,對象使用intset編碼:
    - 有序集合保存的元素數量小于128個
    - 有序集合保存的所有元素成員的長度都小于64字節

    不能滿足這兩個條件的集合對象需要使用skiplist編碼。

    下面就是這個對象的兩種編碼的結構:

    ziplist編碼的有序集合對象其實跟集合對象的差不多,區別是集合元素按分值從小到大進行排序。

    7.對象的引用計數

    因為C語言并不具備自動內存回收功能,所以redis對象系統構建了一個引用計數refcount技術來實現內存回收機制,通過這一機制,程序可以通過跟蹤對象的引用計數信息,在適當的時候自動釋放對象進行內存回收。

      對象的引用計數信息會隨著對象的使用狀態而不斷變化:

    • 在創建一個新對象時,引用計數的值會被初始化為1;
    • 當對象被一個新程序使用時,它的引用計數值會被增1
    • 當對象不再被一個程序使用時,它的引用計數會減1
    • 當對象的引用計數為0時,對象所占用的內存會被釋放。

    總結

    以上是生活随笔為你收集整理的redis学习之数据结构与对象(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。