我们都是架构师!
关注架构师(JiaGouX),添加“星标”
获取每天技术干货,一起成为牛逼架构师
技术群请加若飞:1321113940 进架构师群
投稿、合作、版权等邮箱:admin@137x.com
因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享
背景
CREATE TABLE `t_activity` (
`activeId` bigint(20) NOT NULL COMMENT '活动ID',
`title` varchar(256) NOT NULL COMMENT '活动名称',
`applyStartTime` timestamp NULL DEFAULT NULL COMMENT '报名开始时间',
`applyEndTime` timestamp NULL DEFAULT NULL COMMENT '报名停止时间',
`activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
`activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
`cityIds` varchar(256) NOT NULL COMMENT '覆盖城市,多个逗号分隔',
`couponType` tinyint(4) NOT NULL DEFAULT '0' COMMENT '优惠类型,1 直减;2 折扣;3免费住N天;4免押金;5特价房',
`lowerLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值下限',
`upperLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值上限',
`description` text COMMENT '活动描述',
`cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
`foreignId` bigint(20) NOT NULL DEFAULT '0' COMMENT '外部ID',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '活动状态',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`recordStatus` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据状态',
PRIMARY KEY (`activeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='活动信息表';
在商家端可以通过status字段和cityIds字段来进行可报名活动的展示。
C端读取的展示代码里可以使用设计模式中的代理模式来加一层缓存,在进行中的活动会将数据推入到cache层中。
通过crontab定时任务,每分钟进行时间段的检查,来更新对应的status字段,完成活动状态的流转。
可以使用设计模式中的代理模式来加一层缓存,非强实时的查询走cache,cache中不存在或需实时数据的再走db。
CREATE TABLE `t_couponmeta` (
`couponMetaId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '券id',
`appId` int(11) NOT NULL DEFAULT '1' COMMENT '区分建立来源',
`activeId` bigint(20) NOT NULL DEFAULT '0' COMMENT '活动ID',
`companyId` bigint(20) NOT NULL COMMENT '公司编号',
`cityId` int(11) NOT NULL COMMENT '城市id',
`companyName` varchar(255) DEFAULT NULL COMMENT '公司名称',
`companyShortName` varchar(255) DEFAULT NULL COMMENT '公司简称',
`couponType` tinyint(4) NOT NULL COMMENT '优惠券类型',
`title` varchar(256) NOT NULL COMMENT '优惠券名称',
`directDiscount` int(11) NOT NULL DEFAULT '0' COMMENT '直减券优惠力度',
`discount` int(11) NOT NULL DEFAULT '0' COMMENT '折扣力度',
`freeLive` int(11) NOT NULL DEFAULT '0' COMMENT '免费住n天券',
`threshold` varchar(256) NOT NULL COMMENT '使用门槛',
`deduction` tinyint(4) NOT NULL DEFAULT '1' COMMENT '抵扣说明 1首月抵扣,2 平摊到月',
`totalAmount` int(11) NOT NULL DEFAULT '0' COMMENT '券总数',
`applyAmount` int(11) NOT NULL DEFAULT '0' COMMENT '已领取总数',
`activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
`activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
`startTime` timestamp NULL DEFAULT NULL COMMENT '券使用开始时间',
`expireTime` timestamp NULL DEFAULT NULL COMMENT '券使用结束时间',
`status` int(11) NOT NULL DEFAULT '10' COMMENT '10:新建未启用,20:已启用,30:过期, 40 已结束 50 已中止',
`expireType` tinyint(4) NOT NULL DEFAULT '1' COMMENT '类型:1固定有效期类型,2浮动有效期类型',
`validPeriod` tinyint(4) NOT NULL DEFAULT '0' COMMENT '浮动有效期(单位:天)',
`tenantRange` tinyint(1) NOT NULL DEFAULT '1' COMMENT '租客范围枚举值',
`customScope` varchar(256) NOT NULL DEFAULT '' COMMENT '自定义租客范围',
`comment` varchar(50) DEFAULT NULL COMMENT '备注',
`cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`recordStatus` tinyint(4) DEFAULT '0' COMMENT '状态 0默认 -1删除',
PRIMARY KEY (`couponMetaId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券表';
优惠券的状态流转如图所示
将商家b端绑定了的优惠券全量(新建/启用中/未过期)数据存于MySQL数据表中,表结构比较简单,如下所示:
CREATE TABLE `t_bindcoupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`couponMetaId` int(11) NOT NULL COMMENT '券id',
`companyId` bigint(20) NOT NULL COMMENT '公司编号',
`activityStatus` tinyint(4) NOT NULL COMMENT '状态 0 准备中 1 活动中 2 活动结束 券未失效 3活动结束券失效',
`houseId` bigint(20) NOT NULL DEFAULT '0' COMMENT '房源id',
`recordStatus` tinyint(4) NOT NULL COMMENT '数据状态 0 有效,-1 失效',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_companyId_couponMetaId` (`companyId`,`couponMetaId`),
KEY `idx_planeId` (`planeId`),
KEY `idx_houseid_activitystatus` (`houseId`,`activityStatus`)
) ENGINE=InnoDB AUTO_INCREMENT=17379 DEFAULT CHARSET=utf8 COMMENT='优惠券绑定范围表'
操作房源绑定优惠券的时候,会使用分布式锁,避免并发绑定操作导致超限
由于C端浏览券详情&领券可能会是一个并发量较高的操作,所以尽可能都从缓存中读数据,包括以下数据:
活动信息
优惠券信息
优惠券库存
当活动开始前五分钟会禁止编辑活动信息和优惠券信息,活动开始时将上述数据推入缓存中,不设置过期时间,待活动状态转为结束时再清理数据。
同时在web服务集群中,对这1 2数据项做了一层短时间的本地缓存,减少请求redis集群的网络开销。
若有更好的方式,希望大神能够指点一下
以微服务形式,提供用户优惠券获取接口,以及状态变更接口给调用方使用,状态流转分为未使用、锁定、已使用三种状态。
当客户下了签约订单时,将对应使用的优惠券进行锁定。若订单完成则调用接口将状态流转为已使用,若订单取消则回滚优惠券的状态为未使用。
用户的优惠券数据分库分表,进行数据水平拆分,提升db读写能力。
redis集群以分片形式部署,提升可用性及容量。
集群拆分,每个集群仅处理部分优惠券请求,通过网关打散请求到不同的pod中。
引入jd-hotkey组件,热key实时同步到集群本地缓存中,减少访问分布式缓存。
引入canal组件,通过binlog同步db更新的信息,更新缓存并进行数据联动更新。
总结
至此,一个完整的优惠券系统就构建完毕了。
遵循读多写少用缓存,写多读少用队列的原则。
对于展现的活动数据,代码通过代理模式尽可能的通过缓存进行读取。使用了多级缓存的同时,为了避免产生缓存击穿的场景,对于活动中的数据都采用了主动推数据到redis中的方式。
对于活动->优惠券->房源的联动数据的写操作,代码通过状态模式+观察者模式实现,以及MQ控制并发量的异步更新。
对于库存扣减采用了redis,依托于它的原子性,但是redis不保证集群内部数据强一致性。为了避免超领带来的损失问题,在核销优惠券时进行了数量阈值校验。
对于热点的db库存更新则采用了db事务消息表,通过事务保证领取记录插入成功的同时一定会落入更新库存任务,从而异步串行的进行库存更新。
如喜欢本文,请点击右上角,把文章分享到朋友圈
如有想了解学习的技术点,请留言给若飞安排分享
·END·
相关阅读:
作者:盖茨狗
来源:juejin.cn/post/7160643319612047367
版权申明:内容来源网络,仅供分享学习,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!
我们都是架构师!
关注架构师(JiaGouX),添加“星标”
获取每天技术干货,一起成为牛逼架构师
技术群请加若飞:1321113940 进架构师群
投稿、合作、版权等邮箱:admin@137x.com