阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

使用Redis做预定库存缓存功能

364次阅读
没有评论

共计 4991 个字符,预计需要花费 13 分钟才能阅读完成。

最近在自己的工作中,把其中一个 PHP 项目的缓存从以前的 APC 缓存逐渐切换到 Redis 中,并且根据 Redis 所支持的数据结构做了库存维护功能。缓存是在业务层做的,准确讲应该是在 MVC 模型中 Model 的 ORM 里面。主要逻辑就是先查缓存,查不到的话再查数据库。不过这些不是本文的主要内容,下面我把库存管理功能的缓存设计思路分享一下,希望能带给大家一些收获,有不足之处或者有更好方案的,也希望各位多多指教。

一、业务背景

为了略去我们公司项目背景,我决定把这次的问题类比成一个考卷上的问题。至于业务细节,大家也无需关注~ 看题目就可以了:

假设你是某国最牛的收藏家,手里有各种价值连成的宝物。知道有一天,你觉得做收藏太没意思了,打算把这些宝物卖掉换点现金。

不过把这些值钱的宝贝放在菜市场上卖实在太 low 了。在“互联网 +”时代,我们当然要玩一些不一样的卖法:在你名下有一栋 300 个房间的大楼(编号为 001 至 300),每个房间放着一个密码锁保险箱,在下个月(12 月 1 日至 12 月 31 日)的每一天,你都会挑选 300 件最好的“极品宝物”(也称作 A 类宝物),分别放入这 300 个房间的保险箱里,每天每个房间放什么宝物已经定好了,所有想买宝物的人必须至少提前一天在网上预定,到时候凭借预定码自己打开保险箱取货。没有被预定的宝物将会被你收回,不再售卖。

要做这样一个网络预定系统,它的前端界面大概是这样的:

使用 Redis 做预定库存缓存功能

上图中三个要填的控件,单击后可以出现选择框。现在的问题是,一个房间只有一个宝物,不能被重复预定。所以当买家选择了宝物类型和房间号之后,在选择预定日期时,要在日期选择框给用户一个提示。比如 12 月 3 日 051 号房间已被预定,现在又有另一位用户选择了 051 号房间,那么在弹出日期选择框时,12 月 3 日要置为不可选。如下图(12 月 3 日显示为“缺”):

使用 Redis 做预定库存缓存功能

那么,这样一个简单的库存系统,如何在 redis 中存储呢?

二、库存管理方案(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,表示已经被预定或没有被预定。用图表示为:

使用 Redis 做预定库存缓存功能

如果 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 做预定库存缓存功能

现在由于多了一个预定条件(取货时间),那在做库存存储的时候,粗暴的方式想一下,库存其实就是一个大的四维数组。第一维宝物类型,第二维房间号,第三维预定日期,第四维取货时间。在 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 结构变成了这样子:

使用 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 各个维度基本上已经占满了,这次应该怎么存储呢?

这次的 Redis 库存存储必须要结合业务特点来了。首先,宝箱编号和取货时间这两个维度,能取的值范围并不太多,宝箱编号只有 100 个,只要把 hash value 变成一个长度为 100 的数组,数组的每个位置都存有 INT 类型表示的取货时间即可。然而 hash value 只能是 string……于是乎,只好做一个数组的序列化操作,读取的时候再反序列化回来即可。好在长度只有 100,序列化效率并不会成为系统的瓶颈。

例如,C 类宝物,12 月 23 日、24 日,258 房间,97 和 99 号宝箱在 11 点至 13 点被预定,则存储为:

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 值,如下图所示。需要注意的是,这次我们并不需要 hash 数据类型了,set 类型就已经足够,增删改查复杂度都是 O(1)。里面存储了所有 redis 中已经存在的库存 key 值。

使用 Redis 做预定库存缓存功能

这么做的一个好处是,万一哪天碰到一些特殊情况,需要把所有库存相关缓存全部清空的话,我们可以很容易地取出所有的库存 key 并做删除操作。另外一个好处是,给我们提供了继续扩展的思路……设想一下,现在最复杂的情况是 C 类宝物,一共 5 个维度。假设未来,你不再使用一幢楼的 300 个房间去售卖宝物,而是多幢楼,那么用户在下订单的时候又要多出一个维度——楼栋编号。碰到这种情况,我们完全可以将这个多出来的库存 Key 集合退化为楼栋编号来使用,保证了可能出现的更复杂情况下的扩展性。

在做了这次扩展之后,每次新增预定记录时,需要注意检测库存 key 集合中是否已经存在对应的 redis key 值,如果不存在需要将 redis key 值加入库存 key 集合中。删除操作也类似。

六、总结

上面使用了循序渐进的方法讲述了一下问题,不过现实的场景中,这三种宝物类型在我们的业务中是同时存在的。上面的设计保持了三种宝物类型存储上的统一性。如果只考虑 A 类宝物的话,库存只有三个维度,其实完全不必使用 hash 数据类型来存储,set 类型就足够了。

我们存储这些预定情况的主要目的,就是为了方便快速地查到库存冲突情况。比如有人已经定了 12 月 3 日,59 号房间的 A 类宝物,那又有另外一个人想预定一样的日期、房间的 A 类宝物时,通过内存中的库存查询��我们可以很方便地告诉客户,该库存已经被其他人抢先预定了。

以上就是我在业务中碰到的一个缓存设计的小问题,不吝赐教!

下面关于 Redis 的文章您也可能喜欢,不妨参考下:

Ubuntu 14.04 下 Redis 安装及简单测试 http://www.linuxidc.com/Linux/2014-05/101544.htm
Redis 主从复制基本配置 https://www.linuxidc.com/Linux/2015-03/115610.htm
Redis 集群搭建与简单使用  https://www.linuxidc.com/Linux/2017-03/142210.htm
CentOS 7 下 Redis 的安装与配置 https://www.linuxidc.com/Linux/2017-02/140363.htm
Ubuntu 14.04 安装 Redis 与简单配置 https://www.linuxidc.com/Linux/2017-01/139075.htm
Ubuntu 16.04 环境中安装 PHP7.0 Redis 扩展 https://www.linuxidc.com/Linux/2016-09/135631.htm
Redis 单机 & 集群离线安装部署 https://www.linuxidc.com/Linux/2017-03/141403.htm
CentOS 7.0 安装 Redis 3.2.1 详细过程和使用常见问题 https://www.linuxidc.com/Linux/2016-09/135071.htm
Ubuntu 16.04 环境中安装 PHP7.0 Redis 扩展 https://www.linuxidc.com/Linux/2016-09/135631.htm
Ubuntu 15.10 下 Redis 集群部署文档 https://www.linuxidc.com/Linux/2016-06/132340.htm
Redis 实战 中文 PDF https://www.linuxidc.com/Linux/2016-04/129932.htm
Redis 热迁移实战总结  https://www.linuxidc.com/Linux/2017-02/141083.htm
Redis3.0 配置文件详解  https://www.linuxidc.com/Linux/2017-03/141369.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计4991字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
8004139
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
终于收到了以女儿为原型打印的3D玩偶了

终于收到了以女儿为原型打印的3D玩偶了

终于收到了以女儿为原型打印的 3D 玩偶了 前些日子参加某网站活动,获得一次实物 3D 打印的机会,于是从众多...
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛 NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手! 作为动漫爱好者,你是否还在为...
免费领取huggingface的2核16G云服务器,超简单教程

免费领取huggingface的2核16G云服务器,超简单教程

免费领取 huggingface 的 2 核 16G 云服务器,超简单教程 前言 HuggingFace.co...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的 Nano Banana Pro?附赠邪修的用法 前言 大家好,我是星哥,今天来介绍谷歌的 ...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛 NAS 硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话? 前言 在选择 NAS 用预...
告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

  告别 Notion 焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁” 引言 在数字笔记工...
你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你 为什么要用宝塔跑分? 宝塔跑分其实就是对 CPU、内存、磁盘、IO 做...
还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手! 前言 对于个人开发者、建站新手或是想搭建测试站点的从业者...
小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比

小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比

小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比 星哥玩云,带你从小白到上云高手。今天咱们就来聊聊——什...