Redis是C語言開發的一個高性能鍵值對(key -value) 內存數據庫,可以用作數據庫,緩存和消息中間件等。
文章目錄
Redis
Redis是什麼
簡介
Redis是C語言開發的一個高性能鍵值對(key -value) 內存數據庫,可以用作數據庫,緩存和消息中間件等。
特點
-
作爲內存數據庫,它的性能非常優秀,數據存儲在內存當中,讀寫速度非常快,支持併發10W QPS(每秒查詢次數),單進程單線程,是線程安全的,採用IO多路複用機制。
-
豐富的數據類型,支持字符串,散列,列表,集合,有序集合等,支持數據持久化。可以將內存中數據保存在磁盤中,重啓時加載。
-
主從複製,哨兵,高可用,可用作分佈式鎖。可以作爲消息中間件使用,支持發佈訂閱。
Redis 數據類型
和Java的數據結構類比:
Redis | Java |
---|---|
string | String |
hash | HashMap |
list | LinkedList |
set | HashSet |
sorted set | TreeSet |
1. string (字符串)
介紹:
string是最基本的數據類型,跟 mencached 一樣的類型,也是二進制安全的,可以包含任何數據
如:jpg圖片或者序列化的對象。最大能存儲512MB。
特性
最大能存儲容量:
512MB
數值計算最大範圍:
Java中long的最大值 2^32-1
數據未獲取到:
爲nil相當於null
表示運行結果是否成功:
integer 0 -> false 失敗
integer 1 -> true 成功
表示運行結果值:
integer 3 -> 3條
integer 1 -> 1條
關係型數據庫數據在Redis中 key 熱點數據命名慣例:
大部分數據都是從數據庫來的:所以
一般的命名規範都會是:key[表名:主鍵名:主鍵值:字段名] value[xxxx]
表名 :主鍵名 :主鍵值 :字段名
order :id : 11111 : name
equip :id : 11111 : type
news :id : 11111 : title
用法:
單個操作set get 多個操作加前綴mset mget (Multiple多個)
根據發送時長和執行時長來判斷用單指令還是多指令
// 添加/修改
set key value
// 獲取
get key
// 刪除 1成功,0失敗
del key
// 添加/修改多個
mset key1 value1 key2 value2 ....
// 獲取多個
mget key1 key2 ....
// 獲取value的數據字符長度
strlen key
// 追加信息到值後面(存在就追加,否則新增)
append key value
擴展操作1:Redis解決MySQL分庫分表操作的唯一ID問題
場景:
大型企業級應用中,分表操作是基本操作,使用多張表存儲同類型數據,但是對應的主鍵ID必須保證統一性,不能重複。
oracle數據庫具有sequence設定,可以解決,但是MySQL沒有類似機制。
解決辦法:
利用Redis 生成ID,主要利用Redis是單線程,所以也可以用來生成唯一ID,當使用的是Redis集羣的時候,比如集羣中有5臺Redis,初始化每臺Redis的值爲1,2,3,4,5,設置步長爲5,並且確定一個不隨機的負載均衡策略,能夠保證有序唯一。
優點:不依賴數據庫,靈活,且性能相對於數據庫有一定提高,使用Redis集羣策略還能排除單點故障問題,ID天然有序。
缺點:需要引入Redis這個新組件,開發配置工作量大
//設置數值數據增加指定範圍的值,如果incr傳的負數,會變成減,如果decr傳整數,變成加
//每次+1,key+=1
incr key
//每次+指定的值,key+=increment
incrby ket increment
//每次增加指定小數的值
incrbufloat ket increment
//設置數值數據減少指定範圍的值
//每次減一,key-=1
decr key
//每次減指定的值,key-=increment
decrby key increment
注意事項:
-
string在Redis內部爲字符串存儲,當它進行操作的時候會轉類型爲數字,如果含有其他非數字字符,則會報錯.
-
超出數值上限也會報錯,比如用Java語言的long類型
基本類型:long 二進制位數:64
包裝類:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808 (-2的63次方)
最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)
- Redis所有操作都是原子性的,都是單線程處理所有業務,所以無需擔心併發帶來的數據問題。
另一篇介紹常見的唯一ID生成方案,數據庫自增/UUID/snowflake雪花算法/zookeeper/MongoDB的objectId
擴展操作2:數據時效性問題
Redis 控制數據的生命週期,通過數據是否失效控制業務行爲,適用於所具有時效性限定控制的操作。
場景:
-
微信投票:每四小時只能投一票。
-
電商熱門商品推薦:熱門商品不能一直處於熱門期,每種商品熱門期維持3天,3天后自動取消熱門。
-
熱點新聞,新聞網站會出現熱點新聞,最大特徵是時效性,所以如何控制熱點新聞的時效性。
解決辦法:
設置數據具有指定的生命週期:
// 設置N秒時間,N秒後失效刪除掉數據
setex key seconds values
// 設置N毫秒時間,N毫秒後失效刪除掉數據
psetex key milliseconds value
擴展操作3:主頁高頻訪問信息顯示控制
Redis 應用於各種結構型和非結構型高熱度數據訪問加速
場景:
例如微博大V主頁顯示的粉絲數與微博數量,每個用戶訪問到其主頁都會有這幾個數據
解決辦法:
Redis 存儲格式
- 以用戶主鍵和屬性作爲 key ,數量爲 value ,後臺設定定時刷新策略
// set 用戶:id:id號:粉絲 粉絲數
set user:id:11111:fans 20
// set 用戶:id:id號:博客 博客數
set user:id:11111:blogs 20
// set 用戶:id:id號:關注 關注數
set user:id:11111:focus 20
- 以用戶主鍵作爲 key ,JSON作爲 value,定時刷新(也可以用hash類型)
// set 用戶:id:id號 JSON
set user:id:11111 {ID:11111,NAME:大V,fans:20,blogs:20,focus:30}
對於第一種,可以隨時利用incr key進行數據更新,
而第二種需要全取出來纔可以更新(取數據方便,更新數據麻煩),不過對於這種而言,上面舉例的粉絲數多幾個少幾個用戶也不會特別去關注,只需設定刷新策略,來選擇用哪種方式去實現。
2. hash(哈希):
介紹:
hash 是一個string類型的key-field-value 映射表,特別適合用來存儲對象,和單個string類型不同的是,hash可以對信息的每個屬性字段進行單獨存儲,而string類型則需要對用戶對象進行序列化保存,並且以字符串存儲整個序列化數據(整存整取)對象類的數據存儲如果有比較頻繁的更新需求操作會顯得笨重,而hash可以進行整存零取,從而節約網絡流量,不過內存佔用也會相對比string大。
新的存儲需求:對一系列存儲的數據進行編組,方便管理,典型應用存儲對象信息
需要的存儲結構:一個存儲空間保存多個鍵值對數據
hash類型:底層使用哈希表結構實現數據存儲
特性
-
hash存儲結構優化:
- 如果field數量較少,存儲結構優化爲類數組結構
- 如果field數量較多,存儲結構優化爲HashMap結構
-
hash 類型下的value 只能存儲字符串,不允許存儲其他數據類型,不存在嵌套現象,如果數據未獲取到,則返回nil(NULL)
-
每個hash可以存儲2^32-1個鍵值對
-
hash類型十分貼近對象的數據存儲形式,並且可以靈活添加刪除對象屬性。但是hash設計初衷不是爲了存儲大量對象而設計的,所以不能濫用,更不可以將hash作爲對象列表使用。
-
hgettall 操作可以獲取全部屬性,如果內部field過多,遍歷整體數據效率就會很低,有可能成爲數據訪問瓶頸。
命名慣例
用法
- 添加/修改數據
// 單個
hset key field value
// 多個
hmset key field1 value1 field2 value2..
- 獲取數據
// 單個
hget key field
// 多個
hmget key field1 field2..
- 獲取全部屬性
hgettall key
- 刪除數據
hdel key field1
- 獲取哈希表中字段的數量
hlen key
- 獲取哈希表中是否存在指定的字段
hexists key field 1存在 0不存在
- 獲取哈希表中所有的字段名稱或字段值
// 獲取所有的field名
hkeys key
// 獲去所有的field-value值
hvals key
- 設置指定字段的數值數據增加指定範圍的值
// 增加指定的數值
hincrby key field increment
// 增加指定小數的值
ingcrbyfolat field increment
- 如果key 對應的 field的沒有值,則不添加,否則插入新的field
hsetnx key field value
擴展操作1:購物車設計
場景:
電商網站購物車設計與實現
解決辦法:
1、初步設計:
- 以客戶ID作爲key,每位客戶創建一個hash存儲結構存儲對應的購物車信息。
- 將商品編號作爲field,購買數量作爲value進行存儲。
- 添加商品:追加全新的field與value
- 瀏覽:遍歷hash
- 更改數量;自增/增減,設置value值
- 刪除商品:刪除field
- 清空:刪除key
2、改進設計:
上述的設計僅僅在緩存裏設置了商品ID和數量,但是一個完整的購物車是除了商品ID和數量之外,還顯示一定量的商品信息(標題、介紹、類型之類),按照初步設計來的話,要顯示商品信息還得去數據庫查,這就造成了數據庫壓力增大,並沒有提高效率,所以商品信息也該緩存起來。
解決:
-
將每條購物車中的商品記錄保存成兩條field
-
field1專用於保存購買數量:
- 命名格式:商品ID:數量
- 保存數據:商品數量
-
field2專用於保存商品信息:
- 命名格式:商品ID:信息
- 保存數據:商品信息JSON
問題:如果多個用戶添加同一件商品到購物車,那麼每個購物車都會保存一份同樣的商品信息,所以,需要將商品信息獨立出來存儲,做一個獨立的Hash,減少不必要的開支。又回到了之前保存的商品ID-數量這一個設計上面。
解決:
用到了 hsetnx key field value 命令, 如果 key 對應的 field 的沒有值,則不添加,否則插入新的 field
擴展操作2:用戶熱點信息
上述 string 操作所存儲的用戶熱點信息爲例
場景:
將
// set 用戶:id:id號:粉絲 粉絲數
set user:id:11111:fans 20
// set 用戶:id:id號:博客 博客數
set user:id:11111:blogs 20
// set 用戶:id:id號:關注 關注數
set user:id:11111:focus 20
和
// set 用戶:id:id號 JSON
set user:id:11111 {ID:11111,NAME:大V,fans:20,blogs:20,focus:30}
graph LR
user:id:11111-->JSON
改成利用hash存儲:
擴展操作3:商家搶購活動
場景:
雙十一活動日,銷售手機充值卡的商家對移動、聯通、電信的30元、50元、100元商品退出搶購活動,每種商品搶購上線100張。
解決方案:
- 以商家ID作爲 key
- 將參與搶購的商品ID作爲 field
- 將參與搶購的商品數量作爲對應的 value
- 搶購時使用降值的方式控制產品數量(實際業務還有超賣等問題,後面再討論)
tips : redis 應用於搶購,限購類,限量發放優惠卷,激活碼等業務的數據存儲設計。
對比string 和 hash
- string適合用於整個操作(如json方式存儲的信息)查詢起來比較方便
- hash 適合用於零散的操作(hash內部又是包含多個field-value)比較適合經常進行修改操作的數據
3. list (列表):
介紹
- 數據存儲需求:存儲多個數據,並對數據進入存儲空間的順序進行區分
- 需要的存儲結構:一個存儲空間保存多個數據,且通過數據可以體現進入順序
- list類型:保存多個數據,底層使用雙向鏈表存儲結構實現
- Value對象內部是以LinkedList和ZipList承載。當List的元素個數和單個元素的長度較小時,redis會使用ZipList存儲,減少內存的佔用,其他情況使用LinkedList
ZipList(壓縮列表)使用條件
1、列表對象保存的所有字符串元素的長度都小於64字節 2、列表對象保存的元素數量小於512個
特性:
-
list中保存的數據都是string類型的,數據總容量是有限的,最多2^32-1個元素
-
list具有索引的概念,但是操作數據時通常以隊列的形式進行入隊出隊操作,或者以棧的形式進行入棧出棧操作。(左右操作)
-
獲取全部數據結束索引設置爲-1
-
list可以對數據進行分頁操作,通常第一頁的信息來自於list,第二頁以及更多的信息再通過數據庫查詢加載,這樣可以避免數據庫壓力過大,提高查詢速度,因爲一般首先看到的是第一頁,後面的頁數信息也不一定去看。
技術方案都不會只用一種去解決問題的,都是混合式方案,通過某種技術去解決其中一個點,可能獲得的提升也是很大的。
用list比較合適去解決:
- 依賴list的數據具有順序的特徵信息進行管理
- 使用隊列模型解決多路信息彙總合併的問題
- 使用棧模型解決最新消息的問題
舉例:使用list結構實現棧和隊列。
棧後進先出用lpush+blpop ,
隊列先進先出用 lpush+brpop,可應對多客戶端消費一個隊列。
使用:
r: right右 --> rpush右邊插入
l:left左 --> lpush左邊插入
- 添加/修改數據
lpush key value1 [value2].....lpush key 0 -1 0代表首個元素,-1表示倒數第一個,所以用0 -1可以查全部的元素
lpush key value1 [value2].....
- 獲取數據
lrange key start stop // 第幾位開始-第幾位結束
lindex key index // list第幾個
llen key // list長度
- 獲取並移除數據
lpop key
rpop key
- 規定時間內獲取並移除數據
blpop key1 [key2] timeout //block 阻塞版本的移除,根據timeout等n秒,再獲取數據有數據則取,沒有則nil
- 移除指定數據
// 在某個key列表移除count個 value元素
lrem key count value
擴展操作1:朋友圈點贊列表
場景:
微信朋友圈點贊,要求按照點贊順序顯示點贊好友信息。
解決辦法:
將這條朋友圈信息作爲Key,點贊列表作爲value,每一個新人點贊都將用rpush 加到value裏面,當用戶想取消點讚的時候,該怎麼辦呢?
因爲用戶位置在過一段時間後,不一定會是在最後,對於這種情況可以用到上面的移除指定數據 lrem 命令lrem list 1 xxx
根據上面的例子:redis 可應用於具有操作先後順序的數據控制 ,例如消息隊列,數據隊列,都可以用list來模擬
擴展操作2:用於最新消息展示
場景:
(用在數據庫裏的話就是與最新添加的數據按照時間倒序顯示差不多 order by time DESC)
個人用戶的關注列表需要按照用戶的關注順序進行展示,粉絲列表需要將最近的粉絲列在前面
新聞資訊類的網站將最新新聞資訊按照時間順序展示
集羣服務器日誌統一順序輸出,將所有集羣服務器日誌都打到Redis緩存服務器上,再有其他服務器在緩存上讀取日誌。
list能存儲大量數據,而且它還有存儲順序,能用對應的索引訪問,看上去是非常好的存儲方式,但是list的底層內部存儲結構是一個鏈表結構。 我們知道鏈表結構的存儲效率是很低的,對於這麼一個能存儲大量數據的,但是讀取速度慢的結構,就不合適用了,需要引入一種新的存儲結構set
4. set (集合):
介紹:
新的存儲需求:存儲大量的數據,在查詢方面提供更高的效率
需要的存儲結構:能夠保存大量的數據,高校的內部存儲機制,便於查詢
set類型:與hash存儲結構完全相同,僅存儲鍵(key,field)值,不存儲值(value = nil),並且值不允許重複(field天然不允許重複,重複就覆蓋了之前的)也就是隻利用key-field這部分來存儲數據
集合中最大的成員數爲 每個集合可存儲40多億個成員)。
value 的空間無法使用,因爲開發人員已經決定使用Set這一類型作爲存儲結構,那麼它能調用的API已經規定了,不能跨結構使用
結構對比:
hash類型:
set類型:value 爲空
基本操作:
添加數據
// 類型不允許重複,如果添加數據在set中存在,將保留一份,後續數據添加失敗
sadd key member1 [member2]
獲取全部數據
smembers key
刪除數據
srem key member1 [member2]
獲取集合數據總量
scard key
判斷集合中是否包含指定數據
sismember key member
隨機獲取集合中指定數量的數據
srandmember key [count]
隨機獲取集合中的某個數據並將該數據移除集合
spop key [count]
擴展操作1:數據信息推薦
Redis 用於隨機推薦類信息檢索,例如熱點歌單推薦,熱點新聞推薦,熱賣旅遊線路,應用APP推薦,博主大V推薦
場景:
共同好友,可能認識的人,微博XX也關注了它
解決方案:
通過下列指令來獲取A ∪ B, A ∩ B
,A - B
用於同類信息的關聯搜索:顯示共同關注/顯示共同好友
求兩個集合的交、並、差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
求兩個集合的交、並、差集並存儲到指定集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]
將指定數據從原始集合中移動到目標集合中
smove source destination member
擴展操作2:業務權限校驗
場景:
OA系統員工有一個或者多個角色,每一種角色對應多種不同業務權限,需要快速的進行業務權限校驗
解決方案:
依賴set集合數據不重複的特性,依賴sset集合hash存儲結構特徵完成數據過濾與快速查詢
根據用戶ID獲取用戶所有角色
根據用戶所有角色獲取用戶所有操作權限放入set集合
根據用戶所有角色獲取用戶所有數據入set集合
這裏有兩種方式來驗證權限:
- 將當前用戶:業務的所有權限都查詢出來,然後將查詢結果在代碼裏遍歷比對當前用戶是否擁有此權限
//查詢 用戶id:01業務的所有權限
smembers userid:01
- 直接通過key找到對應的權限返回,有則是1(代表true)
//判斷userid:01集合中是否包含insert這個指定數據
sismember userid:01 insert
對比上面1和2兩種方式,我們使用Redis來提供基礎數據還是提供校驗結果?
第一種是將數據和業務邏輯分離開來,redis只提供數據,業務校驗在業務層將結果遍歷處理
第二種是將業務校驗放在數據提供方進行校驗,直接將結果返回
現在流行數據-業務分離,數據是純粹提供基礎數據,但是業務邏輯處理還是在代碼裏,但是第二種的處理方式是,Redis不僅僅提供了基礎數據,而且還將這部分業務校驗邏輯一併處理,這種方式的業務耦合度會比較高(按照分層思想,我們不應該將業務邏輯處理放到數據提供層去
),比較推薦第一種做法,該分的還是得分,避免以後擴展業務邏輯的不必要的麻煩
擴展操作3:網站訪問數據統計
利用 Redis 的Set集合的數據去重特性,記錄各種訪問數據
場景:
網站數據統計,公司網站推廣,需要統計網站的PV(訪問量),UV(獨立訪問量),IP(獨立IP)。
解決方案:
PV:網站被訪問次數,可通過刷新頁面提高訪問量
這種刷新就加一的數據,可以直接用string類型存儲,利用它的incr命令來統計日訪問量(PV)
UV:網站被不同用戶訪問的次數,可通過cookie統計訪問量,相同用戶切換IP地址,UV不變
建立set模型,記錄不同cookie數量(UV)利用scard統計
IP:網站被不同IP地址訪問的總次數,可通過IP地址統計訪問量,相同IP不同用戶訪問,IP不變
建立set模型,記錄不同IP數量(IP)利用scard統計
擴展操作4:網站黑白名單
利用 Redis 的Set集合的數據去重特性,記錄各種訪問數據
場景:
基於Redis製作網站黑名單,過濾IP地址、設備信息、用戶信息等
解決方案:
建立set模型,記錄不同名單信息,提供給業務邏輯層進行訪問權限判定
5. sorted_set (有序集合):
介紹:
數據排序有利於數據的有效展示,需要提供一種可以根據自身特徵進行排序的方式
Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重複的成員。
在set的存儲結構基礎上做改變,不同的是每個元素都會關聯一個 double/整數 類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。如果爲整數類型則爲64位。
有序集合的成員是唯一的,但分數(score)卻可以重複。
集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。 集合中最大的成員數爲 2^32 - 1 (4294967295, 每個集合可存儲40多億個成員)。
set類型:value 爲空
sorted_set類型:value 爲空,多加一個score,用來做排序用
用法
添加數據
//添加KEY 排序爲score1 值爲member1
zadd key score1 member1 [score2 member2]
獲取全部數據
zrange key start stop
// 獲取key裏面的數據 0 -1 【WITHSCORES爲可選,加上可以顯示出每個值對應排序的分數】
zrange key start stop [WITHSCORES]
//翻轉獲取,倒敘-由大到小,默認由小到大排序
zrevrange key start stop [WITHSCORES]
刪除數據
zrem key member [member...]
按條件獲取數據
// min與max用於限定搜索查詢的條件
// start與stop用於限定查詢範圍,作用於索引,表示開始和結束索引
// offset與count用於限定查詢範圍,作用於查詢結果,表示開始位置和數據總量
zrangebyscore key min max [WITHSCORES] [LIMIT]
zrevrangebyscore key max min [WITHSCORES]
條件刪除數據
zremrangebyrank key start stop
zremrangebyscore key min max
獲取集合數據總量
zcard key
zcount key min max
集合交、並操作
zinterstore destination numkeys key [key ...]
zunionstore destination numkeys key [key ...]
擴展操作1:網站榜單排序
利用 Redis 的sorted_set集合的數據排序特性實現各類榜單,應用於計數器組合排序功能對應的排名
場景:
各類投票、網站排名、活躍度統計榜單(百度熱榜),遊戲親密度(如王者榮耀的)等等。。。
解決方案:
獲取數據對應的索引(排名)
zrank key member
zrevrank key member
score值獲取與修改
zscore key member
zincrby key increment member
擴展操作2:網站會員試用體驗
利用 Redis 應用於定時任務執行順序管理或任務過期管理
場景:
各類VIP體驗、開啓投票、討論,限時進行,逾期作廢等會過期的信息
解決方案:
對於基於時間線限定的任務處理,將處理時間記錄爲score值,利用排序功能區分處理的先後順序
記錄下一個要處理的時間,當到期後處理對應任務,移除redis中的記錄,並記錄下一個要處理的時間
當新任務加入時,判定並更新當前下一個要處理的任務時間
爲提升sorted_set的性能,通常將任務根據特徵存儲成若干個sorted_set。例如1小時內,1天內,周內, 月內,季內,年度等,操作時逐級提升,將即將操作的若干個任務納入到1小時內處理的隊列中
擴展操作3:任務隊列權重管理
應用於即時任務/消息隊列執行管理
場景:
任務/消息權重設定應用 當任務或者消息待處理,形成了任務隊列或消息隊列時,對於高優先級的任務要保障對其優先處理,如 何實現任務權重管理。
解決方案:
對於帶有權重的任務,優先處理權重高的任務,採用score記錄權重即可
應用場景總結:
- redis用於控制數據庫表主鍵id,爲數據庫表主鍵提供生成策略,保障數據庫表的主鍵唯一性
- redis 控制數據的生命週期,通過數據是否失效控制業務行爲,適用於所有具有時效性限定控制的操作
- redis應用於各種結構型和非結構型高熱度數據訪問加速
- redis 應用於購物車數據存儲設計
- redis 應用於搶購,限購類、限量發放優惠卷、激活碼等業務的數據存儲設計
- redis 應用於具有操作先後順序的數據控制
- redis 應用於最新消息展示
- redis 應用於隨機推薦類信息檢索,例如熱點歌單推薦,熱點新聞推薦,熱賣旅遊線路,應用APP推薦,大V推薦等
- redis 應用於同類信息的關聯搜索,二度關聯搜索,深度關聯搜索
- redis 應用於同類型不重複數據的合併、取交集操作
- redis 應用於同類型數據的快速去重
- redis 應用於基於黑名單與白名單設定的服務控制
- redis 應用於計數器組合排序功能對應的排名
- redis 應用於定時任務執行順序管理或任務過期管理
- redis 應用於及時任務/消息隊列執行管理
- redis 應用於按次結算的服務控制
- redis 應用於基於時間順序的數據操作,而不關注具體時間