天天看點

Redis的操作、底層原理及主從,叢集和哨兵配置

作者:烨神es
Redis的操作、底層原理及主從,叢集和哨兵配置

Redis是一個記憶體資料結構存儲系統,它支援各種資料結構,如字元串、哈希表、清單、集合和有序集合等。Redis通常被用作緩存、消息隊列或實時資料分析的資料庫。

Redis的高性能主要得益于其使用記憶體進行資料存儲和通路,而記憶體的讀寫速度比磁盤要快很多。此外,Redis還采用了單線程模型和異步IO等技術,使得其能夠在短時間内處理大量的并發請求。

除了快速讀寫和高并發處理能力,Redis還提供了一些其他有用的功能,如釋出/訂閱機制、事務處理、Lua腳本等,并且它的用戶端支援很多語言,如Java、Python、C#、Node.js等,這使得它成為一個非常流行和适用于各種場景的資料存儲解決方案。

下面分别對Redis支援的資料結構進行詳細介紹和舉例:

  1. 字元串(String)

字元串可以存儲任何類型的資料,如數字、JSON、XML等。操作包括設定值、擷取值、計數器操作、批量操作等。

舉例:

# 設定字元串類型鍵值對
set mykey "hello world"

# 擷取字元串類型鍵值對
get mykey

# 将一個數值加1
incr mycounter
           
  1. 哈希表(Hash)

哈希表可以用來存儲對象,對象由字段和對應的值組成。操作包括設定、擷取、删除單個或多個字段、擷取所有字段等。

舉例:

# 設定哈希表類型鍵值對
hmset user:1 name "John" age 30 email "[email protected]"

# 擷取哈希表特定字段的值
hget user:1 name

# 擷取哈希表所有字段和值
hgetall user:1
           
  1. 清單(List)

清單是一個有序集合,每個元素都有一個索引。操作包括插入、删除、擷取元素等。

舉例:

# 向清單中添加元素
lpush mylist "world"
lpush mylist "hello"

# 擷取清單中指定範圍的元素
lrange mylist 0 -1

# 從清單中删除指定元素
lrem mylist 1 "hello"
           
  1. 集合(Set)

集合是一組無序的元素,每個元素都是唯一的。操作包括添加、删除、擷取元素等。

舉例:

# 向集合中添加元素
sadd myset "apple"
sadd myset "banana"

# 擷取集合中所有元素
smembers myset

# 從集合中删除元素
srem myset "banana"
           
  1. 有序集合(Sorted Set)

有序集合與集合類似,但是每個元素都關聯了一個實數分值,并按照分值排序。操作包括添加、删除、擷取元素等。

舉例:

# 向有序集合中添加元素
zadd myzset 1 "apple"
zadd myzset 2 "banana"

# 擷取有序集合中指定範圍的元素
zrange myzset 0 -1

# 删除有序集合中指定的元素
zrem myzset "banana"           

底層記憶體管理和資料結構設計

Redis是使用C語言編寫的,因為C語言具有高效、跨平台等優點,非常适合用于底層系統程式設計。 實際上,Redis的底層資料結構和記憶體管理都是使用C語言實作的,這也是Redis能夠高效地處理大量資料的一個重要原因。

在 Redis 的 GitHub 倉庫中,可以看到 Redis 的源代碼是使用 C/C++ 編寫的,其中大部分是使用 C 語言編寫的。同時,Redis 還使用到了部分 C++ 的特性,例如異常處理等。

Redis能基于記憶體進行資料存儲和通路,主要得益于其底層記憶體管理和資料結構設計。這些機制讓Redis能夠高效地處理大量資料,并在低延遲下提供快速響應。
  1. 底層記憶體管理

Redis會把所有資料存儲在記憶體中,而不是在硬碟上。這樣可以大大提高資料通路速度,特别是在需要頻繁讀寫的場景中。同時,Redis也提供了多種機制來降低記憶體使用量,包括壓縮資料、删除過期資料等。

  1. 資料結構設計

Redis的資料結構設計也是其能高效地進行記憶體通路的核心。例如,在字元串類型中,Redis會将字元串轉換成位元組數組,并使用C語言中的結構體來表示,這樣就可以直接通過指針進行内部通路,進而避免了額外的記憶體拷貝和類型轉換。

下面舉例說明:

在Redis中,字元串類型的定義如下:

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* 24 LRU時間戳 */
    int refcount;
    void *ptr;
} robj;
           

其中,type表示類型,encoding表示編碼方式,lru表示時間戳(用于LRU算法),refcount表示引用計數,ptr指向實際的資料。

當我們向Redis中添加一個字元串時,它會在記憶體中建立一個redisObject結構體,并将字元串轉換為位元組數組。資料的讀寫可以直接通過指針進行,進而避免了額外的記憶體拷貝和類型轉換。

舉例:

/* 建立一個新的字元串對象 */
robj *createStringObject(char *ptr, size_t len) {
    return createObject(REDIS_STRING,sdsnewlen(ptr,len));
}

/* 将key設定為value */
void setCommand(redisClient *c) {
    robj *o;

    /* 建立或更新key對應的值 */
    o = c->argv[2];
    incrRefCount(o);
    dictReplace(c->db->dict,c->argv[1],o);
}
           

在上面的例子中,當我們執行SET key value時,createStringObject會建立一個redisObject結構體,并将字元串轉換為位元組數組。然後,setCommand會将這個redisObject設定為key對應的值,進而完成記憶體中資料的存儲和通路。

企業中經常使用 Redis 的一些主要功能(Python)

Redis的操作、底層原理及主從,叢集和哨兵配置
  1. 緩存機制

緩存是一個常見的應用場景,它可以提高應用程式的性能和響應速度。Redis 作為一個高效的記憶體資料庫,被廣泛應用于緩存場景中。

在 Python 中使用 Redis 作為緩存通常可以通過 redis-py 庫來實作。具體過程如下:

  • 建立與 Redis 的連接配接
import redis
redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0)           
  • 從 Redis 中讀取資料
data = redis_conn.get('key')           
  • 如果 Redis 中沒有命中,則從資料庫中擷取資料,并将資料存入 Redis 緩存中
if data is None:
    # 從資料庫中擷取資料
    ...
    # 将資料存入 Redis 緩存中
    redis_conn.setex('key', 60, str(data))
           

在上述代碼中,redis_conn.get('key') 操作會從 Redis 資料庫中擷取鍵為 key 的值,如果沒有找到則傳回 None。如果資料不存在,就需要從資料庫中擷取資料并将其存入 Redis 中。

這裡使用了 Redis 的 setex 方法,該方法可以設定鍵值對的過期時間,使得 Redis 可以自動删除過期的資料。

  1. 消息隊列

消息隊列是一種常見的分布式系統架構中的元件之一,可以用于異步任務處理、事件驅動等場景。Redis 作為輕量級的消息隊列系統,具有高效、可靠、簡單等特點,被廣泛應用于消息隊列場景中。

在 Python 中使用 Redis 作為消息隊列通常可以通過 redis-py 庫的釋出/訂閱機制來實作。具體過程如下:

  • 建立與 Redis 的連接配接
import redis
redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0)           
  • 訂閱消息
def subscribe(channel):
    pubsub = redis_conn.pubsub()
    pubsub.subscribe(channel)
    for item in pubsub.listen():
        # 處理接收到的消息
        ...           
  • 發送消息
def publish(channel, message):
    redis_conn.publish(channel, message)           

在上述代碼中,pubsub.subscribe(channel) 操作可以訂閱指定的頻道,而 pubsub.listen() 則會一直阻塞等待消息。當有消息釋出到頻道時,for item in pubsub.listen(): 就會擷取到消息并進行處理。

  1. 會話存儲

會話存儲是一個常見的需求,它可以用于存儲使用者的登入狀态、浏覽器曆史記錄等資料。Redis 作為一個記憶體資料庫,可以快速地存儲和查詢會話資料。

在 Python 中使用 Redis 存儲會話資料通常可以通過 redis-py 庫來實作。具體過程如下:

  • 建立與 Redis 的連接配接
import redis
redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0)           
  • 将會話資料存入 Redis 中
@app.before_request
def before_request():
    session_id = session.sid
    data = {'count': session.get('count')}
    redis_conn.setex('session:' + session_id, 3600, str(data))
           

在上述代碼中,session_id 是會話的唯一辨別符,可以通過 Flask 提供的 session.sid 擷取。 data 是會話存儲的資料,可以是任意 Python 對象。最後,将資料以字元串形式存入 Redis 中,使用 setex 方法設定過期時間為 3600 秒。

通過使用 Redis,我們可以快速地建構高性能、可靠、可擴充的分布式應用程式。

下面将詳細介紹 Redis 的主從複制、叢集和哨兵模式

Redis的操作、底層原理及主從,叢集和哨兵配置
  1. 主從複制

主從複制是 Redis 的常見的高可用方案之一,它可以保證在 Redis 主節點故障的情況下,Redis 從節點可以接管服務,進而保持服務的可用性。下面是基于 Linux 的主從複制示例配置流程:

a.準備兩台 Linux 伺服器,分别作為 Redis 主節點和從節點,并安裝 Redis。

b.在 Redis 主節點上編輯 redis.conf 配置檔案,開啟主節點功能,設定密碼和端口等參數(省略注釋):

bind 0.0.0.0
protected-mode no
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis/redis.log"
dir /var/lib/redis
requirepass yourpassword      # 設定 Redis 密碼
masterauth yourpassword      # 設定主從複制認證密碼
           

c.在 Redis 從節點上編輯 redis.conf 配置檔案,開啟從節點功能,設定密碼和端口等參數(省略注釋):

bind 0.0.0.0
protected-mode no
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "/var/log/redis/redis.log"
dir /var/lib/redis
requirepass yourpassword      # 設定 Redis 密碼
slaveof master_ip master_port      # 設定 Redis 主節點的 IP 位址和端口
masterauth yourpassword      # 設定主從複制認證密碼
           

d.分别啟動 Redis 主節點和從節點的服務。

# 啟動 Redis 主節點服務
$ redis-server /path/to/redis.conf

# 啟動 Redis 從節點服務
$ redis-server /path/to/redis.conf
           
  1. 叢集模式

Redis 的叢集模式可以将多個 Redis 執行個體組成一個叢集,實作資料分片和高可用性等功能。下面是基于 Linux 的 Redis 叢集配置流程:

a.準備三台或以上 Linux 伺服器,并安裝 Redis。

b.每台 Redis 伺服器上都需要編輯 redis.conf 配置檔案,開啟叢集模式,設定密碼和端口等參數(省略注釋):

bind 0.0.0.0
protected-mode no
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis/redis.log"
dir /var/lib/redis
requirepass yourpassword      # 設定 Redis 密碼
cluster-enabled yes      # 開啟叢集模式
cluster-config-file nodes-6379.conf      # 叢集狀态配置檔案
cluster-node-timeout 5000      # 設定叢集節點逾時時間
           

c.在任意一台 Redis 伺服器上執行 redis-trib.rb 腳本建立 Redis 叢集,同時需要指定一個 Node IP 和端口号。

$ redis-trib.rb create --replicas 1 node1:6379 node2:6379 node3:6379 \
node4:6379 node5:6379 node6:6379 --cluster-replicas 1
           

上述指令将在 node1 到 node6 這六台機器上啟動 Redis 執行個體,其中包括三個主節點和三個從節點,并設定複制因子為 1(即每個主節點對應一個從節點)。此外,腳本還會生成一個狀态配置檔案 nodes-6379.conf,用于記錄叢集的狀态。

  1. 哨兵模式

Redis 的哨兵模式可以實作自動切換主從角色、服務發現和故障轉移等功能。下面是基于 Linux 的 Redis 哨兵模式配置流程:

a.準備三台或以上 Linux 伺服器,并安裝 Redis。

b.每台 Redis 伺服器上都需要編輯 redis.conf 配置檔案,開啟哨兵模式,設定密碼和端口等參數(省略注釋):

bind 0.0.0.0
protected-mode no
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis/redis.log"
dir /var/lib/redis
requirepass yourpassword      # 設定 Redis 密碼
sentinel monitor mymaster master_ip master_port 1      # 設定哨兵監控主節點的 IP 和端口
sentinel down-after-milliseconds mymaster 60000      # 設定哨兵認定主節點當機的時間
sentinel auth-pass mymaster yourpassword      # 設定哨兵認證密碼
           

c.在三台 Redis 伺服器上分别啟動一個哨兵程序,通過 redis-sentinel 指令啟動:

$ redis-sentinel /path/to/sentinel.conf --sentinel
           

其中 /path/to/sentinel.conf 是指哨兵配置檔案路徑。在叢集中,每個哨兵需要監控所有的 Redis 節點。

在實際應用中,可以根據自己的需求選擇不同的高可用方案,以保證服務的可靠性和穩定性。

繼續閱讀