Redis現在是比較流行的緩存數據庫,一般剛接觸的時候都會發現其可以存儲字符串(string)、哈希表(hash)、列表(list)、集合(set)、有序集合(sorted set)等。redis是一個key-value存儲,value可以包含上面列出的多種結構,但是key都是字符串。也就是說key是string類型,value爲上面類型的一種。
由於以上每種數據結構的存儲指令在redis中都不一樣,單個看來想要使用redis必須要先區分要存儲的對象的結構,然後選擇相應的指令。但是這樣使用起來確實是很不利的,如果一次要存入多種形式的值,我就要實現多種存儲方式。
爲了便於開發和使用redis引入了對象,即對象存儲。上面的每種數據結構都是一種對象,所以,在項目中只需要實現對象的存儲即可。
Redis中每個對象都有一個redisObject結構,該結構中和保存數據相關的三種屬性分別是存儲數據的類型type、值的編碼屬性encoding和指針ptr屬性:
typedef struct redisObject{
//類型
unsigned type:4;
//編碼
unsigned encoding:4;
//指向底層實現數據結構的指針
void *ptr
//虛擬內存和其他信息等.....
}robj;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
類型常量 | 對象的名稱 | type值 |
---|---|---|
REDIS_STRING | 字符串對象 | string |
REDIS_LIST | 列表對象 | list |
REDIS_HASH | 哈希對象 | hash |
REDIS_SET | 集合對象 | set |
REDIS_ZSET | 有序集合對象 | zset |
獲取存儲值的類型編碼指令:
TYPE key
如,我在redis中存入一個字符串的值:
[root@iZ8vb8r420ejxfron03cj7Z ~]# redis-cli
127.0.0.1:6379> set msg "rhett"
OK
127.0.0.1:6379> get msg
"rhett"
127.0.0.1:6379> type msg
string
- 1
- 2
- 3
- 4
- 5
- 6
- 7
編碼常量 | 對象的名稱 | type值 |
---|---|---|
REDIS_ENCODING_INT | 整數 | int |
REDIS_ENCODING_EMBSTR | embstr編碼的簡單動態字符串(SDS) | list |
REDIS_ENCODING_RAW | 簡單動態字符串 | raw |
REDIS_ENCODING_HT | 字典 | hashtable |
REDIS_ENCODING_LINKEDLIST | 雙端鏈表 | linkedlist |
REDIS_ENCODING_ZIPLIST | 壓縮列表 | ziplist |
REDIS_ENCODING_INTSET | 整數集合 | intset |
REDIS_ENCODING_SKIPLIST | 跳躍表和字典 | skiplist |
redis中的示例:
[root@iZ8vb8r420ejxfron03cj7Z ~]# redis-cli
127.0.0.1:6379> set msg "rhett"
OK
127.0.0.1:6379> object encoding msg
"embstr"
- 1
- 2
- 3
- 4
- 5
特點:快速、非關係、內存存儲、不使用表(必要時才用,少量數據,專屬命令)
比較:MySQL:關係數據庫,大量,增改刪查
Redis:服務器關閉時,緊湊的格式將存儲在內存中的數據寫入硬盤
持久化方法:1.時間點轉儲:指定時間段內有指定數量的寫操作執行
2.將所有修改了數據庫的命令全部寫入一個只追加文件裏面
結構類型(5種)
String:字符串、整數、浮點數
List:鏈表
Set:唯一無序集,不重複
Hash:鍵值對,無序散列表
Zset:有序集
String命令:對給定鍵中的值獲取GET、設置SET、刪除DEL
$redis-cli
redis 127.0.0.1:6379>set hello world
redis 127.0.0.1:6379>get hello
redis 127.0.0.1:6379>del hello
List:
RPUSH:推入右端,返回長度
LRANGE:獲取列表在給定範圍上的所有值
LINDEX:獲取列表在給定位置上的單個元素
LPOP:從左端彈出一個值並返回彈出的值
Set:
SADD:將給定元素加入集合
SMEMBERS:返回集合包含的所有元素
SISMEMBER:檢查給定元素是否存在於集合中
SREM:如果在,移除
Hash:
HSET:關聯指定鍵值對
HGET:獲取鍵的值
HGETALL:獲取所有鍵值對
HDEL:移除鍵
Zset:
ZADD:給定分值的成員添加到有序集合裏
ZRANGE:根據元素位置,從中取多個元素
ZRANGEBYSCORE:獲取給的分值範圍內所有元素
ZREM:刪除給定成員
下面以redis3.2的正式版源碼分析集中存儲機構:
typedef char *sds;
/*下面是sds數據結構的具體實現*/
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
static inline size_t sdslen(const sds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->len;
case SDS_TYPE_16:
return SDS_HDR(16,s)->len;
case SDS_TYPE_32:
return SDS_HDR(32,s)->len;
case SDS_TYPE_64:
return SDS_HDR(64,s)->len;
}
return 0;
}
typedef struct listNode { /*節點*/
struct listNode *prev;
struct listNode *next;
void *value; /*value用函數指針類型,決定了value可以是sds,list,set,dict等類型*/
} listNode;
typedef struct list { /*鏈表結構*/
listNode *head; /*頭節點*/
listNode *tail; /*尾節點*/
/*類似java類裏的的方法,方便調用*/
void *(*dup)(void *ptr); /*複製節點*/ //說實話,我不是很懂這個函數指針的意思,如有清楚地可以給我留言,謝謝。
void (*free)(void *ptr); /*釋放節點*/
int (*match)(void *ptr, void *key); /*匹配節點,返回key值得index,但是我不清楚他在那裏實現的*/
unsigned long len; /*記錄鏈表的長度*/
} list;
typedef struct listIter {
listNode *next;
int direction; //標註迭代器的運行方向
} listIter;
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask; /*hash表的掩碼,總是size-1,用於計算hash表的索引值*/
unsigned long used;
} dictht;
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;
typedef struct intset { /*整數集合的數據結構*/
uint32_t encoding; //編碼方式
uint32_t length;
int8_t contents[];
} intset;
typedef struct zskiplistNode {
robj *obj; //存儲對象的指針
double score; //分數
struct zskiplistNode *backward; //後退指針,每次只能退一步
struct zskiplistLevel {
struct zskiplistNode *forward; //前進指針,每次可以跳躍好幾步
unsigned int span; //這個就是決定前進指針能跳躍幾步的跨度標誌
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
typedef struct zlentry {
//prevrawlen爲上一個數據結點的長度,prevrawlensize爲記錄該長度數值所需要的字節數
unsigned int prevrawlensize, prevrawlen;
//len爲當前數據結點的長度,lensize表示表示當前長度表示所需的字節數
unsigned int lensize, len;
//數據結點的頭部信息長度的字節數
unsigned int headersize;
//編碼的方式
unsigned char encoding;
//數據結點的數據(已包含頭部等信息),以字符串形式保存
unsigned char *p;
} zlentry;
參考書籍:《Redis設計與實現》
特此聲明:本文非本人所原創,是轉載他人文章並總結整合方便自己學習的筆記!!!