Redis學習筆記-基本概念知識
效能優越
redis的效能優越主要來自三個方面
- 基於ANSIC語言編寫,接近於組合語言的機器語言,執行速度快速
- 基於記憶體讀寫
- 資料庫結構只有6種資料型別,資料結構簡單,規則少
應用場景
- 快取常用的資料
- 在需要高速讀寫的場合,比如一些商品搶購和搶紅包的場合
業務考量
- 業務資料常用嗎?命中率如何?如果命中率很低就沒有必要寫入快取
- 該業務是讀操作多,還是寫操作多,如果寫操作多,頻繁寫入資料庫,也沒有必要使用快取
- 業務資料大小如何?如果要儲存幾百兆位元組的檔案,會給快取帶來很大的壓力,有沒有必要?
流程圖
快取
高速讀寫場合
轉換
由於redis只提供基於字串型的操作,而在Java中使用的卻以類物件為主,所以需要redis儲存字串和Java物件相互轉換,Spring對這些進行了封裝和支援,它提供了序列化的設計框架和一些序列化的類,使用它後可以通過序列化把Java物件轉換,使得redis能把它儲存起來,在讀取的時候,再把由序列化的字串轉化為java物件,這樣在Java環境使用redis就更加簡單了,所以更多時候,可以使用Spring提供的RedisTemplate的機制來使用Redis
資料型別
資料型別 | 資料型別儲存的值 | 說明 |
STRING(字串) | 可以儲存字串、整數和浮點數 | 可以對字串進行操作,比如增加字元或者求字串;如果是整數或者浮點數,可以實現計算,比如自增等 |
LIST(列表) | 它是一個連結串列,它的每一個節點都包含一個字串 | Redis支援從連結串列的兩端插入或者彈出節點,或者通過偏移對它進行裁剪;還可以讀取一個或者多個節點,根據條件刪除或者查詢結點等 |
SET(集合) | 它是一個收集器,但是是無序的,在它裡面的每一個元素都是一個字串,而且是獨一無二的,各不相同的 | 可以新增、讀取、刪除單個元素;檢測一個元素是否在集合中;計算它和其他集合的交集、並集和差集等;隨機從集合中讀取元素 |
HASH(雜湊散列表) | 它類似於Java語言中的Map,是一個鍵值對應的無序列表 | 可以增刪改查元素,也可以獲取所有的鍵值對 |
ZSET(有序集合) | 它是一個有序的集合,可以包含字串、整數、浮點數、分值, 元素的排序是根據分值的大小來決定的 |
可以增刪改查元素,根據分值的範圍或者成員來獲取對應的元素 |
HyperLogLog(基數) | 它的作用是計算重複的值,以確定儲存的數量 | 只提供基數的運算,不提供返回功能 |
字串
除了get、set、incr、decr、mget等操作外,Redis還提供了下面一些操作,獲取字串的長度,往字串append內容,設定和獲取字串的一段內容,設定獲取字串的某一位,批量設定一系列字串的內容
注意事項:
- 在測試過程中,如果開始把value設定為浮點數,那麼incr、decr、incrby、decrby的命令都會失敗
- redis並不支援減法、乘法、除法操作
- 由於redis功能比較弱,所以經常會在java程式中讀取它們,然後通過java進行計算並設計它們的值
應用場景
String是最常用的一種資料型別,普通的key/value儲存都可以歸為此類,value其實不僅是String,也可以是數字;比如想知道什麼時候可以封鎖一個ip地址,INCRBY命令可以使這些變得容易,通過原子遞增保持計數
實現方式:m,decr等操作時會轉成數值型進行計算,此時redisObject的encoding欄位為int
雜湊
常用命令:hget,hset,hgetall等
應用場景
比如我們要儲存一個使用者資訊的物件資料,包含以下資訊:
使用者ID,查詢的key
儲存的value使用者物件包含姓名name,年齡age,生日birthday等資訊,
如果用普通的key/value結構來儲存,主要有以下兩種儲存方式:
第一種方式將使用者Id作為查詢的key,把其他資訊封裝成一個物件以序列化的方式儲存,
如 set u001 “李三,18,20010101”
這種方式的缺點是增加了序列化和反序列化的開銷,並且在需要修改其中一項資訊時,需要將整個物件取回,並且操作需要對併發進行保護,引入CAS等複雜問題
第二種方法是這個使用者資訊有多少成員就儲存成多少個key-value對,使用者ID+對應屬性的名稱作為唯一標識來取得對應屬性的值,
如 mset user:001:name”李三” user:001:age18 user:001:birthday”20010101”
雖然省去了序列化開銷和併發的問題,但是使用者ID重複儲存,如果儲存大量的這樣的資料,記憶體浪費還是非常客觀
那麼redis提供的hash很好的解決了這個問題,Redis的hash實際上內部儲存的value是一個hashMap
如 hmset user:001 name “李三” age 18 birthday “20010101”
也就是說key任然是使用者id,value是一個map,這個map的key是成員屬性,value是值。這樣對資料的修改和存取都可以直接通過內部的map的key(redis裡稱內部的key為field),也就是通過key(使用者ID)+field(屬性標籤)操作對應的資料了,既不需要重複村資料,也不會帶來序列化和併發修改。很好的解決了問題。
這裡同時需要注意,redis提供了介面(hgetall)可以直接獲取到全部的屬性資料,但是如果內部的map的成員比較多,那麼涉及到遍歷整個內部的map的操作,由於Redis單執行緒模型的緣故,這個遍歷可能會比較耗時,而其他客戶端的請求完全不響應,這點需要格外注意。
實現方式:
上面已經說的redis hash對應的value內部實際上就是一個hashmap,實際上這裡會有兩個不同的實現,這個hash的成員比較少的時候,redis會為了節省記憶體而採用類似一維陣列的方式來緊湊儲存,而不會採用真正的hashmap結構,對應的value redisobject的encoding為zipmap,而成員數量增大時會自動轉換成真正的hashmap,此時的encoding為ht。
注意事項:
- hmset命令,在javaAPI中,是使用map儲存多個鍵值對在先的
- hgetall命令返回所有的鍵值對,並儲存到一個map物件中,如果hash結構很大,那麼要考慮它對jvm的記憶體影響
- hincrby和hincrbyFloat命令都採用Increment方法,Spring會識別它具體使用那種方法
- redisTemplate.opsForHash().value(key)方法相當於hvals命令,它會返回所有的值,並儲存到一個List物件中;而redisTemplate.opsForHash().key(key)方法相當於hkeys命令,它會獲取所有的key值儲存到一個set物件中
- 在spring中使用redisTemplate.opsForHash().putAll(key,map)方法相當於執行了hmset命令,使用了map,由於配置了預設的序列化器為字串,所以它也只會用字串驚醒轉化,這樣才能執行對應的數值加法,如過使用其他的序列化器,則後面的命令可能會丟擲異常
- 在使用大的hash機構時,需要考慮返回資料的大小,以避免返回太多的資料,引發JVM記憶體溢位或者Redis的效能問題
連結串列
常用命令:lpush,rpush,lpop,lrange,BLOP等
應用場景
Redis list應用場景非常多,也是redis最重要的資料結構之一。
我們可以輕鬆的實現最新訊息排行功能。Lists的另一個應用就是訊息佇列,可以利用Lists的push操作,將任務存在Lists中,然後工作執行緒再用pop操作將任務取出進行執行。
實現方式
Redis list的實現為一個雙向連結串列,既可以支援反向查詢和遍歷,更方便操作,不過也帶來了部分額外的記憶體開銷,redis內部有很多實現,包括髮送緩衝佇列也是用的這個資料結構。
注意事項
- 雙向連結串列,其中以“l”開頭的代表左操作,以“r”開頭的代表右操作
- 對於大量的資料操作的時候,我們需要考慮插入和刪除的內容的大小,因為這將是十分消耗效能的指令,會導致Redis伺服器的卡頓,對於一些不允許卡頓的一些伺服器,可以進行分批次操作,以避免卡頓
- 操作連結串列的命令是程序不安全的,因為多個redis客戶端可能同時操作同一個連結串列,為了避免這種情況,Redis提供了連結串列阻塞命令,它們在執行的時候會給連結串列加鎖
集合
常用命令:sadd,srem,spop,sdiff,smembers,sunion等
應用場景
Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set可以自動排重的,當你需要儲存一個列表資料,又不希望出現重複資料時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set的重要介面,這個也是list所不能提供的。
比如微博應用中,每個人的好友在一個集合中,這樣求兩個人的共同好友操作,只需要求交集命令即可。Redis還提供求交集,並集,差集等操作,非常方便實用,
實現方式:
Set的內部是一個value永遠為null的hashmap,實際上就是計算hash的方式快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。
注意事項
- 對於集合而言,它的每一元素都是不能重複的,當插入相同記錄的時候都會失敗
- 集合是無序的
- 集合的每一個元素都是String資料型別
有序集合
應用場景
以某個條件為權重,比如按從大到小的次序排序
ZREVRANGE命令可以按照得分來取前100名的使用者,ZRANK 可以用來獲取使用者排名,非常直接而且操作容易。
Redis sorted set的使用場景與set類似,區別是set不是有序的,而sorted set可以通過額外提供一個優先順序(score)的引數來為成員排序,並且插入有序的,即自動排序。
比如:twitter的public timeline可以以發表時間作為score來儲存,這樣獲取時就是按時間自動排好的
比如:全班的成績,sortedset,value可以是同學的學號,而score就可以是其考試得分,這樣的資料插入集合,就已經進行了天然的排序。
另外還可以用sorted set來進行帶權重的佇列,比如普通的訊息score為1,重要的訊息score為2,然後工作執行緒可以按照score的倒序來取任務,讓重要的任務先執行。
需要精確的設定過期的時間的應用
比如你可以把score的值設定為過期的時間戳,那麼就可以通過簡單的過期時間排序,定時清除過期資料了,不僅是清除redis的過期資料,你完全可以把這個時間戳當作資料庫的索引,用redis來找出那些資料需要過期清除,然後精確的從資料庫刪除相應的記錄。
實現方式:
Redis sorted set的內部使用Hashmap和跳躍表來保證資料的儲存和有序,hash map裡儲存的是成員到score的對映,而跳躍表裡儲存的是所有成員,排序的依據是hashmap裡儲存的score,使用跳躍表的結構可以獲得較高的查詢效率,並且在實現上比較簡單。
注意事項
- 有序集合和無需集合的主要區別是對於每一個元素除了值以外,它還會多一個分數。
- Redis支援對分數的排序
- 集合是同故宮雜湊實現的,增刪改查的時間複雜度都是O(1)
- 有序集合依賴key表示它是屬於那個集ji依賴分數進行排序
基數
- 基數是一種演算法
- 基數並不是儲存元素,而是給某一個有重複元素集合(一般是很大的資料集合)評估需要的空間單元數
持久化
在redis中存在兩種方式的備份:一種是快照(snapshotting),它是備份當前瞬間Redis在記憶體中的資料記錄;另一種是隻追加檔案(Append-Only File,AOF),其作用就是當Redis執行寫命令後,在一定的條件下將執行過的寫命令依次儲存在redis的檔案中,將來可以一次執行那些儲存的命令恢復redis的資料了。
對於快照備份而言,如果當前Redis的資料量大,備份會造成Redis卡頓,但是恢復重啟是比較快速的
對於AOF備份而言,它只是追加寫命令,所以備份一樣不會造成Redis卡頓,但是回覆重啟需要執行更多的命令,備份檔案可能很大