<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在自己的工作中,把其中一個PHP專案的快取從以前的APC快取逐漸切換到Redis中,並且根據Redis所支援的資料結構做了庫存維護功能。快取是在業務層做的,準確講應該是在MVC模型中Model的ORM裡面。主要邏輯就是先查快取,查不到的話再查資料庫。不過這些不是本文的主要內容,下面我把庫存管理功能的快取設計思路分享一下,希望能帶給大家一些收穫,有不足之處或者有更好方案的,也希望各位多多指教。
為了略去我們公司專案背景,我決定把這次的問題類比成一個考卷上的問題。至於業務細節,大家也無需關注~看題目就可以了:
假設你是某國最牛的收藏家,手裡有各種價值連成的寶物。知道有一天,你覺得做收藏太沒意思了,打算把這些寶物賣掉換點現金。
不過把這些值錢的寶貝放在菜市場上賣實在太low了。在“網際網路+”時代,我們當然要玩一些不一樣的賣法:在你名下有一棟300個房間的大樓(編號為001至300),每個房間放著一個密碼鎖保險箱,在下個月(12月1日至12月31日)的每一天,你都會挑選300件最好的“極品寶物”(也稱作A類寶物),分別放入這300個房間的保險箱裡,每天每個房間放什麼寶物已經定好了,所有想買寶物的人必須至少提前一天在網上預定,到時候憑藉預定碼自己開啟保險箱取貨。沒有被預定的寶物將會被你收回,不再售賣。
要做這樣一個網路預定系統,它的前端介面大概是這樣的:
上圖中三個要填的控制元件,單擊後可以出現選擇框。現在的問題是,一個房間只有一個寶物,不能被重複預定。所以當買家選擇了寶物型別和房間號之後,在選擇預定日期時,要在日期選擇框給使用者一個提示。比如12月3日051號房間已被預定,現在又有另一位使用者選擇了051號房間,那麼在彈出日期選擇框時,12月3日要置為不可選。如下圖(12月3日顯示為“缺”):
那麼,這樣一個簡單的庫存系統,如何在redis中儲存呢?
最粗暴的想法是,我們的庫存其實就是一個很大的三維陣列,第一維寶物型別,第二維房間號,第三維即預定日期。Redis支援5種儲存型別:String,Hash,List,Set,Sorted Set。目前的場景中Hash和Set型別都可以滿足要求,在此我們選擇使用Hash型別做儲存。
Redis的key設定為 寶物型別+房間號(例如 A:205,A代表極品寶物,205為房間號),Redis的value為hash型別,hash key為日期(例如 2016-12-05),hash value為true或false,表示已經被預定或沒有被預定。用圖表示為:
如果A類寶物158房間在12月8日已經被預定,則儲存為
Redis Key —— A:158
Redis Value —— hash table ['2016-12-08' => 1]
你所推出的A類極品寶物很受歡迎,剛推出去不久即被預定出去很多。然而,動輒數十萬元的價格也讓很多有收藏興趣、卻沒那麼富裕的中產階級望而卻步。於是,你又從自己的收藏中挑選出了比A類寶物稍次一些的B類寶物(也稱作“優質寶物”),價格更加親民。
由於B類寶物比A類寶物多一些,你打算換一種玩法,在這300個房間中,每個房間又放入了一個保險箱,這次,你每隔一個小時都會向300個房間的箱中各放入一件B類寶物,沒有被預定的寶物在這一個小時過後會被收回,換成下一個小時的寶物。買家預訂後,按照所預定的小時來取走寶物。對於B類寶物,你的預定系統會多了一個選項,即取貨時間。如下圖:
現在由於多了一個預定條件(取貨時間),那在做庫儲存存的時候,粗暴的方式想一下,庫存其實就是一個大的四維陣列。第一維寶物型別,第二維房間號,第三維預定日期,第四維取貨時間。在Redis中怎樣儲存這類寶物呢?
其實仔細想一下,在儲存A類極品寶物的時候,我們在Redis中的儲存是有浪費維度的情況的,
當時hashValue只存了一個true表示有預定,這個維度其實是被浪費掉了。考慮到取貨時間全是整點,一整天也就是0至1點,1至2點,……,23至24點共計24種情況,所以我們完全可以使用二進位制整數表示被預定的時間。例如1表示0至1點,2表示1至2點,4表示2至3點,……,
8388608 (= 2^23)表示23至24點。多個時間段被預定,只需要將數值取邏輯或操作即可。
這樣,我們的Redis結構變成了這樣子:
例如,B類寶物103房間,12月5日和6日的上午8點至12點被預定,在redis中儲存為
Redis Key —— B:103
Redis Value —— hash table ['2016-12-05' => 3840, '2016-12-06' => 3840]
對於B類寶物,在做新增預定時,需要注意先將原有的hash value取出,和新的預定取貨時間做邏輯或操作,然後再把結果寫回Redis中,而不能像A類寶物一樣直接呼叫hSet去設定hash value;取消預定時,要注意先將原有的hash value取出,把要取消的時間段從hash value中扣除掉(互斥或+邏輯與操作),然後重新將剩餘的已預訂取貨時間寫回Redis中,而不能直接呼叫hDel去刪除。
四、再次進階&庫存管理方案
自從推出了B類寶物之後,你的生意又比以往火爆了許多。於是新的需求又來了,現在有大量的遊客、學生黨等沒什麼豐厚積蓄的人表示對你的寶物非常感興趣,來這個城市旅遊的人都希望帶一些紀念品回去。然而,B類寶物的價格雖然比A類便宜一些,對於這些人來講還是有點貴。於是,你決定把自己餘量最多的實惠寶物(C類寶物)拿出來售賣。
這部分寶物數量是最多的,於是你在這300個房間中,每個房間新增了100個寶箱,專門用於存放C類寶物。這100個寶箱分別被編號為1號,2號,……,100號。同樣的,每天的每個小時,你都會向這300個房間中,每個房間的100個寶箱中分別放入一件C類寶物(也就意味著,整個大樓每小時C類寶物會更新30000件)。如果沒有人預定,則下一個小時寶物更換。終於,這下可以滿足所有人的需求了。
對於C類寶物,你的預定介面成了下面的樣子:
我們又多了一個預定條件。此時,又面臨著庫儲存存的問題。照例,這個庫存其實就是一個大的五維陣列,寶物型別、房間號、預定日期、取貨時間、寶箱編號各自佔有一個維度。不過前面我們的Redis各個維度基本上已經佔滿了,這次應該怎麼儲存呢?
這次的Redis庫儲存存必須要結合業務特點來了。首先,寶箱編號和取貨時間這兩個維度,能取的值範圍並不太多,寶箱編號只有100個,只要把hash value變成一個長度為100的陣列,陣列的每個位置都存有INT型別表示的取貨時間即可。然而hash value只能是string……於是乎,只好做一個陣列的序列化操作,讀取的時候再反序列化回來即可。好在長度只有100,序列化效率並不會成為系統的瓶頸。
例如,C類寶物,12月23日、24日,258房間,97和99號寶箱在11點至13點被預定,則儲存為:
Redis Key —— B:103
Redis Value —— hash table ['2016-12-05' => 3840, '2016-12-06' => 3840]
其中6144用二進位制表示為‘110000000000’,hash value為陣列序列化以後的字串,實際專案中可以使用json格式。好了,現在Redis對於三種寶物的儲存都有了。
對於C類寶物,在使用者取消預定、新增預定時,同樣不能簡單地呼叫hSet和hDel進行覆蓋設定和刪除,要取出已經預定的情況,與已經預定的取貨時間做位運算。
庫存理論上就是一個多維陣列,我們所做的主要工作就是怎樣把各個維度合理的儲存起來,並能夠方便地進行增加、刪除、查詢操作。從節約使用記憶體的角度講,在最開始還沒有任何人預定的時候,Redis整個可以是空的,對於A類寶物來說,hash value等於false和根本不存在對應的redis key或hash key是等效的。
另外,寶物型別和房間號合起來做redis key,會導致我們在redis中和寶物庫存相關的key的數量比較多,為了方便統一管理這些key,可以再增加一條redis快取,專門用來儲存和寶物庫存相關的所有redis key值,如下圖所示。需要注意的是,這次我們並不需要hash資料型別了,set型別就已經足夠,增刪改查複雜度都是O(1)。裡面儲存了所有redis中已經存在的庫存key值。
這麼做的一個好處是,萬一哪天碰到一些特殊情況,需要把所有庫存相關快取全部清空的話,我們可以很容易地取出所有的庫存key並做刪除操作。另外一個好處是,給我們提供了繼續擴充套件的思路……設想一下,現在最複雜的情況是C類寶物,一共5個維度。假設未來,你不再使用一幢樓的300個房間去售賣寶物,而是多幢樓,那麼使用者在下訂單的時候又要多出一個維度——樓棟編號。碰到這種情況,我們完全可以將這個多出來的庫存Key集合退化為樓棟編號來使用,保證了可能出現的更復雜情況下的擴充套件性。
在做了這次擴充套件之後,每次新增預定記錄時,需要注意檢測庫存key集合中是否已經存在對應的redis key值,如果不存在需要將redis key值加入庫存key集合中。刪除操作也類似。
上面使用了循序漸進的方法講述了一下問題,不過現實的場景中,這三種寶物型別在我們的業務中是同時存在的。上面的設計保持了三種寶物型別儲存上的統一性。如果只考慮A類寶物的話,庫存只有三個維度,其實完全不必使用hash資料型別來儲存,set型別就足夠了。
我們儲存這些預定情況的主要目的,就是為了方便快速地查到庫存衝突情況。比如有人已經定了12月3日,59號房間的A類寶物,那又有另外一個人想預定一樣的日期、房間的A類寶物時,通過記憶體中的庫存查詢,我們可以很方便地告訴客戶,該庫存已經被其他人搶先預定了。
以上就是我在業務中碰到的一個快取設計的小問題,不吝賜教!
以上就是Redis做預定庫存快取功能設計使用的詳細內容,更多關於Redis預定庫存快取設計的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45