redis學習總結(超讚!!!)
我是技術搬運工,好東西當然要和大家分享啦原文地址
Redis 是什麼
Redis 是速度非常快的非關係型(NoSQL)記憶體鍵值資料庫,可以儲存鍵和五種不同型別的值之間的對映。
五種型別資料型別為:字串、列表、集合、有序集合、散列表。
Redis 支援很多特性,例如將記憶體中的資料持久化到硬碟中,使用複製來擴充套件讀效能,使用分片來擴充套件寫效能。
Redis 的五種基本型別
資料型別 | 可以儲存的值 | 操作 |
---|---|---|
STRING | 字串、整數或者浮點數 | 對整個字串或者字串的其中一部分執行操作 對整數和浮點數執行自增或者自減操作 |
LIST | 連結串列 | 從兩端壓入或者彈出元素 讀取單個或者多個元素 進行修剪,只保留一個範圍內的元素 |
SET | 無序集合 | 新增、獲取、移除單個元素 檢查一個元素是否存在於集合中 計算交集、並集、差集 從集合裡面隨機獲取元素 |
HASH | 包含鍵值對的無序散列表 | 新增、獲取、移除單個鍵值對 獲取所有鍵值對 檢查某個鍵是否存在 |
ZSET | 有序集合 | 新增、獲取、刪除元素個元素 根據分值範圍或者成員來獲取元素 計算一個鍵的排名 |
STRING
> set hello world OK > get hello "world" > del hello (integer) 1 > get hello (nil)
LIST
> rpush list-key item (integer) 1 > rpush list-key item2 (integer) 2 > rpush list-key item (integer) 3 > lrange list-key 0 -1 1) "item" 2) "item2" 3) "item" > lindex list-key 1 "item2" > lpop list-key "item" > lrange list-key 0 -1 1) "item2" 2) "item"
SET
> sadd set-key item (integer) 1 > sadd set-key item2 (integer) 1 > sadd set-key item3 (integer) 1 > sadd set-key item (integer) 0 > smembers set-key 1) "item" 2) "item2" 3) "item3" > sismember set-key item4 (integer) 0 > sismember set-key item (integer) 1 > srem set-key item2 (integer) 1 > srem set-key item2 (integer) 0 > smembers set-key 1) "item" 2) "item3"
HASH
> hset hash-key sub-key1 value1 (integer) 1 > hset hash-key sub-key2 value2 (integer) 1 > hset hash-key sub-key1 value1 (integer) 0 > hgetall hash-key 1) "sub-key1" 2) "value1" 3) "sub-key2" 4) "value2" > hdel hash-key sub-key2 (integer) 1 > hdel hash-key sub-key2 (integer) 0 > hget hash-key sub-key1 "value1" > hgetall hash-key 1) "sub-key1" 2) "value1"
ZSET
> zadd zset-key 728 member1 (integer) 1 > zadd zset-key 982 member0 (integer) 1 > zadd zset-key 982 member0 (integer) 0 > zrange zset-key 0 -1 withscores 1) "member1" 2) "728" 3) "member0" 4) "982" > zrangebyscore zset-key 0 800 withscores 1) "member1" 2) "728" > zrem zset-key member1 (integer) 1 > zrem zset-key member1 (integer) 0 > zrange zset-key 0 -1 withscores 1) "member0" 2) "982"
鍵的過期時間
Redis 可以為每個鍵設定過期時間,當鍵過期時,會自動刪除該鍵。
對於散列表這種容器,只能為整個鍵設定過期時間(整個散列表),而不能為鍵裡面的單個元素設定過期時間。
過期時間對於清理快取資料非常有用。
釋出與訂閱
釋出與訂閱實際上是觀察者模式,訂閱者訂閱了頻道之後,釋出者向頻道傳送字串訊息會被所有訂閱者接收到。
釋出與訂閱有一些問題,很少使用它,而是使用替代的解決方案。問題如下:
- 如果訂閱者讀取訊息的速度很慢,會使得訊息不斷積壓在釋出者的輸出快取區中,造成記憶體佔用過多;
- 如果訂閱者在執行訂閱的過程中網路出現問題,那麼就會丟失斷線期間傳送的所有訊息。
事務
Redis 最簡單的事務實現方式是使用 MULTI 和 EXEC 命令將事務操作包圍起來。
MULTI 和 EXEC 中的操作將會一次性發送給伺服器,而不是一條一條傳送,這種方式稱為流水線,它可以減少客戶端與伺服器之間的網路通訊次數從而提升效能。
持久化
Redis 是記憶體型資料庫,為了保證資料在斷電後不會丟失,需要將記憶體中的資料持久化到硬碟上。
快照持久化
將某個時間點的所有資料都存放到硬碟上。
可以將快照複製到其它伺服器從而建立具有相同資料的伺服器副本。
如果系統發生故障,將會丟失最後一次建立快照之後的資料。並且如果資料量很大,儲存快照的時間也會很長。
AOF 持久化
AOF 持久化將寫命令新增到 AOF 檔案(Append Only File)的末尾。
對硬碟的檔案進行寫入時,寫入的內容首先會被儲存到緩衝區,然後由作業系統決定什麼時候將該內容同步到硬碟,使用者可以呼叫 file.flush() 方法請求作業系統儘快將緩衝區儲存的資料同步到硬碟。因此將寫命令新增到 AOF 檔案時,要根據需求來保證何時將新增的資料同步到硬碟上,有以下同步選項:
選項 | 同步頻率 |
---|---|
always | 每個寫命令都同步 |
everysec | 每秒同步一次 |
no | 讓作業系統來決定何時同步 |
always 選項會嚴重減低伺服器的效能;everysec 選項比較合適,可以保證系統奔潰時只會丟失一秒左右的資料,並且 Redis 每秒執行一次同步對伺服器效能幾乎沒有任何影響;no 選項並不能給伺服器效能帶來多大的提升,而且也會增加系統奔潰時資料丟失的數量。
隨著伺服器寫請求的增多,AOF 檔案會越來越大;Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 檔案中的冗餘寫命令。
複製
通過使用 slaveof host port 命令來讓一個伺服器成為另一個伺服器的從伺服器。
一個從伺服器只能有一個主伺服器,並且不支援主主複製。
從伺服器連線主伺服器的過程
主伺服器建立快照檔案,傳送給從伺服器,並在傳送期間使用緩衝區記錄執行的寫命令。快照檔案傳送完畢之後,開始向從伺服器傳送儲存在緩衝區中的寫命令;
從伺服器丟棄所有舊資料,載入主伺服器發來的快照檔案,之後從伺服器開始接受主伺服器發來的寫命令;
主伺服器每執行一次寫命令,就向從伺服器傳送相同的寫命令。
主從鏈
隨著負載不斷上升,主伺服器可能無法很快地更新所有從伺服器,或者重新連線和重新同步從伺服器將導致系統超載。為了解決這個問題,可以建立一箇中間層來分擔主伺服器的複製工作。中間層的伺服器是最上層伺服器的從伺服器,又是最下層伺服器的主伺服器。
處理故障
要用到持久化檔案來恢復伺服器的資料。
持久化檔案可能因為伺服器出錯也有錯誤,因此要先對持久化檔案進行驗證和修復。對 AOF 檔案就行驗證和修復很容易,修復操作將第一個出錯命令和其後的所有命令都刪除;但是隻能驗證快照檔案,無法對快照檔案進行修復,因為快照檔案進行了壓縮,出現在快照檔案中間的錯誤可能會導致整個快照檔案的剩餘部分無法讀取。
當主伺服器出現故障時,Redis 常用的做法是新開一臺伺服器作為主伺服器,具體步驟如下:假設 A 為主伺服器,B 為從伺服器,當 A 出現故障時,讓 B 生成一個快照檔案,將快照檔案傳送給 C,並讓 C 恢復快照檔案的資料。最後,讓 B 成為 C 的從伺服器。
分片
Redis 中的分片類似於 MySQL 的分表操作,分片是將資料劃分為多個部分的方法,對資料的劃分可以基於鍵包含的 ID、基於鍵的雜湊值,或者基於以上兩者的某種組合。通過對資料進行分片,使用者可以將資料儲存到多臺機器裡面,也可以從多臺機器裡面獲取資料,這種方法在解決某些問題時可以獲得線性級別的效能提升。
假設有 4 個 Reids 例項 R0,R1,R2,R3,還有很多表示使用者的鍵 user:1,user:2,... 等等,有不同的方式來選擇一個指定的鍵儲存在哪個例項中。最簡單的方式是範圍分片,例如使用者 id 從 0~1000 的儲存到例項 R0 中,使用者 id 從 1001~2000 的儲存到例項 R1 中,等等。但是這樣需要維護一張對映範圍表,維護操作代價很高。還有一種方式是雜湊分片,使用 CRC32 雜湊函式將鍵轉換為一個數字,再對例項數量求模就能知道應該儲存的例項。
客戶端分片
客戶端使用一致性雜湊等演算法決定鍵應當分佈到哪個節點。
代理分片
將客戶端請求傳送到代理上,由代理轉發請求到正確的節點上。
伺服器分片
Redis Cluster。
事件
事件型別
檔案事件
伺服器有許多套接字,事件產生時會對這些套接字進行操作,伺服器通過監聽套接字來處理事件。常見的檔案事件有:客戶端的連線事件;客戶端的命令請求事件;伺服器向客戶端返回命令結果的事件;
時間事件
又分為兩類:定時事件是讓一段程式在指定的時間之內執行一次;週期性時間是讓一段程式每隔指定時間就執行一次。
事件的排程與執行
伺服器需要不斷監聽檔案事件的套接字才能得到待處理的檔案事件,但是不能監聽太久,否則時間事件無法在規定的時間內執行,因此監聽時間應該根據距離現在最近的時間事件來決定。
事件排程與執行由 aeProcessEvents 函式負責,虛擬碼如下:
def aeProcessEvents(): # 獲取到達時間離當前時間最接近的時間事件 time_event = aeSearchNearestTimer() # 計算最接近的時間事件距離到達還有多少毫秒 remaind_ms = time_event.when - unix_ts_now() # 如果事件已到達,那麼 remaind_ms 的值可能為負數,將它設為 0 if remaind_ms < 0: remaind_ms = 0 # 根據 remaind_ms 的值,建立 timeval timeval = create_timeval_with_ms(remaind_ms) # 阻塞並等待檔案事件產生,最大阻塞時間由傳入的 timeval 決定 aeApiPoll(timeval) # 處理所有已產生的檔案事件 procesFileEvents() # 處理所有已到達的時間事件 processTimeEvents()
將 aeProcessEvents 函式置於一個迴圈裡面,加上初始化和清理函式,就構成了 Redis 伺服器的主函式,虛擬碼如下:
def main(): # 初始化伺服器 init_server() # 一直處理事件,直到伺服器關閉為止 while server_is_not_shutdown(): aeProcessEvents() # 伺服器關閉,執行清理操作 clean_server()
從事件處理的角度來看,伺服器執行流程如下:
Redis 與 Memcached 的區別
兩者都是非關係型記憶體鍵值資料庫。有以下主要不同:
資料型別
Memcached 僅支援字串型別,而 Redis 支援五種不同種類的資料型別,使得它可以更靈活地解決問題。
資料持久化
Redis 支援兩種持久化策略:RDB 快照和 AOF 日誌,而 Memcached 不支援持久化。
分散式
Memcached 不支援分散式,只能通過在客戶端使用像一致性雜湊這樣的分散式演算法來實現分散式儲存,這種方式在儲存和查詢時都需要先在客戶端計算一次資料所在的節點。
Redis Cluster 實現了分散式的支援。
記憶體管理機制
在 Redis 中,並不是所有資料都一直儲存在記憶體中,可以將一些很久沒用的 value 交換到磁碟。而 Memcached 的資料則會一直在記憶體中。
Memcached 將記憶體分割成特定長度的塊來儲存資料,以完全解決記憶體碎片的問題,但是這種方式會使得記憶體的利用率不高,例如塊的大小為 128 bytes,只儲存 100 bytes 的資料,那麼剩下的 28 bytes 就浪費掉了。
Redis 適用場景
快取
適用 Redis 作為快取,將熱點資料放到記憶體中。
訊息佇列
Redis 的 List 型別是雙向連結串列,很適合用於訊息佇列。
計數器
Redis 這種記憶體資料庫才能支援計數器的頻繁讀寫操作。
好友關係
使用 set 型別的交集很容易就可以知道兩個使用者的共同好友。
資料淘汰策略
可以設定記憶體最大使用量,當記憶體使用量超過時施行淘汰策略,具體有 6 種淘汰策略。
策略 | 描述 |
---|---|
volatile-lru | 從已設定過期時間的資料集中挑選最近最少使用的資料淘汰 |
volatile-ttl | 從已設定過期時間的資料集中挑選將要過期的資料淘汰 |
volatile-random | 從已設定過期時間的資料集中任意選擇資料淘汰 |
allkeys-lru | 從所有資料集中挑選最近最少使用的資料淘汰 |
allkeys-random | 從所有資料集中任意選擇資料進行淘汰 |
no-envicition | 禁止驅逐資料 |
如果使用 Redis 來快取資料時,要保證所有資料都是熱點資料,可以將記憶體最大使用量設定為熱點資料佔用的記憶體量,然後啟用 allkeys-lru 淘汰策略,將最近最少使用的資料淘汰。
一個簡單的論壇系統分析
該論壇系統功能如下:
- 可以釋出文章;
- 可以對文章進行點贊;
- 在首頁可以按文章的釋出時間或者文章的點贊數進行排序顯示;
文章資訊
文章包括標題、作者、贊數等資訊,在關係型資料庫中很容易構建一張表來儲存這些資訊,在 Redis 中可以使用 HASH 來儲存每種資訊以及其對應的值的對映。
Redis 沒有關係型資料庫中的表這一概念來將同類型的資料存放在一起,而是使用名稱空間的方式來實現這一功能。鍵名的前面部分儲存名稱空間,後面部分的內容儲存 ID,通常使用 : 來進行分隔。例如下面的 HASH 的鍵名為 article:92617,其中 article 為名稱空間,ID 為 92617。
點贊功能
當有使用者為一篇文章點贊時,除了要對該文章的 votes 欄位進行加 1 操作,還必須記錄該使用者已經對該文章進行了點贊,防止使用者點贊次數超過 1。可以建立文章的已投票使用者集合來進行記錄。
為了節約記憶體,規定一篇文章釋出滿一週之後,就不能再對它進行投票,而文章的已投票集合也會被刪除,可以為文章的已投票集合設定一個一週的過期時間就能實現這個規定。
對文章進行排序
為了按釋出時間和點贊數進行排序,可以建立一個文章釋出時間的有序集合和一個文章點贊數的有序集合。(下圖中的 score 就是這裡所說的點贊數;下面所示的有序集合分值並不直接是時間和點贊數,而是根據時間和點贊數間接計算出來的)