Redis 的第 n 次涉及,我該整理整理了

程式碼的壞味道發表於2019-07-18

未完待續!

redis你是怎麼發音的 ?瑞得兒思瑞迪思
曾經被一個人糾正了N多次,還是習慣瑞得兒思。可能兒化音比較洋氣

定義:單執行緒架構和非阻塞I/O多路複用模型,實現高效能的記憶體資料庫服務

官方解釋:Redis是基於記憶體的儲存。本身效能足夠高了,沒必要多執行緒。
非阻塞:不必等完全處理完一個任務就可以去處理另一個任務
多路複用:多路-指的是多個socket連線,複用-指的是複用一個執行緒。

這裡是不是有點疑惑了,單執行緒又是非阻塞多路複用。redis,到底是怎麼一回事呢?

Redis 是跑在單執行緒中的,所有的操作都是按照順序線性執行的,但是由於讀寫操作等待使用者輸入或輸出都是阻塞的,所以 I/O 操作在一般情況下往往不能直接返回,這會導致某一檔案的 I/O 阻塞導致整個程式無法對其它客戶提供服務,而 I/O 多路複用就是為了解決這個問題而出現的.

Redis 對 IO 多路複用的封裝
以 epoll 為例,瞭解 Redis 內部是如何封裝 IO 多路複用的
為了將所有 IO 複用統一,Redis 為所有 IO 複用統一了型別名 aeApiState,對於 epoll 而言,型別成員就是呼叫 epoll_wait所需要的引數
接下來就是一些對epoll介面的封裝了,包括建立 epoll(epoll_create),註冊事件(epoll_ctl),刪除事件(epoll_ctl),阻塞監聽(epoll_wait)等
建立 epoll 就是簡單的為 aeApiState 申請記憶體空間,然後將返回的指標儲存在事件驅動迴圈中\
註冊事件和刪除事件就是對 epoll_ctl 的封裝,根據操作不同選擇不同的引數\
阻塞監聽是對 epoll_wait 的封裝,在返回後將啟用的事件儲存在事件驅動中

事件驅動迴圈流程
IO 複用的封裝實現完成,那麼 Redis 是何時呼叫 IO 複用函式的呢,這就需要從 server.c/main 函式入手,可以猜測到當 main 函式初始化工作完成後,就需要進行事件驅動迴圈,而在迴圈中,會呼叫 IO 複用函式進行監聽\
在初始化完成後,main 函式呼叫了 aeMain 函式,傳入的引數就是伺服器的事件驅動

Redis 對於時間事件是採用連結串列的形式記錄的,這導致每次尋找最早超時的那個事件都需要遍歷整個連結串列,容易造成效能瓶頸。而 libevent 是採用最小堆記錄時間事件,尋找最早超時事件只需要 O(1) 的複雜度

總結一下:單執行緒指的是執行任務單執行緒先把他看做`任務盒子`,非阻塞的多路複用指的是多個使用者連線,redis自己封裝了一個`使用者盒子`把使用者連線資料放裡面這個過程是非阻塞多路複用,然後把使用者盒子到任務盒子去執行這是單執行緒的(怪怪的)

基本資料型別:
扯了那麼多,會用才是王道,畢竟redis不是我能開發的。
1.redis的幾種資料型別

  • string
  • hash
  • list
  • set
  • zset

string

int:8個位元組的長整型
embstr:小於等於39個位元組的字串
raw:大於39個位元組的字串

Redis會根據當前值的型別和長度決定使用哪種內部編碼實現

string常用命令描述

序號 命令 描述
1 set key value 設定指定key的值 例:set cfun that is a goog boy
2 get key 設定指定key的值 例:get cfun 輸出:that is a goog boy
3 getrange key start end 返回 key 中字串值的子字元。例: getrange cfun 0 3 輸出:that
4 getset key value 將給定 key 的值設為 value ,並返回 key 的舊值(old value),key無舊值則返回nil ,key存在但不是字串是返回錯誤
5 getbit key offset 對 key 所儲存的字串值,獲取指定偏移量上的位(bit)
6 mget key1 key2 獲取所有(一個或多個)給定 key 的值。
7 setex key seconds value 將值 value 關聯到 key ,並將 key 的過期時間設為 seconds (以秒為單位)。
8 setnx key value 只有在 key 不存在時設定 key 的值

hash
是一個string型別的field和value的對映表,hash特別適合用於儲存物件
Redis 中每個 hash 可以儲存 2^32^ - 1 鍵值對(40多億)。

1.ziplist(壓縮列表):當雜湊型別元素個數小於hash-max-ziplist-entries配置(預設512個)、同時所有值都小於hash-max-ziplist-value配置(預設64位元組)時,Redis會使用ziplist作為雜湊的內部實現
2.hashtable(雜湊表):當雜湊型別無法滿足ziplist的條件時,Redis會使用hashtable作為雜湊的內部實現,因為此時ziplist的讀寫效率會下降,而hashtable的讀寫時間複雜度為O(1)

Hash常用命令描述

序號 命令 描述
1     hget key field   獲取儲存在雜湊表中指定欄位的值。
2     hexists key field   檢視雜湊表 key 中,指定的欄位是否存在。
3     hgetall key   獲取在雜湊表中指定 key 的所有欄位和值
4     hmset key field1 value1 [field2 value2 ]   同時將多個 field-value (域-值)對設定到雜湊表 key 中。
5    hset key field value   將雜湊表 key 中的欄位 field 的值設為 value 。

list
列表型別,儲存類似一維陣列資料

    1. ziplist(壓縮列表) 元素個數小於配置(預設512),每個元素大小小於64位元組(預設)
    2. linkedlist(連結串列) 當列表型別無法滿足ziplist的條件時,Redis會使用linkedlist作為列表的內部實現

List常用命令描述

序號 命令 描述
1     lpush key value1  將一個值插入到已存在的列表頭部
2     lpop key   移出並獲取列表的第一個元素。
3     lindex key index   透過索引獲取列表中的元素
4     hmset key field1 value1 [field2 value2 ]   同時將多個 field-value (域-值)對設定到雜湊表 key 中。
5    lset key index value   透過索引設定列表元素的值
6    lrem key count value   移除列表元素
7   rpop key   移除列表的最後一個元素,返回值為移除的元素。
8   rpush key value1 [value2]   在列表中新增一個或多個值。

set
集合型別:Set 是 String 型別的無序集合。集合成員是唯一的,這就意味著集合中不能出現重複的資料
Redis 中集合是透過雜湊表實現的,所以新增,刪除,查詢的複雜度都是 O(1)。

1.intset(整數集合):當集合中的元素都是整數且元素個數小於set-maxintset-entries配置(預設512個)時,Redis會選用intset來作為集合的內部實現,從而減少記憶體的使用。
2.hashtable(雜湊表):當集合型別無法滿足intset的條件時,Redis會使用hashtable作為集合的內部實現。

set常用命令描述

序號 命令 描述
1     sadd key member1 [member2] 向集合新增一個或多個成員
2     scard key   獲取集合的成員數。
3     sdiff key1 [key2]   返回給定所有集合的差集
4     sinter key1 [key2] 返回給定所有集合的交集
5    sismember key member   判斷 member 元素是否是集合 key 的成員
6    smembers key   返回集合中的所有成員
7   smove source destination member   將 member 元素從 source 集合移動到 destination 集合
8   spop key 移除並返回集合中的一個隨機元素
srem key member1 [member2]   移除集合中一個或多個成員

zset
Redis 有序集合和集合一樣也是string型別元素的集合,且不允許重複的成員。
不同的是每個元素都會關聯一個double型別的分數。redis正是透過分數來為集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重複。
集合是透過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。
集合中最大的成員數為 232 - 1 (4294967295, 每個集合可儲存40多億個成員)。

1.ziplist(壓縮列表):當有序集合的元素個數小於zset-max-ziplistentries配置(預設128個),同時每個元素的值都小於zset-max-ziplist-value配置(預設64位元組)時,Redis會用ziplist來作為有序集合的內部實現。  
2.skiplist(跳躍表):當ziplist條件不滿足時,有序集合會使用skiplist作為內部實現,因為此時ziplist的讀寫效率會下降。

set常用命令描述

序號 命令 描述
1     zadd key score1 member1 [score2 member2] 向有序集合新增一個或多個成員,或者更新已存在成員的分數
2     zcard key 獲取有序集合的成員數。
3     zcount key min max   計算在有序集合中指定區間分數的成員數
4     zrank key member 返回有序集合中指定成員的索引
5    zincryby key increment member   有序集合中對指定成員的分數加上增量 increment
6    zinterstore destination numkeys key [key ...]   計算給定的一個或多個有序集的交集並將結果集儲存在新的有序集合 key 中
7   zrange key start stop [WITHSCORES]   透過索引區間返回有序集合成指定區間內的成員
8 zrem key member [member ...]   移除有序集合中的一個或多個成員

String:

普通的key/value儲存

Hash:

hget,hset,hgetall
儲存一個使用者物件,以使用者id為key.

List:

lpush,rpush,lpop,lrange
儲存使用者關注列表,粉絲列表
list的實現是一個雙向連結串列,可支援反向查詢和遍歷
redis內部的很多實現,包括髮送緩衝佇列也是這個資料結構

Set:

sadd,spop,smembers.sunion
redis set對外提供的功能與list類似是一個列表的功能。特殊之處是set是可以自動排重的(不重複)。
set內部實現是一個value永遠為null的hashMap.實際是透過hash的方式來快速排重的
set可提供一個成員是否在一個集合內

sorted set

zadd,zrange,zrem,zcard
Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以透過使用者額外提供一個優先順序(score)的引數來為成員排序,並且是插入有序的,即自動排序。當你需要一個有序的並且不重複的集合列表,那麼可以選擇sorted set資料結構,比如twitter 的public timeline可以以發表時間作為score來儲存,這樣獲取時就是自動按時間排好序的。
Redis sorted set的內部使用HashMap和跳躍表(SkipList)來保證資料的儲存和有序,HashMap裡放的是成員到score的對映,而跳躍表裡存放的是所有的成員,排序依據是HashMap裡存的score,使用跳躍表的結構可以獲得比較高的查詢效率,並且在實現上比較簡單。

具體解釋可以參考我的下一篇文章
https://learnku.com/articles/32449

Redis的持久化機制

RDB持久化

RDB持久化是指在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,實際操作過程是fork一個子程式,先將資料集寫入臨時檔案,寫入成功後,再替換之前的檔案,用二進位制壓縮儲存。

AOF(append only file)持久化

AOF持久化以日誌的形式記錄伺服器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文字的方式記錄,可以開啟檔案看到詳細的操作記錄。

Redis持久化磁碟IO方式優缺點

RDB存在優勢:
該方式,整個Redis資料庫只包含一個檔案,對於檔案備份很方便,容易資料恢復
對於Redis服務程式而言,在開始持久化時,需要fork出子程式,之後再由子程式完成這些持久化任務,極大的避免服務程式執行IO操作
相比AOF.如果資料集很大,RDB啟動效率會更高
劣勢:
如果系統當機,未來得及寫入磁碟資料會丟失
由於RDB是透過fork子程式來協助完成資料持久化,因此資料集較大是,會導致伺服器停止服務幾百毫秒

本作品採用《CC 協議》,轉載必須註明作者和本文連結
cfun

相關文章