hello你好我是辰兮 ,很早前就要整理這篇文章了,本篇是關於Redis在電影購票方面的設計與實現,設計的業務的是如何利用Redis儲存對應電影排片的座位,Redis事務,樂觀鎖等後期會繼續補充和整理,分享獲取新知,大家一起進步 !
本篇來講關於Redis在電影購票系統方面的應用,主要提供Redis在設計鍵值方面的簡單思路!
因為購票的業務是屬於一個高並行的情況,而Redis作為一個優秀的快取資料庫在很大程度上可以幫助我們緩解購票壓力,包括Redis本身的一些特性可以幫助我們更好的實現我們購票業務,包括樂觀鎖,Redis事務等等…
場景描述:
1、首先電影院會有很多電影,比如《哪吒》,《我和我的家鄉》等
2、對於同一家電影院它同一部電影可以有很多的排片,即對應的影廳不一樣,如情侶廳,豪華廳,VIP廳等,自然而言對應的座位也不一樣
3、每一個影廳對應座位按道理後臺是可以人為設定的,即設定幾行幾列,哪裡有間隔
如特殊情況:疫情期間可以間隔排布影廳座位,影廳座位不一樣
一定要能理解電影座位就是根據電影院的實際情況後臺設計的
如上圖和下圖看的的就是不同的影廳
我們需要知道的兩個業務邏輯,第一個是每個電影院的每個電影廳都有自己的不同名字,第二個是每個電影院的座位排布不一樣,座位數目以及座位的排布可以要根據我們的實際情況,我做的是一個滿足任意電影院的購票排片系統,所以我們可以根據自己的喜歡決定
如下是一個簡單的草圖,我可以在如下的方格中設定電影廳名稱,電影廳型別,以及座位排布情況,我這樣設定的就是5*5的一個座位,後面前端顯示出來就是這樣一個座位表!
這裡我們的業務邏輯是將座位的資料會以Json字串的形式存入資料庫。這樣沒有我們設計的座位都有一個單獨對應的儲存Id
這裡是整個業務的核心,我們對電影院進行合理的排片,本頁面包涵兩個查詢功能,和一個插入功能,如查詢影片,查詢電影廳,點選確認排片後
我們會把座位先從資料庫中取出來,然後從新以Json字串的形式存入redis中,設定過期時間,過期時間為電影開場前的五分鐘,這樣我們滿足了電影購票的基本邏輯(開場前的五分鐘無法購票)。
當我們開啟使用者前端頁面顯示下圖
技術思路:Redis電影座位的設計
我們的設計如下的儲存方式保證了每一部電影排片都對應唯一的id以及他的行和列都是對應的,(scheduleId)代表電影排片的唯一id。
「redis-miaoyan-」+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId()
//電影座位儲存在redis中的鍵值設計
//"redis-miaoyan-"是用來區分,實際情況可以再短一點
//scheduleId排片表id
//sd.getRowId()+"-"+sd.getColumnId() 對應座位的行和列 用「-」隔開
"redis-miaoyan-"+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId()**
會有一個排片表記錄排片的電影,scheduleId就是記錄這個排片電影的唯一id,我們用這個id來設計鍵值就可以更好的區分哪一場電影對應座位
技術思路:如何利用redis設計過期時間?
用redis設定過期時間要符合業務邏輯,diff就是我們設定的過期時間,過期時間為我們目前排片時候的時間減去電影開場的時間,這樣電影開場就無法購票了,一般提前五分鐘,所以提前五分鐘清空redis。
redisTemplate.expire(「redis-miaoyan-」+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId(),diff, TimeUnit.MINUTES);
//用expire設定鍵值的過期時間,這樣過期後就可以釋放redis相關儲存資源
redisTemplate.expire("redis-miaoyan-"+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId(),diff, TimeUnit.MINUTES);
剛剛後臺設計的是5*5的座位,如圖我們可以看到就是這些座位,對應的座位選上後,右側出現幾排幾列,如1排6座/1排10座/5排6座/5排10座
ps:這裡可以給前端加一個業務邏輯就是隻能提示買四個座位,防止惡意購票。
如何利用好redis設計並取座位
我們的座位是管理員從後臺存入的到redis的,這裡取出來座位的核心程式碼如下
//從redis中查詢對應排片Id(場次)所對應的座位資訊
public List<Map<String, Object>> seatList(Integer scheduleId){
Set<String> keyset = redisTemplate.keys("redis-miaoyan-"+scheduleId+"-*");
List<SeatDesc> list = new ArrayList<>();
for(String key : keyset){
String s = redisTemplate.boundValueOps(key).get();
list.add(new Gson().fromJson(s, SeatDesc.class));
}
List<Map<String, Object>> list3 = SeatServiceImpl.seatUtil(list);
return list3;
}
回頭覆盤一下,每一部電影都有對應的座位和對應的廳那我們如何設計資料呢?
可以簡單的介紹的每一次後臺設定排片這個影片就有一個對應的排片ID即(Schedule ID),我們把所有和他相關座位以如下的形式存入資料庫"redis-miaoyan-"+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId()
//具體排片對應的排片id +對應的行+對應的列
"redis-miaoyan-"+scheduleId+"-"+sd.getRowId()+"-"+sd.getColumnId()
我再以如下方式取出來
Set keyset = redisTemplate.keys(「redis-miaoyan-」+scheduleId+"-*");
這樣就可以保證我每次取的座位都是當前電影排片的設定的座位
//取出來就取出來的是所有該scheduleId排片id下的redis鍵值 這樣對應的座位就全部取出來額放到一個集合中
Set<String> keyset = redisTemplate.keys("redis-miaoyan-"+scheduleId+"-*");
在Redis中利用 「-*」 是可以去除所有該鍵值的
Redis的樂觀鎖設定
當我們點選購票後我們的redis的座位會被鎖定,這樣的好處,避免兩個人購買相同的座位。每一次購票到完成就相當於一次Redis的事務,我們可以思考這次購票要麼成功,要麼失敗,不然就會導致兩個人購買同一張票的情況。具體的操作如下,即確保了不能重複購買電影票。使用者在選座的時候執行multi這個方法。
業務邏輯補充講解
這裡是我們點選購票後所進入的頁面,我們可以對比上述的購票座位,檢查發現是一致的,我們購票後通過redis鎖定座位,和電影院的業務邏輯一樣,我們設計了十五分鐘的過期時間,即如果你十五分鐘內未進行支付,則會出現訂單支付失敗,請重新購票。
這裡前端和後臺都要做定時器,Redis選座超過十五分鐘要把這個座位釋放掉,前端要提示使用者在規定時間內完成支付。
這樣的好處是為了防止惡意佔座的情況。
自己整理了很久的一篇文章,如有描述不當的地方懇請大佬們指出!
The best investment is to invest in yourself.
2020.10.25 願你們奔赴在自己的熱愛裡!