Redis是一個記憶體資料結構存儲系統,它支援各種資料結構,如字元串、哈希表、清單、集合和有序集合等。Redis通常被用作緩存、消息隊列或實時資料分析的資料庫。
Redis的高性能主要得益于其使用記憶體進行資料存儲和通路,而記憶體的讀寫速度比磁盤要快很多。此外,Redis還采用了單線程模型和異步IO等技術,使得其能夠在短時間内處理大量的并發請求。
除了快速讀寫和高并發處理能力,Redis還提供了一些其他有用的功能,如釋出/訂閱機制、事務處理、Lua腳本等,并且它的用戶端支援很多語言,如Java、Python、C#、Node.js等,這使得它成為一個非常流行和适用于各種場景的資料存儲解決方案。
下面分别對Redis支援的資料結構進行詳細介紹和舉例:
- 字元串(String)
字元串可以存儲任何類型的資料,如數字、JSON、XML等。操作包括設定值、擷取值、計數器操作、批量操作等。
舉例:
# 設定字元串類型鍵值對
set mykey "hello world"
# 擷取字元串類型鍵值對
get mykey
# 将一個數值加1
incr mycounter
- 哈希表(Hash)
哈希表可以用來存儲對象,對象由字段和對應的值組成。操作包括設定、擷取、删除單個或多個字段、擷取所有字段等。
舉例:
# 設定哈希表類型鍵值對
hmset user:1 name "John" age 30 email "[email protected]"
# 擷取哈希表特定字段的值
hget user:1 name
# 擷取哈希表所有字段和值
hgetall user:1
- 清單(List)
清單是一個有序集合,每個元素都有一個索引。操作包括插入、删除、擷取元素等。
舉例:
# 向清單中添加元素
lpush mylist "world"
lpush mylist "hello"
# 擷取清單中指定範圍的元素
lrange mylist 0 -1
# 從清單中删除指定元素
lrem mylist 1 "hello"
- 集合(Set)
集合是一組無序的元素,每個元素都是唯一的。操作包括添加、删除、擷取元素等。
舉例:
# 向集合中添加元素
sadd myset "apple"
sadd myset "banana"
# 擷取集合中所有元素
smembers myset
# 從集合中删除元素
srem myset "banana"
- 有序集合(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能夠高效地處理大量資料,并在低延遲下提供快速響應。
- 底層記憶體管理
Redis會把所有資料存儲在記憶體中,而不是在硬碟上。這樣可以大大提高資料通路速度,特别是在需要頻繁讀寫的場景中。同時,Redis也提供了多種機制來降低記憶體使用量,包括壓縮資料、删除過期資料等。
- 資料結構設計
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 作為一個高效的記憶體資料庫,被廣泛應用于緩存場景中。
在 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 可以自動删除過期的資料。
- 消息隊列
消息隊列是一種常見的分布式系統架構中的元件之一,可以用于異步任務處理、事件驅動等場景。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(): 就會擷取到消息并進行處理。
- 會話存儲
會話存儲是一個常見的需求,它可以用于存儲使用者的登入狀态、浏覽器曆史記錄等資料。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 的常見的高可用方案之一,它可以保證在 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
- 叢集模式
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,用于記錄叢集的狀态。
- 哨兵模式
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 節點。