redis学习之数据结构与对象(一)
1.SDS
簡介:Redis沒有采用C語言的以空字符串結尾的字符數組,而是構建一種簡單動態字符串(Simple dynamic string,SDS),并將它作為string的表示。
struct sdshdr {// buf 中已占用空間的長度int len;// buf 中剩余可用空間的長度int free;// 數據空間char buf[]; // 依然以’\0’結尾 };SDS與C語言的字符串的區別
二進制安全,所以能保存圖片,視頻,音頻文件
常用操作:
- 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学习之数据结构与对象(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 负载均衡和CDN技术
- 下一篇: scala学习 之 环境搭建(一)