首頁  >  文章  >  資料庫  >  怎麼用Redis做預定庫存快取功能

怎麼用Redis做預定庫存快取功能

PHPz
PHPz轉載
2023-05-28 10:12:231424瀏覽

一、業務背景

我決定將本次問題比喻為考卷上的問題,以避免介紹我們公司專案的背景。至於業務細節,大家也不需要關注~看題目就可以了:

假設你是某國最牛的收藏家,手裡有各種價值連成的寶物。有一天你可能會感到收藏變得無聊了,於是決定出售這些珍貴物品以獲取現金。

不過把這些值錢的寶貝放在菜市場上賣實在太low了。在「網路」時代,我們當然要玩一些不一樣的賣法:在你名下有一棟300個房間的大樓(編號為001至300),每個房間放著一個密碼鎖保險箱,在下個月( 12月1日至12月31日)的每一天,你都會挑選300件最好的「極品寶物」(也稱為A類寶物),分別放入這300個房間的保險箱裡,每天每個房間放什麼寶物已經定好了,所有想買寶物的人必須至少提前一天在網上預定,到時候憑藉預定碼自己打開保險箱取貨。沒有被預定的寶物將會被你收回,不再販賣。

要做這樣一個網路預定係統,它的前端介面大概是這樣的:

怎麼用Redis做預定庫存快取功能

#上圖中三個要填的控件,點擊後可以出現選擇框。現在的問題是,一個房間只有一個寶物,不能被重複預定。當買家選定寶物類型和房間號碼後,當他們選擇預定日期時,建議在日期選擇框中提供一些提示資訊。例如12月3日051號房間已被預定,現在又有另一位用戶選擇了051號房間,那麼在彈出日期選擇框時,12月3日要置為不可選。如下圖(12月3日顯示為「缺」):

怎麼用Redis做預定庫存快取功能

那麼,這樣一個簡單的庫存系統,如何在redis中儲存呢?

二、庫存管理方案(Redis)

我們最初的構想是,我們的存貨可以被視為一個巨大的三維數組,其中第一個維度表示寶物類型,第二維表示房間號,第三維表示預定日期。 Redis提供了五種儲存類型,分別為:String、Hash、List、Set、Sorted Set。我們可以在目前場景下使用Hash類型來儲存數據,因為它能夠滿足我們的需求,同時Set類型也是一個可行的選項。

Redis的key設定為 寶物類型房間號碼(例如 A:205,A代表極品寶物,205為房間號碼),Redis的value為hash類型,hash key為日期(例如 2016-12-05 ),hash value為true或false,表示已經被預定或沒有被預定。用圖表示為:

怎麼用Redis做預定庫存快取功能

如果A類寶物158房間在12月8日已經被預定,則儲存為

1

2

3

#Redis Key —— A:158

Redis Value —— hash table ['2016-12-08' #=> 1]

三、進階場景&庫存管理方案

A類頂級寶物的推出受到了熱烈歡迎,僅推出不久便已被訂購數不少。許多中產階級對收藏感興趣,但高昂的價格常常令他們望而卻步。於是,你從自己的珍藏中選擇了B類寶物,它比A類寶物稍遜一些,但價格更為合理,也被稱為「優良寶物」。

由於B類寶物比A類寶物多一些,你打算換一種玩法,在這300個房間中,每個房間又放入了一個保險箱,這次,你每隔一個小時都會將300個房間的箱子各放入一件B類寶物,沒有被預定的寶物在這一小時過後會被收回,換成下一個小時的寶物。買家預訂後,請按照預定的小時取走寶物。對於B類寶物,你的預定係統會多了一個選項,也就是取貨時間。如下圖:

怎麼用Redis做預定庫存快取功能

現在由於多了一個預定條件(取貨時間),那在做庫存存放的時候,粗暴的方式想一下,庫存其實就是一個大的四維數組。這句話可以改寫為:四維資訊包括寶物類型、房間號碼、預定日期和取貨時間。在Redis中怎麼儲存這類寶物呢?

其實仔細想一下,在儲存A類極品寶物的時候,我們在Redis中的存儲是有浪費維度的​​情況的,

實際上,當時只使用了一個hashValue存儲了預定的狀態,導致該維度的資訊被浪費了。考慮到取貨時間全是整點,一整天也就是0至1點,1至2點,……,23至24點共計24種情況,所以我們完全可以使用二進制整數表示被預定的時間。例如1表示0至1點,2表示1至2點,4表示2至3點,……,

23至24點可以用2的23次方(8388608)來表示。多個時間段被預定,只需要將數值取邏輯或操作即可。

這樣,我們的Redis結構變成了這樣子:

怎麼用Redis做預定庫存快取功能

#例如,B類寶物103房間,12月5日和6日的上午8點至12點被預定,在redis中儲存為

##

對於B類寶物,在做新增預定時,需要注意先將原有的hash value取出,和新的預定取貨時間做邏輯或操作,然後再把結果寫回Redis中,而不能像A類寶物一樣直接調用hSet去設定hash value;取消預定時,要注意先將原有的hash value取出,將要取消的時間段從hash value中扣除(異或邏輯與操作),然後重新將剩餘的已預訂取貨時間寫回Redis中,而不能直接呼叫hDel去刪除。

四、再進階&庫存管理方案

自從推出了B類寶物之後,你的生意又比以往火爆了許多。於是新的需求又來了,現在有大量的遊客、學生黨等沒什麼豐厚積蓄的人表示對你的寶物非常感興趣,來這個城市旅遊的人都希望帶一些紀念品回去。儘管B類寶物價格略低於A類,但對這些人來說仍有些昂貴。於是,你決定把自己餘量最多的實惠寶物(C類寶物)拿出來販售。

在這300個房間中,C類寶物儲存數量最多,因此你在每個房間增加了100個專門用於儲存C類寶物的寶箱。這100個寶箱分別被編號為1號,2號,……,100號。同樣的,每天的每個小時,你都會向這300個房間中,每個房間的100個寶箱中分別放入一件C類寶物(也就意味著,整個大樓每小時C類寶物會更新30000件)。如果沒有人預定,則下一個小時寶物更換。終於,這下可以滿足所有人的需求了。

對於C類寶物,你的預定介面成了下面的樣子:

怎麼用Redis做預定庫存快取功能

#我們又多了一個預定條件。此時,又面臨庫存儲存的問題。照例,這個庫存其實就是一個大的五維數組,寶物類型、房間號碼、預定日期、取貨時間、寶箱編號各自佔有一個維度。前面我們已經用掉了Redis的各個容量,現在要儲存資料該怎麼辦?

這次的Redis庫存儲存必須要結合業務特點來了。首先,寶箱編號和取貨時間這兩個維度,能取的值範圍並不太多,寶箱編號只有100個,只要把hash value變成一個長度為100的數組,數組的每個位置都存有INT類型表示的取貨時間即可。然而hash value只能是string……於是乎,只好做一個陣列的序列化操作,讀取的時候再反序列化回來即可。還好長度只有100,序列化效率不會成為系統的瓶頸。

儲存方式為:12月23日、24日在258房間的C類寶物中,編號為97和99的寶箱在上午11點至下午1點期間已被預定

#1

2

3

Redis Key  —— B:103

 

##Redis Value —— hash table [ '2016-12-05' => 3840, '2016-12-06' => 3840]

#1

2

3

#Redis Key —— C :258

 

Redis Value —— hash table [#'2016-12-23' =&gt ; '[97 => 6144, 99 => 6144]''2016-12-24' =&gt ; '[97 => 6144, 99 => 6144]' ]

其中6144用二進位表示為‘110000000000’,hash value為數組序列化以後的字串,實際項目中可以使用json格式。好了,現在Redis對於三種寶物的儲存都有了。

怎麼用Redis做預定庫存快取功能

對於C類寶物,當使用者取消預定、新增預定時,同樣不能簡單地呼叫hSet和hDel進行覆蓋設定和刪除,要取出已經預定的情況,與已經預定的取貨時間做位運算。

五、儲存最佳化

庫存理論上就是一個多維數組,我們所做的主要工作就是怎樣把各個維度合理的儲存起來,並且能夠方便地進行增加、刪除、查詢操作。從節省使用記憶體的角度來講,在最開始還沒有任何人預定的時候,Redis整個可以是空的,對於A類寶物來說,hash value等於false和根本不存在對應的redis key或hash key是等效的。

另外,寶物類型和房號合起來做redis key,會導致我們在redis中和寶物庫存相關的key的數量比較多​​,為了方便統一管理這些key,可以再增加一條redis緩存,專門用來儲存和寶物庫存相關的所有redis key值,如下圖所示。需要注意的是,在這種情況下,使用set資料類型就可以滿足要求了,而不必使用hash資料類型,因為set資料類型的增刪改查複雜度都為O(1)。裡面儲存了所有redis中已經存在的庫存key值。

怎麼用Redis做預定庫存快取功能

這麼做的一個好處是,萬一哪天碰到一些特殊情況,需要把所有庫存相關緩存全部清空的話,我們可以很容易地取出所有的庫存key並做刪除操作。另外一個好處是,給我們提供了繼續擴展的思路……設想一下,現在最複雜的情況是C類寶物,一共5個維度。假設未來,你不再使用一棟樓的300個房間去販賣寶物,而是多幢樓,那麼用戶在下訂單的時候又要多出一個維度——樓樓編號。碰到這種情況,我們完全可以將這個多出來的庫存Key集合退化為樓棟編號來使用,保證了可能出現的更複雜情況下的擴展性。

在做了這次擴充之後,每次新增預定記錄時,需要注意偵測庫存key集合中是否已經存在對應的redis key值,如果不存在需要將redis key值加入庫存key集合中。刪除操作也類似。

以上是怎麼用Redis做預定庫存快取功能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除