首页 > 数据库 > Redis > 正文

怎么用Redis做预定库存缓存功能

PHPz
发布: 2023-05-28 10:12:23
转载
1422 人浏览过

一、业务背景

我决定将本次问题比作考卷上的问题,以避免介绍我们公司项目的背景。至于业务细节,大家也无需关注~看题目就可以了:

假设你是某国最牛的收藏家,手里有各种价值连成的宝物。有一天你可能会感到收藏变得无聊了,于是决定出售这些珍贵物品以获取现金。

不过把这些值钱的宝贝放在菜市场上卖实在太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]

123
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中存储为

1

2

3

Redis Key  —— B:103

 

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

123
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个房间中,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 —— C:258

 

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

123
Redis Key —— C:258 Redis Value —— hash table ['2016-12-23' => '[97 => 6144, 99 => 6144]''2016-12-24' => '[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
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!