写点什么

设计千万级学生管理系统的考试试卷存储方案

用户头像
9527
关注
发布于: 2021 年 05 月 23 日

作业要求

基于模块 4 第 6 课的估算结果和 Redis sentinel 的初步方案设计,完善考试试卷存储方案,具体包括:1)完善 Redis 的数据结构设计,明确具体使用哪种 Redis 数据结构

2)设计具体的读写流程(可以文字描述也可以序列图描述,序列图要有文字辅助说明)

3)对照模块 4 第 6 课的性能估算结果,计算 Redis sentinel 集群的服务器数量和性能


性能估算

用户量预估

在这里用户量预估为 1000 万

用户行为建模

用户有以下的关键行为

  • 登录注册

  • 文件上传下载

  • 学生选课

  • 学生考试

性能需求计算

登陆行为

  • 该系统主要管理学生的信息,作业,还有考试等,其中交作业是高频场景,每天每个学生都需要交作业。在这里假设每个学生每天提交 4 次作业,那么登陆的次数就是 1000w * 4 = 4000w。交作业时间是集中在晚 8 点到 10 点,那么登陆的 TPS 要求为: 4000w/(4*3600) = 3000/s

  • 每次登陆会产生一条登陆记录,因此日存储登陆记录为 4000w,假设登陆记录保持 3 个月,那么总的数据量为 4000w x 3 x 30 = 36 亿。每条记录包含学生 ID,登陆时间,登陆 IP,总共为 12 字节,那么数据总量为 36 亿 * 12 = 43G

注册行为

  • 总共 1000w 学生,但是每年只有新生需要注册,假设每年的新生为 250w,而注册时间为 1 个月,那么每天的注册请求为 250w/30 = 8.3w,第一天的注册人数会多一点,所以可以调整到 10w 一天,TPS 为 10w/(12*3600) = 3/s

  • 学生注册后需要存储学生信息,其中

  • 在校学生数据存量=1000w*200 字节=2G,图片数据 1000w*1M = 10T

  • 离校学生数据存量=2G/4=0.5G, 图片数据 10T/4=2.5T

  • 考试行为

  • 每门学科每年 2 次考试,每个学生平均一学期 20 门课,每门考试 20 道判断题,20 道选择题,4 道大题,考试结果永久保存,存储量为

  • 在校学生: 1000w*20(课)*2(考试次数)*1000(答案)*2(学期)*3(年)=2.4T

  • 离校学生: 2.4T/4 = 0.6T

  • 考试提交,假设考试在 1 个月内完成,时间为上午下午各 4 小时,请求试卷在开考前 1 分钟,提交答案在考试结束前 30 分钟

  • 请求试卷: 1000w*20(课)/20(周末不考)/4(每天 4 堂考试)/60 秒=5w/s

  • 提交试卷: 1000w*20(课)/20(周末不考)/4(每天 4 堂考试)/30 分钟=1700/s

存储性能需求

登陆

  • 登录次数: 3000/s

  • 登陆记录: 存储数据量 36 亿条,存储容量 43G,写入 TPS = 登陆 TPS = 3000/s

注册

  • 注册 TPS = 3/s

  • 在校学生数据存储: 基本数据=2G,图片数据=10T

  • 离校学生数据存储: 基本数据每年增长 500M,图片数据增长 2.5T

考试

  • 在校学生考试结果存储: 2.4T

  • 离校学生考试结果存储: 年增长 0.6T

  • 试卷请求 QPS: 5w/s

  • 提交试卷 TPS: 1700/s

存储架构设计

登陆记录

单机->不需要分区->分片架构->数据库分库分表

在校学生基本信息

单机存储->单机写->单机读->自动切换->数据库主备

在校学生图片数据

集群存储->分片架构->HBase

离校学生图片数据

集群存储->分片架构->HBase

考试数据

对于考试结果,有不同的应用场景,分为老师和学生两种应用场景,而每一种应用场景,对于数据的操作都不一样,因此采取两种不同的存储方式,有一定的数据冗余

老师课程考试结果

  • MySQL 分库分表,因为老师需要对每一个学生的考试结果进行分析,产生报表等等,采用 MySQL 的关系型数据库便于老师的分析操作

学生考试结构

  • HBase,学生只需要针对自己的考试结果进行查询,不需要对(也不允许)对别人的考试结果进行查询,并且学生也不需要对自己的考试结果进行分析对比,所以采用 HBase 做存储就足够了。


试卷存储和访问

由于此次作业是专门针对考试试卷的存储方案,因此在这里进行详细叙述。根据上一节对考试系统的性能分析,我们知道以下数据

  • 在校学生考试结果存储: 2.4T

  • 离校学生考试结果存储: 年增长 0.6T

  • 试卷请求 QPS: 5w/s

  • 提交试卷 TPS: 1700/s

在最高峰的时候,TPS 能够达到 5w/s,这个是高性能的读请求,因为试卷并不是实时生成,而是由老师提前生成上传,然后考试的时候直接读取。所以是 5w/s 的读请求峰值。并且试卷一旦生成,修改的频率是比较低的,因为 Redis 自带的存储功能可以满足这个需求。并且利用 Redis 的缓存,可以极大的提升考试试卷的读请求性能。


Redis 分析

Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA 脚本(Lua scripting), LRU 驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis 哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis 的应用场景

速度快,完全基于内存,使用 C 语言实现,网络层使用 epoll 解决高并发问题,单线程模型避免了不必要的上下文切换及竞争条件;缓存,毫无疑问这是 Redis 当今最为人熟知的使用场景,在提升服务器性能方面非常有效。以下是几个使用 Redis 的场景用例:

  1. 排行榜,如果使用传统的关系型数据库来做,非常麻烦,而利用 Redis 的 SortSet 数据结构能够非常方便搞定;

  2. 计算器/限速器,利用 Redis 中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用 MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个 API 的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;

  3. 好友关系,利用集合的一些命令,比如求交集、并集、差集等,可以方便搞定一些共同好友、共同爱好之类的功能;

  4. 简单消息队列,除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制,比如到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦;

  5. Session 共享,以 PHP 为例,默认 Session 是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用 Redis 保存 Session 后,无论用户落在那台机器上都能够获取到对应的 Session 信息。


Redis sentinel

Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。


总结

基于以上的分析,所以考试试卷的存储和访问采用 Redis 存储系统,而试卷的存储格式可以是 JSON string 结构,也可以是 list 的结构。每一个题目可以考虑称为 list 里面的一个存储对象。


存储方案设计

存储方案就是设计基于存储系统的数据模型。根据存储架构,我们可以采用以下的存储方案

数据结构设计

学生图片

  • key: 学校 ID + 学号 ID + pic

  • Column Family: Info

  • Column: Pic

  • 学生登陆之后,在界面上显示头像,按照 Key 读取信息

  • 管理员可以查看班级所有同学头像,并且可以按照学号前缀里面的班级信息 scan,比如 20200810 代表 2020 级 8 系 10 班

学生登陆

  • key: 学校 ID + 学号 ID + timestamp

  • Column Family: Login

  • Column: IP

  • 学生查看自己的登陆记录,直接按照前缀聊查询即可

考试结果

  • key: 学校 ID + 学号 ID + 考试 ID

  • Column Family: test

  • Column: result (JSON), score,

  • 学生提交考试结果,直接按照 key 保存 result

  • 老师批改试卷后,直接写入 score

  • 学生查看自己成绩,按照 key 读取 result 和 score,可以看到得分和错误

考试试卷

  • key:学校 ID + 考试 ID

  • Column Family: test

  • Column: questions (list 结构),每一道题目是 list 里面的一个对象

  • 老师准备好试卷之后,直接按照 key 保存试卷

  • 学生考试开始前,按照 key 读取试卷


发布于: 2021 年 05 月 23 日阅读数: 76
用户头像

9527

关注

还未添加个人签名 2020.04.22 加入

还未添加个人简介

评论

发布
暂无评论
设计千万级学生管理系统的考试试卷存储方案