背景
前不久陪朋友参加了一场教师面试,进入面试现场时,每个人都要扫描二维码进行排队。当时扫码的人挺多的,不一会,服务器就卡主了,一堆人就开始焦急起来,因为提交报名的马上就要截止。好在过了几分钟,又可以进行排队了。等这件事平息之后,就在思考了一下,其实这次的报名的并发不是很高,并发量在100内,但是偏偏这么点并发就可以使得服务器卡主,这个教师报名排队系统的开发可能需要思考下了。
笔者不才,也曾开发过一个排队的功能。这个并发要远远高于前面的预约面试。这里阐述下我的实现思路,欢迎大家吐槽。
一、实现思路
1.1 数据表设计
首先,数据库设计,这里肯定有一个报名信息表,存储当前报名的开始时间、结束时间、报名标题等等。
报名信息表大概如下:
id | title | startTime | endTime |
---|---|---|---|
1 | 北京大学教师面试报名 | 2021/1/1 | 2021/1/2 |
2 | 北京大学附属小学招生 | 2021/1/1 | 2021/1/2 |
报名记录表大概如下:
id | user_mobile | order | createTime | 报名id |
---|---|---|---|---|
1 | 132{手机号信息} | 1 | 2021/1/1 | 1 |
2 | 152{手机号} | 2 | 2021/1/1 | 1 |
表设计很简单,报名记录表记录每一次报名的报名人和报名序号。一般这种不允许重复的数据,会在报名id
、user_mobile
、order
三个字段上加上唯一索引,保证数据绝对不重复。
1.2 实现思路
排序问题
比较棘手的是这个排序问题,现在一般的项目都是集群部署,在确认当前人的序号时,存在并发问题。也就是存在多个人都争夺一个序号的问题。采用redis的incr
命令可以更加友好的解决这一问题。
incr命令是原子增加1,并且将增加后的值返回。操作如下:
redis> SET page_view 20
OK
redis> INCR page_view
(integer) 21
通过这一命令在报名开始的时候设置值为0,然后每一个人报名时,都通过incr命令获取当前排名。
去重问题
一般情况下,每个手机号在同一个报名场景中只允许报名一次。因此去重也是一个比较麻烦的问题。如果每次都去查询数据库有没有报名记录的话,也是一个很造嫌弃的操作。
通过redis的bitmap
数据结构可以更简单高效的实现数据的去重。也许会有人使用set
或者hash
。虽然HashSet
和HashMap
也同样能实现用户的去重和统计,但是如果使用HashSet
和HashMap
存储的话,每一个数据比如用户ID都要存成int,占4字节即32bit。而一个用户ID在Bitmap
中只占一个bit,内存节省了32倍。
#设置值
setbit key offset value
#例如第一次教师报名中用户userId为123的用户已经报名
setbit baoming_key_1 123 1
# 获取值
# 可查看是否已经报名
getbit key offset
通过此数据结构,不光可以去重,也可以处理黑名单。通过如此思路,实现起报名排队的功能速度会快很多。
思路大概是这样,欢迎大家吐槽哈。