Redis都有哪些使用場景

7small7發表於2022-06-15
本文轉自Redis面試大全,彙總PHP、Golang、Redis、MySQL等相關面試題。

聊聊Redis現狀

Redis作為一種記憶體型的非關係型的資料庫,不管在網際網路大廠,小廠,大專案和小專案中,幾乎都會被使用。為什麼Redis會受到如此青睞呢?關於這個問題,可能很多的程式設計師只是看著別人用而用,缺乏對Redis一個全面的瞭解。

Redis使用場景

快取

快取現在幾乎是所有中大型網站都在用的必殺技,合理的利用快取不僅能夠提升網站訪問速度,還能大大降低資料庫的壓力。Redis提供了鍵過期功能,也提供了靈活的鍵淘汰策略,所以,現在Redis用在快取的場合非常多。

排行榜

很多網站都有排行榜應用的,如京東的月度銷量榜單、商品按時間的上新排行榜等。Redis提供的有序集合資料類構能實現各種複雜的排行榜應用。

計數器

什麼是計數器,如電商網站商品的瀏覽量、視訊網站視訊的播放數等。為了保證資料實時效,每次瀏覽都得給+1,併發量高時如果每次都請求資料庫操作無疑是種挑戰和壓力。Redis提供的incr命令來實現計數器功能,記憶體操作,效能非常好,非常適用於這些計數場景。

分散式會話

叢集模式下,在應用不多的情況下一般使用容器自帶的session複製功能就能滿足,當應用增多相對複雜的系統中,一般都會搭建以Redis等記憶體資料庫為中心的session服務,session不再由容器管理,而是由session服務及記憶體資料庫管理。

分散式鎖

在很多網際網路公司中都使用了分散式技術,分散式技術帶來的技術挑戰是對同一個資源的併發訪問,如全域性ID、減庫存、秒殺等場景,併發量不大的場景可以使用資料庫的悲觀鎖、樂觀鎖來實現,但在併發量高的場合中,利用資料庫鎖來控制資源的併發訪問是不太理想的,大大影響了資料庫的效能。可以利用Redis的setnx功能來編寫分散式的鎖,如果設定返回1說明獲取鎖成功,否則獲取鎖失敗,實際應用中要考慮的細節要更多。

社交網路

點贊、踩、關注/被關注、共同好友等是社交網站的基本功能,社交網站的訪問量通常來說比較大,而且傳統的關聯式資料庫型別不適合儲存這種型別的資料,Redis提供的雜湊、集合等資料結構能很方便的的實現這些功能。

最新列表

Redis列表結構,LPUSH可以在列表頭部插入一個內容ID作為關鍵字,LTRIM可用來限制列表的數量,這樣列表永遠為N個ID,無需查詢最新的列表,直接根據ID去到對應的內容頁即可。

訊息系統

訊息佇列是大型網站必用中介軟體,如ActiveMQ、RabbitMQ、Kafka等流行的訊息佇列中介軟體,主要用於業務解耦、流量削峰及非同步處理實時性低的業務。Redis提供了釋出/訂閱及阻塞佇列功能,能實現一個簡單的訊息佇列系統。另外,這個不能和專業的訊息中介軟體相比。

如何使用

上面提到了各種使用場景,在這些場景使用中,無非就是對Redis資料型別的操作。就需要對Redis資料型別有所瞭解。在Redis中有這些資料型別。
String、Hash、List、Set、Zset、GEO、Stream、HyperLogLog、BitMap。

資料使用場景

String型別

String型別是一種字串型別,類似一種鍵值對的形式。

一般我們用String型別用來儲存商品數量、使用者資訊和分散式鎖等應用場景。

儲存商品數量。

set goods:count:1 1233
set goods:count:2 100

使用者資訊。

set user:1 "{"id":1, "name":"張三", "age": 12}"
set user:2 "{"id":2, "name":"李四", "age": 12}"

分散式鎖。

# 設定一個不存在的鍵名為id:1值為10, 過期時間為10秒。
127.0.0.1:6379> set id:1 10 ex 10 nx
OK
127.0.0.1:6379> get id:1
"10"
# 當前的鍵還未過期,在次設定則不會設定成功。
127.0.0.1:6379> set id:1 10 ex 10 nx
(nil)
# 當10秒之後去獲取,當前的鍵則為空。
127.0.0.1:6379> get id:1
(nil)
用Redis實現分散式鎖的原理,當一個鍵存在則設定失敗。

hash型別

hash型別是一種類似關係型資料庫結構的資料結構。有一個鍵名,鍵存的內容是以鍵值對的形式存在。

利用hash結構,我們可以用來儲存使用者資訊、物件資訊等業務場景。

存使用者資訊。

127.0.0.1:6379> hset user:1 id 1 name zhangsan age 12 sex 1
(integer) 4
127.0.0.1:6379> hset user:2 id 2 name lisi age 14 sex 0
(integer) 4
127.0.0.1:6379> hmget user:1 id name age sex
1) "1"
2) "zhangsan"
3) "12"
4) "1"

儲存物件資訊。

127.0.0.1:6379> hset object:user id public-1 name private-zhangsan
(integer) 2
127.0.0.1:6379> hmget object:uesr id name
1) (nil)
2) (nil)
127.0.0.1:6379> hget object:user id
"public-1"
127.0.0.1:6379>
這裡儲存一個user物件,物件裡面有兩個屬性,分別是id和name字,分別儲存的是屬性的訪問許可權和預設值拼接。

list型別

list型別是一個列表型別的資料結構。用一個鍵,按照順序排列資料。

list一般用在的場景是佇列、棧和秒殺等場景。

佇列。

127.0.0.1:6379> lpush list:1 0 1 2 3 4 5 6
(integer) 7
127.0.0.1:6379> rpop list:1 1
1) "0"
127.0.0.1:6379> rpop list:1 1
1) "1"
127.0.0.1:6379> rpop list:1 1
1) "2"
使用list實現佇列,是因為佇列遵循先進先出的特點。

棧。

127.0.0.1:6379> lpush list:1 3 4 5 6
(integer) 3
127.0.0.1:6379> lpop list:1
"6"
127.0.0.1:6379> lpop list:1
"5"
127.0.0.1:6379> lpop list:1
"4"
127.0.0.1:6379> lpop list:1
"3"
使用list實現佇列,是因為佇列遵循後進先出的特點。

秒殺。

127.0.0.1:6379> lpush order:user 11 12 14 15 16 17
(integer) 6
在秒殺場景下,我們可以將秒殺成功的使用者先寫進佇列,後續的業務在根據佇列中資料進行處理。

set型別

zet是一種集合型別,並且這種集合內的元素是無需且不會重複的。

set型別一般可以用在使用者簽到、網站訪問統計、使用者關注標籤、好友推薦、猜獎、隨機數生成等業務場景。

某日使用者簽到情況。

127.0.0.1:6379> sadd sign:2020-01-16 1 2 3 4 5 6 7 8
(integer) 8
127.0.0.1:6379> smembers sign:2020-01-16
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
鍵為具體某日,儲存的值則是簽到使用者的id。

使用者關注標籤。

127.0.0.1:6379> sadd user:1:friend 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> sadd user:2:friend 11 22 7 4 5 6
(integer) 6
127.0.0.1:6379> sinterstore user:relation user:1:friend user:2:friend
(integer) 3
127.0.0.1:6379> smembers user:relation
1) "4"
2) "5"
3) "6"
使用者1關注了id為1,2,3,4,5,6的欄目。使用者2關注了id為11,22,7,4,5,6的欄目。這裡取兩個使用者共同關注的欄目。

猜獎。

127.0.0.1:6379> spop user:2:friend 1
1) "5"
用set實現猜獎,主要是使用了隨機丟擲集合類的元素的特點。

zset型別

zset型別和set型別都是屬於集合型別,兩者不同點,在設定zset資料時要設定一個分數,這個分數可以用來做資料排序,並且zset型別的資料是有序的,因此zset也被叫做有序集合。

zset除了可以用在set可以用的場景下,更多的是可以用在排序的場景,如排行榜、延遲佇列,就像未支付的訂單在多少時間內就失效。

簽到排行榜。

127.0.0.1:6379> zadd goods:order 1610812987 1
(integer) 1
127.0.0.1:6379> zadd goods:order 1610812980 2
(integer) 1
127.0.0.1:6379> zadd goods:order 1610812950 3
(integer) 1
127.0.0.1:6379> zadd goods:order 1610814950 4
(integer) 1
127.0.0.1:6379> zcard goods:order
(integer) 4
127.0.0.1:6379> zrangebyscore goods:order 1610812950 1610812987
1) "3"
2) "2"
3) "1"
將使用者的簽到時間作為排行的分數,最後查詢指定範圍內簽到使用者的id。

Bitmaps型別

Bitmaps底層儲存的是一種二進位制格式的資料。在一些特定場景下,用該型別能夠極大的減少儲存空間,因為儲存的資料只能是0和1。為了便於理解,可以將這種資料格式理解為一個陣列的形式儲存。

利用該特點,可以將該型別用在一些訪問統計、簽到統計等場景。

某個使用者一個月的簽到記錄。

127.0.0.1:6379> setbit user:2020-01 0 1
(integer) 0
127.0.0.1:6379> setbit user:2020-01 1 1
(integer) 0
127.0.0.1:6379> setbit user:2020-01 2 1
(integer) 0
127.0.0.1:6379> bitcount user:2020-01 0 4
(integer) 3
統計出該使用者這個月只有4天簽到。

統計某一天網站的簽到數量。

127.0.0.1:6379> setbit site:2020-01-17 1 1
(integer) 0
127.0.0.1:6379> setbit site:2020-01-17 3 1
(integer) 0
127.0.0.1:6379> setbit site:2020-01-17 4 1
(integer) 0
127.0.0.1:6379> setbit site:2020-01-17 6 1
(integer) 0
127.0.0.1:6379> bitcount site:2020-01-17 0 100
(integer) 4
127.0.0.1:6379> getbit site:2020-01-17 5
(integer) 0
這裡將使用者的id作為偏移量,簽到就是1。可以統計出具體訪問的總數,同時可以根據某個使用者的id查詢是否在當前簽到。如果根據偏移量重複設定一個值,此時不會被重複新增,只是Redis會返回1表示當前已經存在。

計算某段時間內,都簽到的使用者數量。

127.0.0.1:6379> setbit site:2020-01-18 6 1
(integer) 0
127.0.0.1:6379> setbit site:2020-01-18 4 1
(integer) 0
127.0.0.1:6379> setbit site:2020-01-18 7 1
(integer) 0
127.0.0.1:6379> bitop and continue:site site:2020-01-18 site:2020-01-17
(integer) 1
使用該場景,是因為該資料型別可以計算出多個key的交集(and)。同時可以取並集(or),或(or),異或(xor)。

HypefLogLog型別

HypefLogLog型別從使用上來說,有點類似於集合型別。該型別實際是一種字串型別的資料結構。使用該型別最大的好處就是減少空間、但是也存在一定的誤差率。該型別也是不允許同一個key存在重複元素。該型別也支援合併多個key的值。

該資料型別一般用在一些不需要精確計算的統計類場景。

使用者簽到統計。

127.0.0.1:6379> pfadd 2020:01:sgin  1 2 3 4 5 6 7 8
(integer) 1
# 嘗試重複新增
127.0.0.1:6379> pfadd 2020:02:sgin  1 2 3 4 5 6 7 8
(integer) 0
127.0.0.1:6379> pfadd 2020:02:sgin  9
(integer) 1
127.0.0.1:6379> pfadd 2020:02:sgin  10
(integer) 1
127.0.0.1:6379> pfadd 2020:02:sgin  11
(integer) 1
127.0.0.1:6379> pfcount 2020:02:sgin
(integer) 11

GEO型別

GEO型別是一種儲存地理資訊的資料格式,基於該資料特點。可以用在一些距離計算、附近推薦等業務場景。

距離計算

127.0.0.1:6379> geoadd city:distance nx 121.32 42.36 beijing 121.20 38.56 
shanghai 100.36 38.56 sichuan
(integer) 3
127.0.0.1:6379> geopos city:distance beijing shanghai sichuan
1) 1) "121.32000178098678589"
   2) "42.36000020595371751"
2) 1) "121.19999974966049194"
   2) "38.55999947301710762"
3) 1) "100.3599974513053894"
   2) "38.55999947301710762"
# 計算出北京到上海的距離
127.0.0.1:6379> geodist city:distance beijing shanghai km
"422.7819"

Stream型別

Stream型別是Redis在5.0之後版本新增的一種資料結構。該資料結構主要使用者訊息佇列的場景。Redis 本身是有一個 Redis 釋出訂閱 (pub/sub) 來實現訊息佇列的功能,但它有個缺點就是訊息無法持久化,如果出現網路斷開、Redis 當機等,訊息就會被丟棄。

訊息佇列

# 新增訊息佇列
127.0.0.1:6379> xadd message * name zhangsan age 12
"1610873104343-0"
127.0.0.1:6379> xrange message - +
1) 1) "1610873104343-0"
   2) 1) "name"
      2) "zhangsan"
      3) "age"
      4) "12"
# 獲取訊息佇列
127.0.0.1:6379> xrevrange message + - count 1
1) 1) "1610873104343-0"
   2) 1) "name"
      2) "zhangsan"
      3) "age"
      4) "12"

相關文章