1. 程式人生 > >Redis學習筆記-基本概念知識

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(基數) 它的作用是計算重複的值,以確定儲存的數量 只提供基數的運算,不提供返回功能

字串

 

除了getsetincrdecrmget等操作外,Redis還提供了下面一些操作,獲取字串的長度,往字串append內容,設定和獲取字串的一段內容,設定獲取字串的某一位,批量設定一系列字串的內容

 

注意事項:

 

  • 在測試過程中,如果開始把value設定為浮點數,那麼incrdecrincrbydecrby的命令都會失敗
  • redis並不支援減法、乘法、除法操作
  • 由於redis功能比較弱,所以經常會在java程式中讀取它們,然後通過java進行計算並設計它們的值

 

應用場景

 

String是最常用的一種資料型別,普通的key/value儲存都可以歸為此類,value其實不僅是String,也可以是數字;比如想知道什麼時候可以封鎖一個ip地址,INCRBY命令可以使這些變得容易,通過原子遞增保持計數

 

實現方式:m,decr等操作時會轉成數值型進行計算,此時redisObjectencoding欄位為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卡頓,但是回覆重啟需要執行更多的命令,備份檔案可能很大