redis -- 基本數據類型對象

redis:remote dictionary server

字面意思:遠程“字典“服務器(哈哈哈哈)

由Salvatore Sanfilippo寫的key-value存儲系統,屬於nosql的一種,Redis是一個開源的使用ANSI c語言編寫,遵守BSD協議,支持網絡, 基於內存但是可以實現持久的一款數據庫。

redis相對於數據庫家族的優勢:

  • redis有着更爲複雜的數據類型,支持set、list、hash、string等,它的一個鍵最大可爲512M,value最大可達1G。
  • redis有着更高的運行速度和持久化的選擇, redis是一款運行在內存的單進程單線程的異步數據庫,單進程單線程並代表就low ,能讀的速度是110000次/s,寫的速度是81000次/s 。對於持持久化來說,它提供快照持久化和AOF持久化的方式選擇設置,以及master-slave方式的數據備份,極大的方便了程序媛用戶。
  • redis極高的支持原子化操作,redis的操作基本上都是原子化的,此外,多個操作合併後的原子性執行它也是支持的。

redis的安裝:

網上教程多,它支持原碼安裝,看一下它的src目錄下的關鍵文件

redis中的對象類型:

命名規則:除了\n 和空格不能作爲名字的組成外,其他都可以出現,且長度不作要求。

redis基本數據對象類型模型圖:

string對象類型:

string類型的基本操作:

string對象類型是redis的基本數據類型, 但是它跟c語言裏面的字符串常量或則字符數組不是同一個概念;在redis裏面實現string對象有三種基本的方式:

  1. 如果string對象保存的是整數值, 並且這個值可以用long來表示, 那麼redisobject裏面的ptr指針從void*轉變爲long型, 字符串編碼設置爲int。這個編碼方式並不是一成不變的,
    當操作改變了存儲對象,編碼也許會變爲raw。
  2. 如果string對象保存的是字符串值, 且這個字符串長度大於32字節, string對象將採用一個SDS來保存這個字符串,編碼方式爲raw, 調用兩次內存分配,
    一次是redisobject對象結構一次是sdshdr對象結構。
  3. 如果string對象保存的是字符串值, 且這個字符串長度小於32字節, string對象將採用一個embstr編碼方式來保存,分配的內容跟raw編碼一樣,只不過它調用一次內存分配一塊連續的空間,
    這塊空間包含兩個結構體。嚴格意義上, embstr編碼方式的字符串只是可讀的,若操作改變, 則可能變爲raw編碼方式。

int

embstr

sdshdr – SDS

看一下SDS(簡單動態字符串)的結構組成:

SDS相對應c語言字符串的優勢:
1. 將獲取字符串長度的工作從0(n)降到0(1)的複雜度。
2. SDS API能夠降SDS的空間自動擴展,解決溢出的問題。如c的strcat不檢查溢出,以及SDS的sdscat的檢查並擴展容量。這也是一種節約空間的策略;
3. 空間優化策略: 起作用的前提是SDS對象的數組長度內容頻繁的改變 ;利用的是SDS對象的free屬性
·······a、空間預分配:優化在字符數組增長;擴展數組時,額外分配空間(小於1M翻倍分配,大於1M多加1M);利用SDS的free屬性來記錄這個位置。
·······b、惰性空間釋放:優化字符數組縮短;縮短數組時,並不通過內存重新分配回縮,而是調整free的值,記錄,以待將來對這段空間的使用。
4. 字節數組,類型多;在c的數組裏面他以\0 的方式標誌結束一個字符串,內容中間不可包含空格;但是SDS中它是可以保存的,它採用二進制處理buf裏面的內容,不會對其有任何的限制,存取不會造成差別,所以各種的文件類型都可以被它完好的引用。

list對象類型:

lis對象t類型的基本操作:

編碼方式:
1. ziplist 列表保存的所有字符串元素長度都小於64個字節;保存的元素個數小於512個。
2. 其餘情況使用linklist。

linklist是一個對雙向不循環鏈表進行過封裝的對象,兩邊雙向一共四個方向操作,可以練兩結合,因此list既可以作爲棧出現也可以作爲隊列出現。

鏈表結點結構體:
typedef struct listNode{
    //前驅節點
    struct listNode* prev;
    //後驅節點
    struct listNode* next;
    //節點值
    void *value;
}listNode;
鏈表結構:
typedef struct list{
    //表頭結點
    listNode* head;
    //表尾結點
    listNode* tail;
    //鏈表長度
    unsigned long len;
    //結點值複製函數
    void *(*dup)(void * ptr);
    //結點值釋放函數
    void (*free)(void * ptr);
    //結點值對比函數
    void (*match)(void *ptr, void* key);
}list;

實現模型:


ziplist

ziplist 壓縮列表, 爲了節約內存而出現, 特點:是由一系列特殊比阿媽的連續內存塊組成的序列, 可以包含任意多個結點。

zlbytes 記錄整個壓縮列表佔用的內存字節數
zltail 記錄壓縮列表 表尾結點 距離列表起始位置的字節數
zllen 記錄壓縮列表包含的字節數
entry 列表結點
zlend 標記壓縮列表的結束

壓縮列表的結點:

結點包含三個屬性:
其中previous_entry_length屬性記錄的是上一個結點的長度,如果上一個結點長度小於254個字節, 該屬性用一個字節來保存它的長度, 否則用5個字節來保存。(利用這個屬性, 可以實現向前的結點獲取)
enccoding 屬性標記結點保存的數據類型以及長度
content 保存結點的值

連鎖更新:
當有結點的添加或着擴大修改或着刪除中間小結點的時候, 可能後續結點的prvious_entry_length屬性的大小由1變爲5, 因此,該結點的大小如果大於254個字節。後繼結點的prvious_entry_length屬性應該變爲5個字節,因此又引發一次空間重新分配。以此類推。




set類型:

可以實現數學裏面的交,並,差集的運算,集合中的元素不重複。
set類型的基本操作:

編碼方式:
1. intset 集合對象保存的所有元素都是整數值,元素數量不超過512個。
2.其他情況使用hashtable

hashtable

intset

intset 整數集合 :各個數據項在數組中按值的大小從小到大有序的排列,並且數組中不包含任何重複項。

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

整數集合的升級: 新添加的元素類型比整數集合現有的元素的類型要長,就要進行升級的活動。
升級三部曲:
1. 擴展底層數組的大小
2. 將底層數組現有的元素都換成與新元素相同的類型,並將轉換後的元素放置到正確的位置上,這個過程保持元素的有序性。
3. 將新元素添加到底層數組裏面。
注意:不存在回縮的降級。




sorted set有序對象類型:

每個元素都是一個值-權的組合,通過權值可以有序的獲取集合中的元素。
sorted set的基本操作:

有序對象zset的編碼方式:
1. ziplist 保存元素小於128個, 所有雅俗怒的長度都小於64個字節。
2. 其餘情況採用skiplist編碼。

ziplist

ziplist 方式:每個集合元素使用兩個緊挨着的儀器的壓縮列表結點來保存,第一個結點保存元素的成員,第二個元素爆粗元素的分值。元素在壓縮列表內按分值的從小到達進行排序。

skiplist編碼方式採用zset結構體的方式實現,zset結構體包含一個跳越表skiplist和一個字典。

typedef struct set{
    zskiplist *zsl;
    dict *dict;
}set;

skiplist – zset

使用skiplist編碼的zset對象模型:



跳越表:

跳越表:
typedef struct zskiplist{
    struct skiplistNode *header, *tail;
    //表頭結點和表尾結點
    unsigned long length;  //表中結點數量
    int level;    // 表中層數最大的結點的層數
}zskiplist;

跳越表結點:
typedef struct zskiplistNode{
    struct zskiplistLevel{  //層
        struct zskiplistNode *forward; //前進指針
        unsigned int span;  //跨度
    }level[];
    struct zskiplistNode *backward; //後退指針
    double score;  //分值
    rob *obj;  //對象
}zskiplistNode;

跳越表的結構模型:




hash對象類型:

鍵-值對集合,每個hash可以存儲40多億個鍵-值對。

hansh對象的編碼方式:

  1. ziplist 哈希對象保存的所有鍵值對的鍵和值的字符串長度都小於64字節,哈希對象保存的鍵值對數量小於512個,滿足以上條件使用ziplist。
  2. 不滿足以上條件使用hashtable編碼。

ziplist

採用ziplist的hash對象,首先將保存了鍵的壓縮列表結點壓入壓縮列表表尾, 然後再將保存了值的壓縮列表結點壓入隊尾, 這樣鍵值對的位置是相鄰的。

hashtable

hashtable 編碼採用字典作爲底層的實現。
字典:又稱符號表、關聯數組或映射。

字典:
typedef struct dict{
    dictType *type; //類型特定函數
    void  *privdata;  //私有數據
    ditch ht[2];      //哈希表, 包含兩個數組項, 通常只用第一個, 第二個在進行rehash的時候使用。
    int trehashind;  //rehash索引 
}dict;

哈希表:
typedef struct dictch{
    dictEntry **table; //哈希表數組
    unsigned long size; //哈希表大小
    unsigned long sizemask; //哈希表大小掩碼, 用於計算索引值, 數值爲size-1
    unsigned long used; //哈希表已有的結點數量
}dictcht;

哈希表結點:
typedef struct dictEntry{
    void *key;
    union{
        void *val;
        uint64_tu64;
        int64_ts64;
    }
    struct dictEntry *next; //後面連接的是哈希表結點, 將多個哈希值相同的鍵值對連接在一起, 以此來解決鍵衝突的問題。
}dictEntry;

對象模型:

解決鍵衝突:當兩個或以上的鍵被分配到了哈希數組的同一個索引上的時候, 就稱發生了衝突。
鏈地址法:將這些發生衝突的結點, 使用next指針構成一個單向的鏈表。以此來解決鍵衝突的問題。(通常將新的結點放在第一個位置, 因此添加的複雜度爲 0(1))

rehash重整散列:這個動作不是一下完成的,具有漸進式的特點。
重新計算鍵的哈希值和索引值,然後將鍵值對放置到ht[1]哈希表的制定位置上。當所有的動作完成, ht[0]變成一個空表,將ht[1]設置爲ht[0],並再創建一個空的哈希表, 爲下一次的rehash做準備。

rehash的步驟;
1. 爲ht[1]分配空間, 讓字典同時持有ht[1] ht[0]兩個哈希表。
2. 在字典維護一個索引計數器變量rehashidx,並將它設爲0, 表示rehash正式開始。
3. 在reshah期間, 每次對字典的操作, 程序會順帶將ht[0]哈希表在rehashidx索引上的所有鍵值對rehash到ht[1],當工作完成rehashidx屬性的值增加一。
4. 當rehash操作完全的完成, 程序將rehashidx屬性的值設置爲-1;

負載因子=哈希表已保存結點數量/哈希表大小

哈希表自動進行擴展的條件:
1. 服務器目前沒有進行BGSAVE 或BGREWRITEAOF,並且哈希表的負載因子大於1。
2. 服務器目前進行BGSAVE 或BGREWRITEAOF,並且哈希表的負載因子大於5。




持久化:

爲了數據安全, redis會把本身的數據以文件的形式保存到硬盤中,在服務器重啓之後自動吧硬盤數據恢復到內存裏面。將數據保存到硬盤的過程就叫做’持久化’。

快照持久化RDB:

一次性把redis中的全部數據文件(鍵值對)保存一份存儲在硬盤中。(適合數據量不大的情況,如10G以下)
設置快照頻率的配置:
這裏寫圖片描述

手動發起快照持久化的方式有兩種savebgsave
save 是直接的阻塞服務器,直到工作完成, bgsave 會啓動子進程。

RDB的載入是在服務器啓動的時候如果檢測到它的存在就會自動載入。(如果開啓了AOF持久化,則載入AOF文件不載入RDB文件)



AOF(APPEND ONLY FILE)持久化:

把用戶執行的指令(添加、修改、刪除)都保存到備份到文件中,還原數據的時候及時執行具體寫指令。
注意:當在配置文件中開啓AOF持久化的時候會清空redis數據庫的全部內容,所以慎重!!

相關配置:

優化操作:

持久化的相關操作:

主從服務器:

目的:降低一個redis服務器的負載,將多個redis服務器設置爲一個主服務器的slave服務器,master主服務器根slave服務器之間會進行自動的數據同步。

將一個服務器設置爲其他服務器的slave服務器的方法:



all

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章