千万级学生管理系统考试存储系统方案
需求分析
假设每门学科每年 2 次考试,每个学生平均一学期 20 门课,考试采取机考的方式,每门考试的答案 20 判断题、20 选择题、4 道大题(答案 200 字以内),考试结果永久保存,在校学生能够看到自己曾经的考试结果,则考试结果记录的存储量为:
• 在校学生:1000 万 * 20(课)* 2(考试次数) * 1000(答案)* 2(学期) * 3(只有前三年考试)= 2.4T。
• 离校学生:每年 250 万,存储量为 0.6T。
假设学校的考试都安排在某一个月内,考试的时候请求试卷,提交答案,中间答题过程浏览器本地完成,由于考试集中在上午 4 小时和下午 4 小时,
且请求试卷集中在考试开始的前 1 分钟,提交答案集中在考试结束前的 30 分钟,因此估算如下:
• 请求试卷:1000 万 * 20(课)/ 20(周末不考试) / 4(每天 4 堂考试)/ 1 分钟 = 250 万请求/分钟 ≈ 5 万/每秒。
• 提交试卷: 1000 万 * 20(课)/ 20(周末不考试) / 4(每天 4 堂考试)/ 30 分钟 = 1700/每秒。
数据结构设计
数据结构分析
String :key :学校-院系-专业-课程-题型-题号,value:采用 String 数据结构
List:key :学校-院系-专业-课程-题型,单条查询及修改都不方便。
Map:key :学校-院系-专业-课程-题型,filed 为题目序号,value 为试卷内容
Set :不满足业务场景
Scored Set:key :学校-院系-专业-课程-题型,value:题目内容,可以以题目编号作为分值
数据结构选择
结论:选择 Scored Set。
分析:根据上面分析,使用 String 和 Scored Set 都是可以的,从存储空间占用和查询性能上来说,分散使用 String 存储,会占用更大的空间,查询性能对比与 Scored Set 肯定是会慢一点;而 ZSet 从业务上来说,调整顺序可以直接调整分数即可,比较简单,且从存储空间占用和查询性能上来说,也会更优。
读写流程
新增试题:教师新增一道题,使用根据其所在学校、院系、专业、课程、题型组装 Key,添加题目时,使用 ZADD key score member
删除试题:用 zremrangebyrank key index index 删除指定顺序的题目
修改试题:先删除试题,再插入这里也涉及两个操作,可以使用 Redis 事务或者使用 lua 脚本保证原子性。
调整试题顺序:确定好该条数据调整的位置后,先删除 scores 位置大的记录,修改 scores 小记录的 scores 指,再插入,这里也涉及多个操作,可以使用 Redis 事务或者使用 lua 脚本保证原子性。
学生读取具体题目:学生按照学校-院系-专业-课程-题型作为 Key,使用 zrange key 0 0 进行查询查询第一道题,后面的题,是顺序还是跳着做都可以,只要选择题号后,使用 zrange key index index 获取即可
Sentinel 集群选择
试卷请求 QPS 为 5 万/s,Redis 单机读取性能为 10 万,因此单机就可以满足读取性能要求,为了保证高可用和自动切换,使用 Sentinel,为了保证出现出从切换仍然是高可用,可以设计整个 Redis 集群为 3 台服务器,一主两从,同时为了保证 Sentinel 的高可用,也是用三台服务器组成 Sentinel 集群。
版权声明: 本文为 InfoQ 作者【踩着太阳看日出】的原创文章。
原文链接:【http://xie.infoq.cn/article/5ef6665bea9433b3e91125fc5】。未经作者许可,禁止转载。
评论