Ruby Rails - 这个 Class 的最有效解决方案?

Ruby on Rails - Most efficient solution for this Class?

我是高级 Comp。科学。主要为我们的教员从事高级设计项目。这个项目的名字是"Gradebook",它负责让教师为学生记录成绩,让学生在class中查看自己的成绩。此项目是在 Rails 上用 Ruby 编写的,此功能集已集成到我们当前的 CS 网站中。

我们项目的一个要求是不断更新课程平均分和每个学生的平均分。所以我设计了一个 CourseInfo class 和一个 StudentInfo class 来帮助完成这个过程。

CourseInfo class 接受成绩簿(ActiveRecord 对象)作为参数并计算课程平均分。它创建了一个 StudentInfo 对象的关联数组,每个 StudentInfo 对象都包含 class 中学生的总平均分。这样做的好处是我可以用一行初始化class的代码来计算平均课程,而且非常干净。

但是我正在考虑一个问题。问题是,当发出另一个 HTTP 请求时,CourseInfo 对象无法存活,我必须继续重新创建它。无论我是添加作业、编辑类别还是记录成绩,我都必须保持更新,因为这个项目一直使用 AJAX 请求。教师不必刷新任何页面,因为每个操作都会创建 AJAX 请求。

例如,假设我正在记录特定作业的成绩。我将每个成绩记录到电子表格中,都会发出 AJAX 请求,并且课程平均成绩会随着每个新成绩而更新。但问题是,如果我想在记录学生成绩后更新 Course Average,由于 CourseInfo 对象在下一个请求中不会保持活动状态,我必须重新创建该对象以保持平均值更新。但这是很多工作。这涉及计算每个学生的每项作业的平均水平,然后计算每个学生的课程平均水平。我知道,很多工作,可以更简单吗?

所以很自然地,我希望这个 CourseInfo 对象在客户使用该网站时永远存在。我想过很多不同的方法来解决这个问题:

1) 全局变量或 Class 变量 - 老实说,我想远离这种方法,因为我听说这是糟糕的设计。我还听说这种方法不是线程安全的。但它似乎为我的问题提供了一个简单的解决方案?

2) 序列化数据库中的对象 - 这是我学习最多的内容。我听说有时人们会在网络应用程序中序列化一个包含用户偏好的哈希,为什么不序列化我的 CourseInfo 对象呢?我还对 MessagePack gem 进行了一些研究,我可能会使用 MessagePack 对 CourseInfo 对象进行编码,然后将其存储到数据库中。我觉得这会带来显着的性能提升。

3) 使用某种缓存 - Redis 等 Gem 充当缓存,我喜欢 Redis,因为它是键值存储。我可以为会话期间使用的每个成绩簿存储一个 CourseInfo 对象,如果我需要更新 CourseInfo 对象,我可以使用成绩簿的 ID 作为键来简单地获取 CourseInfo 对象。但我不确定这是否是线程安全的。如果两位教师试图同时更新两个不同的成绩怎么办?每个使用 Gradebook 的客户都会有此 CourseInfo 对象的多个实例吗?

4) 将它存储在会话中 - 是的,我几乎把这个选项从我的列表中划掉了。我研究了这种方法,听说在会话中存储大量数据很糟糕。我不想这样做。

你怎么看?如果我不想为每个请求重新初始化这个大对象,我怎样才能让它永远存在?什么是最有效的解决方案?你觉得我的设计怎么样?

非常感谢您的帮助!谢谢!

使用 2) 序列化数据库中的对象

由于实现最简单的事情的敏捷理念可能首先起作用。

Saving arrays, hashes, and other non-mappable objects in text columns

course_average 始终反映用户记录的持久状态。序列化它在 ActiveRecord 中很简单。如果你使用的是 postgres ,你甚至可以使用原生的 json store,你不仅可以反序列化,还可以查询。无需额外的复杂性来维护额外的商店。此解决方案还具有持久计数器缓存的好处。(如果没有任何变化,则无需重新计算)

然而,使用缓存也是一个有价值的选择。请记住,如果你想将 redis 用作缓存存储,你必须显式配置 cache expiring policy,因为默认情况下 none 的键将过期并且你将收到内存不足错误,当 redis增长超出了机器上 RAM 的大小。 redis-rails gem 将设置 rails 使用 redis 进行缓存。

在会话中存储此信息也可能有效,但请注意您的会话不要变得很大。整个会话数据总是完全加载到内存中,无论是否需要其中的某些信息。始终为每个 http 连接将数兆字节的数据加载到内存中可能不是一个好主意。

还有第5个选项,我先评价一下。检查一下,平均值的计算真的需要这么长时间吗?或者可以改进它的性能,例如通过减少 n+1 次查询,设置适当的索引,在 sql 中完成整个计算或在 sql 中完全准备必要的数据,以便在 1 次查询中获取所有必要的数据。