该项目的主要功能特点是类似于一个秒杀系统,存在短时间高并发问题,在拿到项目需求后,我们对该项目进行了两版程序设计,初始版本中,在高并发的情况下,无法保持数据的正确性,存在可能一个用户被抽中多次的问题,以及对数据库频繁的写操作会降低程序运行效率。
在第二版中,我们着重对两点问题进行了优化,摒弃了直接查询、更新数据库的思路,转用了Redis进行缓存处理,很好的解决了第一版中的两大痛点。
下面将对该项目程序设计的整体思路进行阐述。
项目需求
该项目是一个和微信小程序结合的抽奖活动,主要的业务需求如下:
1. 普通用户部分:
- 手机扫二维码或者点击链接进入小程序
- 点击“点我送礼”按钮,随机匹配送礼用户
- 获得匹配结果,将礼物送给匹配用户
2. 管理员部分:
- 上传用户数据
- 进行数据初始化操作
- 查看抽奖结果
初始数据库设计
根据项目需求,首先进行了初始版本的数据库设计,除了基本数据字段,我们在这里另外添加了两个标志字段user_in和user_out,分别代表该用户收到礼物对应ERP账号和送出礼物对应的ERP账号,用来记录当前用户收到了谁的礼物和将礼物送给了谁。
表结构如下图所示:
初始流程
在初始版本中,用户和管理员的主要程序流程如下图所示。
在用户流程中,从前端传进来的数据并不是用户手机号,而是jsCode,vi,和croptedData这三个参数,在Controller层接收以后调用微信小程序接口根据jsCode获取SessionKey,再进行解密操作取得用户手机号,传入业务层。
在管理员流程部分,目前只支持批量导入,并且使用事务处理写入数据库的过程,要么全部成功,要么全部失败。
初始版本存在的问题
在初始版本设计中,用户每进行一次抽奖活动,后端就需要对数据库进行读写操作,存在严重的并发问题:
- 每进行一次抽奖活动,都要从数据库中查询数据,效率低下
- 存在并发问题:从数据库中产生的未中奖用户列表无法与数据库数据保持一致,有可能导致一个用户中奖多次
- 更新数据库操作要进行加锁处理,降低效率
优化思路
针对以上问题,我们在第二版中,针对抽奖算法做出了调整,摒弃了直接从数据库查询、更新数据的想法,改用Redis进行数据缓存,利用Redis中的List结构存储未中奖用户,用户进行抽奖活动时,从list中pop出中奖用户返回给前端,从而很好地解决了并发问题。而且整个流程只有在给前端返回中奖用户信息时才会对数据库进行读操作,不需要加锁,不会产生脏读问题,保证了数据安全。
主要优化思路如下:
- 管理员端只导入基本数据,取消user_in和user_out字段
- 将用户手机号初始化两份到JimDB中,在初始化时进行无序存储
- List:存放未中奖用户手机号
- Hash:存放用户手机号<当前用户手机号,中奖用户手机号>
- 从List中lpop出手机号作为中奖用户,更新Hash中当前用户value为中奖用户手机号
- 返回中奖用户信息,在管理员端可以进行结果查询
初始化流程
初始化流程如图所示:
抽奖流程
抽奖流程如图所示:
不足之处
在正式上线使用后,也暴露出很多不足之处,目前该项目还未实现的功能模块如下:
- 查看结果模块的完全实现(目前只能查看Hash中的数据,没用过滤分析功能和对数据的二次加工处理)
- 单独修改用户信息(目前只支持批量上传,无法修改)
- 锁定未抽奖用户进行提示(目前没有这个功能的实现