每日一句
Once you choose hope, anything’s possible.
— Christopher Reeve
任务汇总
工作日志
- 上午,查看 jmeter 的使用,并测试。
- 下午,jmeter 数据循环太多,服务端没有配置好,导致直接服务崩了,循环 10 次,下午 5 点还没结束。
- 下午,由于测试出问题,还没出结果,心情郁闷,因此期间打了炉石。
- 下午,5 点多,编写 redis 和 mysql 和 python 查看代码,从小数目依次增加调整设备和判断情况。
- 晚上,组员退出,安排课程给徐俊飞,徐俊飞推脱,只好自己写。
社交日志
为了课程模块和抢课模块服务,使用了 Redis 中间件和 MySQL 数据库来存储。
其中,Redis 中存储以下信息:
stulist
对应学生 ID 的集合,属于 Redis 的集合结构,表示学生的列表,存储 stuID
course_cap+courseID
对应 courseID 这个课程的容量,简单的 key-value 结构,-1 时表示这个课程不存在,非负表示这个课程存在,非负值表示这个课程当前剩余容量。
courseinfo+courseID
对应 courseID 这个课程的信息,属于 Redis 哈希结构,包含 courseID、Name、TeacherID
stu_course+stuID
对应 stuID 选择的课程,属于 Redis 的集合结构,表示学生选择的课程,存储 courseID
创建课程部分
- 判断容量是否小于 0,即容量出错
- 从数据库读取 name 对应课程,判断 name 是否存在,存在则不添加数据直接返回;name 不存在,则添加数据
- 添加数据成功后,将课程的属性放到缓存中,包含课程容量、课程信息
获取单个课程部分
- 直接使用缓存中的课程容量,如果容量 key 不存在或容量为负值,则课程不存在
- 如果容量存在,则获取课程的信息。判断缓存中是否存在课程信息,如果不存在,则从数据库中访问,并更新到缓存中;如果存在,则使用缓存数据。
绑定课程部分
- 根据课程 ID 获取数据库中的课程数据,没有,则课程不存在;存在,则判断课程的教师。
- 教师存在,则返回无法绑定;如果教师不存在,则将教师更新,并更新到数据库中。
- 数据库更新成功时,删除缓存中的课程信息,保证数据一致性。
解绑课程部分
- 逻辑与绑定课程部分类似。先看看数据库中的教师是否存在,不存在则无法绑定,存在则置空并更新到数据库中。
- 数据库更新成功时,删除缓存中的课程信息,保证数据一致性。
获取老师课程部分
- 这部分简单一些,直接读数据库,看看能否读到数据。
抢课部分的数据是否通过接口插入,还是会通过数据库插入。数据库插入的话,应该是后端已经运行状态了。
除此之外,抢课的话,是否会存在先测试抢课,然后再测试非抢课的功能,再测试抢课的可能性。
(为此,课程容量还是通过 lua 脚本更新的,若为-1,则表明之前没有这个课程,直接更新;若非零,则说明之前存在,不更新。)
当然,如果先抢课,之后把数据库清空,重新注入数据的话,那由于没清空缓存,直接 GG,希望没有这样的可能。
如果中间修改数据的话,那还是不用缓存了,直接读写数据库好了。
所以,在没有说明的情况下,我只能假设如果存在两次抢课,那么这两次抢课共用相同的数据,中间不存在数据库中增加数据、删除数据的可能。
var BookChannel = make(chan *models.UserCourse, maxChannel)
管道,将学生的选课结果异步的写入数据库中
抢课部分
- 课程是否存在
- 读取缓存中的容量,
- 如果容量为-1,说明之前查找过这个不存在的课程,返回课程不存在;
- 容量为 0,课程余量为 0,返回课程已满;容量为正,课程存在且有容量
- 如果缓存中不存在容量,则读取数据库数据,并根据课程是否存在将容量放到缓存,有就 cap,没有就-1 并返回课程不存在
- 读取缓存中的容量,
- 学生是否存在
- 不存在 stu_list,读取数据库并加载到缓存中;判断学生是否在 stu_list 中,不存在,则返回学生不存在
- lua 脚本学生抢课
- 判断课程 ID 是否在学生已选课表里,返回 2
- 如果课程容量小于等于 0,则课程容量已满,返回 0
- 课程容量-1,将课程放到学生课表里,返回 1
- 如果抢课结果为 1,
- 那么将学生 ID 和课程 ID 传入管道中,返回结果
- 管道消费者将学生选课数据插入到数据库中。
- 最开始是直接 go 协程的,但是担心协程太多,爆掉
获得用户课程部分
- 学生是否存在(与抢课部分相同)
- 从缓存 stucourse+stuID 获得学生的课程,
- 如果结果为空,
- 认定为缓存中没有数据,从数据库中访问,并向缓存中增加一个空字符串""表示之前已经访问过
- 将数据库中获得学生对应的课程放到缓存 stucourse+stuID 中(由于 set,自动去重)
- 调用 GetCourseInfo 函数,先从缓存 course_info+courseID 中读取课程的信息,如果信息不存在,则读取数据库并加载到缓存中
- 如果结果不为空
- 如果只有一个,且为空字符串,则表明之前访问过,且读取了数据库仍然是空,直接返回
- 如果不止一个或唯一一个不为空字符串,则获得学生对应课程 ID
- 调用 GetCourseInfo 函数,读取课程的信息,同上
- 如果结果为空,
- 根据课程数目返回相应结果
当前的安排比较不合理
如果数据先放到数据库,再开启程序,那么可以在开启程序的时候直接将数据加载到内存中,这是只需要考虑数据是否比较多。
如果学生、课程比较小,那么可以使用 redis 的 list 或者 set 来存储学生、课程。
如果学生、课程比较多,那么全部放进去,有可能会导致 redis 内存爆掉,这样的情况,可以调节 redis 内存或者使用 redis 的布隆过滤器。如果使用 redis 布隆过滤器,那么就可以极大的降低内存空间,布隆过滤器认为学生 ID 或者课程 ID 不存在,那么直接返回,减少数据库访问次数。如果布隆过滤器认为存在,则再去查找数据。
同时,如果数据比较多,