Bootstrap

架构训练营 week4 作业

1. 业务背景

千万级的学生管理系统需要有相应的试卷存储和读取的机制,有以下业务要求:每门学科每年2次考试,每个学生平均一学期20门课,考试采取机考的方式,每门考试的答案20判断题、20选择题、4道大题(答案200字以内),考试结果永久保存,在校学生能够看到自己曾经的考试结果。

学校的考试都安排在某一个月内,考试的时候请求试卷,提交答案,中间答题过程浏览器本地完成,由于考试集中在上午4小时和下午4小时,且请求试卷集中在考试开始的前1分钟,提交答案集中在考试结束前的30分钟。

2. 约束和限制

3. 总体架构

3.1 架构分析

性能预估:

根据背景,请求试卷和提交试卷的性能预估为:

请求试卷:1000万*20(课)/20(天)/4(次)/1分钟=250万请求/分钟 ,也就是5万/秒

因此请求试卷的性能复杂度较高。

题型一共3类:

假设:

判断题,一题20字,则一共400字

选择题,一题50字,每个选项20字,20题一共2600字

大题,一题100字,答案200字,则一共1200字

现在服务器一般是64位,所以一个汉字会用4B存储,所以 (400+2600+1200)*4 = 16800,约20K

所以一份试卷的存储空间需要20KB

假设一个班级40人,一个年级10个班级,那么每个年级400人,总学生数1000万,也就表示每个年级约330万人,330万/400 = 8250所学校。

每个学校每个年级的学生在同一时间都会考某一门考试的试卷。

假设试卷都不一样,则同时最多会有8250 * 3 *20 = 495000 份试卷同时被读取。每份试卷20KB,则会有 495000*20 = 9900000KB 约 9G的数据被读取到内存里。这个内存还是可以被一些比较普通的服务器所接受的。

而说到试卷的存储,需要的空间约为:8250*20(门课)*3(个年级)*2(次考试/年)*2(期中期末2次考试)*20KB = 37.6G,也就是将近40G的磁盘空间来存储所有学校3个年级的试卷,3个年级之外的试卷,则最好用其他的方式来存储。

因此所需的服务器为:8核16G的服务器,磁盘空间为80G(冗余一些的磁盘空间)。

这样的服务器3台。

高可用:使用了Redis sentinel对于整个redis的集群进行了高可用的保障,任何一个redis的实例出现写入或者读取问题的时候,sentinel就会重新确定master,并且尝试恢复不可服务的实体,在重启后将它改为slave。

高性能:单个redis在压测过程中,完全可以支持5万/s的并发量,所以三个redis组成的集群,更加可以应付这样的读取压力。而试题的写入是一个并发量极低的过程,所以无需考虑写入的性能问题。

数据永久保存:redis有持久化功能,使用redis的RDB的功能,定时对于数据进行持久化,并且用sentinel保证主从之间的数据的一致性,可以保证数据不丢失。

3.2 总体架构

4. 详细设计

4.1 核心功能

试题写入、编辑流程

试题的写入或者编辑流程,由教师在界面上编辑完试卷的内容,经过浏览器发送请求至nginx,经由App最终到达Redis,Redis存入内存中。之后redis会定时将内存中的数据持久化,持久化之后的数据就不会因为宕机而丢失,而是存在磁盘中。

试题读取流程

因为试题是存储在Redis中的,所以在读取的时候,根据用户传来的key可以直接获取试题的内容。

考生会通过浏览器选择哪一年的几年级的什么科目的期中或者期末考试试题,选完之后,参数通过浏览器、nginx经由app最后发送至Redis,redis根据key可以很快获取到对应的试题(List),List最终返回给浏览器之后,被渲染成一份完整的试卷展示给考生,考生就可以考试作答了。

4.2 关键设计

4.3 设计规范

试题存储采用Redis的List的数据结构。因为试题有很多题,且是有顺序的,而试题的读取是一个顺序遍历的过程就可以解决的,不需要进行索引。而试题编辑的话,也需要将所有的试题罗列出来,编辑其中的部分,最后全量提交即可,因此List的结构就可以完全满足。

试题的key使用 年份+年级+课程+期中(1)/期末(2)

试题的Value使用Redis的List保存一套试卷的所有试题。

5. 质量设计

无,因为仅仅是存储系统部分

6. 演进规划

无,因为是存储系统部分