架构实战营模块四作业
设计千万级学生管理系统的考试试卷存储方案
【作业要求】
基于模块 4 第 6 课的估算结果和 Redis sentinel 的初步方案设计,完善考试试卷存储方案,具体包括:
• 完善 Redis 的数据结构设计,明确具体使用哪种 Redis 数据结构。
• 设计具体的读写流程(可以文字描述也可以序列图描述,序列图要有文字辅助说明)。
• 对照模块 4 第 6 课的性能估算结果,计算 Redis sentinel 集群的服务器数量和性能。
性能估算
学生数量为 1000w,假设平均每所大学有 1.5w 人,每所大学有 100 个专业,每个专业 1 学期要修 20 门课,每门学科每年 2 次考试,考试采取机考的方式,每门考试包括 20 道判断题、20 道选择题、4 道大题,且只有前三年级会考试。据此我们可以计算出如下数据:
1 份试卷的大小。假设 1 道判断题 150 个字以内,1 道选择题 200 个字以内,4 道大题每题 400 字以内,则试卷大小为:(20*150 + 20*200 + 4*400) *3(1 个汉字 3 个字符) ≈ 25k
1 次考试总的试卷数。
(1000w/1.5w) *100 *20 *3(3 个年级) ≈ 400w
试卷总存储量。考试完成后,老师会对考试结果进行分析统计,所以我们需要保留试卷的内容。但这个分析统计的周期不会跨学生的年级,比如老师一般不会在学生升到大二的时候还在分析学生在大一期间的试卷信息,因此试卷内容最多保留一年即可。据此计算出:
1 次考试试卷存储量为:(1000w/1.5w) *100 *20 *3(3 个年级) *25k ≈ 100G
1 年内在校学生试卷总存储量为:1 次考试试卷存储量 *2(1 个学期两次考试) *2(1 年 2 个学期) ≈ 400G
存储试卷 TPS。假设学校的考试都安排在某一个月内,试卷都是由老师提前在题库生成,则试卷存储的 TPS 为:400w/20(周末不出题)/5(每天集中在 5 个小时内出题) /3600s ≈ 12,计算下来 TPS 较低,且生成试卷对 RT 的性能要求也不高(10s 内都可接受),因此试卷存储没有高性能要求。
请求试卷 QPS。假设学校的考试都安排在某一个月内,考试的时候请求试卷,提交答案,由于考试集中在上午 4 小时和下午 4 小时,且请求试卷集中在考试开始的前 1 分钟,因此请求试卷估算 QPS 为:
1000 万 * 20(课)/ 20(周末不考试) / 4(每天 4 堂考试)/ 1 分钟 = 250 万请求/分钟 ≈ 5w。
数据结构设计
使用 redis string 存储试卷内容
key 为试卷 ID。
value 存储采用 Snappy 压缩后的 JSON 字符串。如果不压缩的话,一份试卷 25k,每秒访问量 5w,那么每秒产生 1200MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
使用 redis sorted set 存储试卷基础信息
key 的格式为:学校院系编号-专业编号-年级编号。
成员信息包括:试卷 ID、课程编号、考试开始/结束时间(yyyyMMddHHmmss 格式)、备注。
成员 score:按照考试开始时间来分配 score 值。
读写分析
存储试卷
老师在题库生成试卷时,需要填写试卷的基本信息:院校、专业、年级、课程编号、考试开始/结束时间、备注。提交试卷时,生成两份数据,两份数据都存储成功后试卷才算保存成功:
试卷内容。使用 redis string 存储试卷内容。详细格式见上面数据结构设计部分。
试卷基本信息。使用 redis sorted set 存储试卷的基础信息。详细格式见上面数据结构设计部分。
请求试卷
学生请求试卷时,根据学生所在的“学校院系编号-专业编号-年级编号”作为 key,当前请求时间作为 score 值,使用 ZREVRANGEBYSCORE 命令获取小于该 score 的最大值对应的课程信息。并校验当前时间是否在该课程设置的考试开始/结束时间。如果是,根据课程的试卷 ID,读取试卷内容。
sentinel 集群配置
1 次考试试卷存储量,在未压缩的情况下为:(1000w/1.5w) *100 *20 *3(3 个年级) *25k ≈ 100G。采用 Snappy 压缩后≈35G。redis 集群采用 1 主 2 从 3 台机器,sentinel 采用 3 台机器做集群。
版权声明: 本文为 InfoQ 作者【spark99】的原创文章。
原文链接:【http://xie.infoq.cn/article/dd5dc223d88c91a95303b2582】。未经作者许可,禁止转载。
评论