分佈式專題-分佈式緩存技術之Redis01-Redis的使用

前言

關於Redis,一共分爲4節,本節爲Redis開篇:

  1. 初步瞭解分佈式緩存技術Redis的使用
    主要會以數據結構爲導向去了解Redis
  2. Redis內部的原理揭祕
    Redis的內部原理、Lua腳本的結合使用、Redis的回收策略、Redis持久化原理
  3. 瞭解分佈式Redis
    分佈式Redis、主從、哨兵、分片、企業級集羣方案
  4. Redis的應用實戰
    Redis的應用實戰,如何在實際應用中去使用redis

Redis的魅力

緩存大致可以分爲兩類,一種是應用內緩存,比如Map(簡單的數據結構),以及EH Cache( Java第三方庫),另一種就是緩存組件,比如Memached,Redis;Redis(remote dictionary server)是一個基於KEY-VALUE的高性能的存儲系統,通過提供多種鍵值數據類型來適應不同場景下的緩存與存儲需求

存儲結構

大家一定對字典類型的數據結構非常熟悉,比如map ,通過key value的方式存儲的結構。 redis的全稱是remote dictionary server(遠程字典服務器),它以字典結構存儲數據,並允許其他應用通過TCP協議讀寫字典中的內容。數據結構如下
在這裏插入圖片描述

Redis的安裝

redis約定次版本號(第一個小數點後的數字)爲偶數版本是穩定版,如2.8、3.0, 奇數版本爲非穩定版,生產環境需要使用穩定版;目前最新版本爲Redis5.0.8, 我們本次仍然以3.2作爲演示版本

安裝配置

  1. 下載redis的安裝包

    https://github.com/antirez/redis/releases
    國內網絡完全可以支持~

  2. tar -zxvf 解壓
    在這裏插入圖片描述

  3. cd 到解壓後的目錄
    在這裏插入圖片描述

  4. 執行make 完成編譯可能會遇到的錯誤

1.需要安裝tcl yum install tcl 、 yum install gcc
2.error: jemalloc/jemalloc.h: No such file or directory
說關於分配器allocator, 如果有MALLOC 這個 環境變量, 會有用這個環境變量的 去建立Redis。
而且libc 並不是默認的 分配器, 默認的是 jemalloc, 因爲 jemalloc 被證明 有更少的 fragmentation problems 比libc。
但是如果你又沒有jemalloc 而只有 libc 當然 make 出錯。 所以加這麼一個參數。解決辦法
make MALLOC=libc

或者參考此文章:make redis 報錯
Tips:
一般情況下,安裝
yum -y install gcc-c++
yum -y install tcl

兩條命令,就完全可以成功安裝redis

  1. make test 測試編譯狀態

在這裏插入圖片描述
6. make install {PREFIX=/path}
在這裏插入圖片描述

啓動停止Redis

安裝完redis後的下一步就是怎麼去啓動和訪問,我們首先先了解一下Redis包含哪些可執行文件

Redis-server Redis服務器
Redis-cli Redis命令行客戶端
Redis-benchmark Redis性能測試工具
Redis-check-aof Aof文件修復工具
Redis-check-dump Rdb文件檢查工具
Redis-sentinel Sentinel服務器(2.8以後)

我們常用的命令是redis-server和redis-cli

  1. 直接啓動
    redis-server …/redis.conf
    服務器啓動後默認使用的是6379的端口 ,通過–port可以自定義端口 ; 6379在手機鍵盤上MERZ對應,MERZ是一名意大利歌女的名字
    Redis-server --port 6380
    以守護進程的方式啓動,需要修改redis.conf配置文件中daemonize yes
  2. 停止redis
    redis-cli SHUTDOWN
    考慮到redis有可能正在將內存的數據同步到硬盤中,強行終止redis進程可能會導致數據丟失,正確停止redis的方 式應該是向Redis發送SHUTDOW命令
    當redis收到SHUTDOWN命令後,會先斷開所有客戶端連接,然後根據配置執行持久化,最終完成退出

數據類型

老生常談,redis常見的數據結構一共有五種!!

  • 字符串類型- String
  • 哈希表類型 -Hash
  • 列表類型 -List
  • 集合類型 -Set
  • 有序集合類型- Zset

目前最新版本爲Redis5.0.8,已經可以支持一些特殊類型

  • HyperLogLog
  • 地理位置
  • 位圖

更多redis的api,使用規則,這裏推薦一個網站:http://redisdoc.com/index.html

字符串類型

字符串類型是redis中最基本的數據類型,它能存儲任何形式的字符串,包括二進制數據。你可以用它存儲用戶的 郵箱、json化的對象甚至是圖片。一個字符類型鍵允許存儲的最大容量是512M

內部數據結構

在Redis內部,String類型通過 int、SDS(simple dynamic string)作爲結構存儲,int用來存放整型數據,sds存放字節/字符串和浮點型數據。在C的標準字符串結構下進行了封裝,用來提升基本操作的性能,同時也充分利用已有的C的標準庫,簡化實現邏輯。我們可以在redis的源碼中【/src/sds.h】中看到sds的結構如下;

#ifndef __SDS_H
#define __SDS_H

#define SDS_MAX_PREALLOC (1024*1024)

#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>


typedef char *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[];
};
//8表示字符串最大長度是2^8-1  (長度爲255)
struct __attribute__ ((__packed__)) sdshdr8 {
	//表示當前sds的長度(單位是字節)
    uint8_t len; 
	//表示已爲sds分配的內存大小(單位是字節)
    uint8_t alloc;
     //用一個字節表示當前sdshdr的類型,因爲有sdshdr有五種類型,所以至少需要3位來表示000:sdshdr5,001:sdshdr8,010:sdshdr16,011:sdshdr32,100:sdshdr64。高5位用不到所以都爲0。
    unsigned char flags;
    //sds實際存放的位置
    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[];
};

//...

sdshdr8的內存佈局

在這裏插入圖片描述

列表類型

列表類型(list)可以存儲一個有序的字符串列表,常用的操作是向列表兩端添加元素或者獲得列表的某一個片段。

列表類型內部使用雙向鏈表實現,所以向列表兩端添加元素的時間複雜度爲O(1), 獲取越接近兩端的元素速度就越快。這意味着即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是很快的
在這裏插入圖片描述

內部數據結構

redis3.2之前,List類型的value對象內部以linkedlist或者ziplist來實現, 當list的元素個數和單個元素的長度比較小的時候,Redis會採用ziplist(壓縮列表)來實現來減少內存佔用。否則就會採用linkedlist(雙向鏈表)結構。
redis3.2之後,採用的一種叫quicklist的數據結構來存儲list,列表的底層都由quicklist實現。
這兩種存儲方式都有優缺點,雙向鏈表在鏈表兩端進行push和pop操作,在插入節點上覆雜度比較低,但是內存開 銷比較大; ziplist存儲在一段連續的內存上,所以存儲效率很高,但是插入和刪除都需要頻繁申請和釋放內存;
quicklist仍然是一個雙向鏈表,只是列表的每個節點都是一個ziplist,其實就是linkedlist和ziplist的結合,quicklist 中每個節點ziplist都能夠存儲多個數據元素,在源碼中的文件爲【quicklist.c】,在源碼第一行中有解釋爲:A doubly linked list of ziplists意思爲一個由ziplist組成的雙向鏈表;

在這裏插入圖片描述
利用list的特性,我們可以應用到以下場景

棧:後進先出 lpush lpop
隊列:先進先出 lpush rpop
消息隊列:lpush brpop

哈希表類型

在這裏插入圖片描述

內部數據結構

map提供兩種結構來存儲,一種是hashtable、另一種是前面講的ziplist,數據量小的時候用ziplist. 在redis中,哈希表分爲三層,分別是,源碼地址【/src/dict.h】
dictEntry

管理一個key-value,同時保留同一個桶中相鄰元素的指針,用來維護哈希桶的內部鏈;

typedef struct dictEntry {
    void *key;
    //因爲value有多種類型,所以value用了union來存儲
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    //下一個節點的地址,用來處理碰撞,所有分配到同一索引的元素通過next指針鏈接起來形成鏈表key和v都可以保存多種類型的數據
    struct dictEntry *next;
} dictEntry;

dictht
實現一個hash表會使用一個buckets存放dictEntry的地址,一般情況下通過hash(key)%len得到的值就是buckets的 索引,這個值決定了我們要將此dictEntry節點放入buckets的哪個索引裏,這個buckets實際上就是我們說的hash表。dict.h的dictht結構中table存放的就是buckets的地址

typedef  struct  dictht  {
   dictEntry  **table;//buckets的地址
   unsigned  long  size;//buckets的大小,總保持爲  2^n
   unsigned  long  sizemask;//掩碼,用來計算hash值對應的buckets索引 
   unsigned  long  used;//當前dictht有多少個dictEntry節點
}  dictht;

dict

dictht實際上就是hash表的核心,但是隻有一個dictht還不夠,比如rehash、遍歷hash等操作,所以redis定義了 一個叫dict的結構以支持字典的各種操作,當dictht需要擴容/縮容時,用來管理dictht的遷移,以下是它的數據結 構

typedef  struct  dict  {
   dictType  *type;//dictType裏存放的是一堆工具函數的函數指針, 
   void  *privdata;//保存type中的某些函數需要作爲參數的數據
   dictht  ht[2];//兩個dictht,ht[0]平時用,ht[1]  rehash時用
   long  rehashidx;  //當前rehash到buckets的哪個索引,-1時表示非rehash狀態
   int  iterators;  //安全迭代器的計數。
}  dict;

比如我們要講一個數據存儲到hash表中,那麼會先通過murmur計算key對應的hashcode,然後根據hashcode取 模得到bucket的位置,再插入到鏈表中

集合類型

集合類型中,每個元素都是不同的,也就是不能有重複數據,同時集合類型中的數據是無序的。一個集合類型鍵可 以存儲至多232-1個 。集合類型和列表類型的最大的區別是有序性和唯一性
集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在。由於集合類型在redis內部是使用的值 爲空的散列表(hash table),所以這些操作的時間複雜度都是O(1).

在這裏插入圖片描述

內部數據結構

Set在的底層數據結構以intset或者hashtable來存儲。當set中只包含整數型的元素時,採用intset來存儲,否則, 採用hashtable存儲,但是對於set來說,該hashtable的value值用於爲NULL。通過key來存儲元素

有序集合類型

在這裏插入圖片描述
有序集合類型,顧名思義,和前面講的集合類型的區別就是多了有序的功能
在集合類型的基礎上,有序集合類型爲集合中的每個元素都關聯了一個分數,這使得我們不僅可以完成插入、刪除 和判斷元素是否存在等集合類型支持的操作,還能獲得分數最高(或最低)的前N個元素、獲得指定分數範圍內的元 素等與分數有關的操作。雖然集合中每個元素都是不同的,但是他們的分數卻可以相同

內部數據結構

zset類型的數據結構就比較複雜一點,內部是以ziplist或者skiplist+hashtable來實現,這裏面最核心的一個結構就 是skiplist,也就是跳躍表

在這裏插入圖片描述在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述在這裏插入圖片描述

後記

我在雲盤裏面上傳了一本電子書,《Redis實戰》,大家可以先下載去看看。

附贈《Redis實戰的PDF版電子書
提取碼:9lr8

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