写点什么

模块四作业(试卷存储方案)

作者:Dean.Zhang
  • 2022 年 4 月 29 日
  • 本文字数:1858 字

    阅读完需:约 6 分钟

背景

在千万级学生管理系统的考试需求中,试卷的 QPS 估算为 5w/s,所以需要一个 In-memory 的低延迟数据存储中间件存储试卷,Redis 是一个不错的选择。

数据结构设计

我们会用到 Redis 数据结构中的 Sort Set 和 Hash。

总体思路是

  1. 通过 Sort Set 按更新时间顺序存储试卷 id

  2. 通过 Hash 按试卷 id 为键,试卷元信息为值存储每个试卷的元信息

  3. 通过 Hash 按试卷 id 为键,试卷内容为值存储每个试卷的内容

试卷列表 id 存储

该部分使用 Redis 中的 Sort Set 数据结构。

需要按照不同维度,以试卷的更新时间为顺序(Score),存储试卷 id 的去重列表,

查询时,可以通过 ZREVRANGEBYSCORE 的 score rage 实现按时间查找,如果配合 limit 和 ZCOUNT 还可以实现分页查找功能。

根据需求,有

  1. 按照学校和年级查询

  2. 按照学校、年级和科目查询

两种不同维度列表页的查询场景,所以需要按照下面两种维度进行存储。

按照学校+年级存储

Key

学校英文名称-年级

Value

score:更新时间戳

值:科目-试卷 id

(此处的值需要存储科目 id,以确保信息完整)

实例

# 新增/更新清华大学大一年级科目2的试卷id,即123456ZADD Tsinghua-1 1651203218 "2-123456"# 查询清华大学大一年级最近7天的试卷id列表ZREVRANGEBYSCORE Tsinghua-1 1651203221 1651203219# 分页查找清华大学大一年级最近7天的试卷id,每页5个(查找第二页)ZREVRANGEBYSCORE Tsinghua-1 1651203227 1651203219 limit 5 10
复制代码


按照学校+年级+科目存储

Key

学校英文名称-年级-科目 id

Value

score:更新时间戳

值:试卷 id

实例

# 新增/更新清华大学大一年级科目2的试卷id,即123456ZADD Tsinghua-1-2 1651203218 "123456"# 查询清华大学大一年级科目2最近7天的试卷id列表ZREVRANGEBYSCORE Tsinghua-1-2 1651203221 1651203219# 分页查找清华大学大一年级科目2最近7天的试卷id,每页5个(查找第二页)ZREVRANGEBYSCORE Tsinghua-1-2 1651203227 1651203219 limit 5 10
复制代码

试卷元信息存储

该部分用 Redis 中的 Hash 数据结构,即一个 hash map,可以快速存取试卷的元信息,

注意学校范围内试卷 id 需要保证唯一

  1. 列表页查询场景

在各种查询列表页的场景中,通过 Sorted Set 查询到当前页的所有试卷 id,然后再通过这些试卷 id 查询试卷的元信息并显示在列表页。

  1. 考试/批改时获取试卷场景

直接使用试卷 id 就能拿到试卷的元信息

Key

学校英文名称+"meta"

Value

hashKey:试卷 id

值:更新时间戳 + 试卷名称 + 作者 + 描述+ 状态(未使用、正在使用、已使用)

实例

# 存HSET Tsinghua_meta "123456" "1651203219;大一数学期中考试;张xx;难度适中...;0"# 取HGET Tsinghua_meta "123456"
复制代码

试卷内容存储

该部分用 Redis 中的 Hash 数据结构,即一个 hash map,可以快速存取试卷的内容,

  1. 考试/批改时获取试卷场景

直接使用试卷 id 就能拿到试卷的内容

Key

学校英文名称+"content"

Value

hashKey:试卷 id

值:试卷内容的 Json

实例

# 存HSET Tsinghua_content "123456" "{\"选择题\":[{\"1\":\"题目描述\"},{\"2\":\"题目描述\"}]}"# 取HGET Tsinghua_content "123456"
复制代码

读写流程

新增/修改试卷


说明

  1. 新增试卷分为两步,一是新增试卷元信息,而是编辑试卷内容

需要存储 4 个 redis 中的数据结构,即

a. 学校+年级 -> 科目+试卷 id 的 sorted sort

b. 学校+年级+科目 -> 试卷 id 的 sorted sort

c. 试卷 id -> 试卷元信息的 hash

d. 试卷 id -> 试卷内容的 hash

  1. 更新试卷元信息

需要存储 3 个 redis 中的数据结构,即

a. 学校+年级 -> 科目+试卷 id 的 sorted sort

b. 学校+年级+科目 -> 试卷 id 的 sorted sort

c. 试卷 id -> 试卷元信息的 hash

  1. 更新试卷内容

需要存储 4 个 redis 中的数据结构,即

a. 学校+年级 -> 科目+试卷 id 的 sorted sort

b. 学校+年级+科目 -> 试卷 id 的 sorted sort

c. 试卷 id -> 试卷元信息的 hash

d. 试卷 id -> 试卷内容的 hash

分页查询试卷列表


说明

  1. 通过用户选择的时间区间和分页参数,计算总页数和数据的 offset

  2. 通过 ZREVRANGEBYSCORE 命令按更新时间倒序查找该分页的试卷 id 列表

  3. 遍历试卷 id 列表,通过 HGET,查询每个试卷的元信息组成列表页返回

  4. 根据用户选择的查询维度(按学校+年级或按学校+年级+科目),操作不同的 sorted set

查看试卷详情


说明

  1. 通过当前考试的试卷 id,直接在 Redis 的 hash 中 O(1)查找到试卷元信息和内容


服务器数量与性能

根据以往经验,一台 32 核的机器上 Redis 的 QPS 能到 5-10w,保守起见,按 5w 算,其实满足试卷功能的性能需求。

但为了数据的高可用与高性能,需要使用 Redis sentinel 部署在多台服务器。

如果使用 8 核 cpu 的服务器,理论需要使用 4 台服务器,预留一些性能空间,总共需要 5 台 8 核 cpu 的服务器,

又因为 Redis 依赖内存才能达到最佳性能,而试卷需要的存储空间也就是 G 级别,10M 100 ✖️ 100 ✖️ 10 ✖️ 3000

发布于: 刚刚阅读数: 3
用户头像

Dean.Zhang

关注

还未添加个人签名 2018.04.25 加入

还未添加个人简介

评论

发布
暂无评论
模块四作业(试卷存储方案)_Dean.Zhang_InfoQ写作社区