繁体   English   中英

优化数据库设计

[英]Database design for optimisation

几年前,我为11-16岁的学生设计了PHP,JavaScript和MySQL的奖励系统。

前提很简单:

  • 教职员工针对不同类别的学生(“积极态度和行为”,“模范公民”等)指向学生
  • 学生累积这些积分,然后将其用于我们的在线商店(iTunes凭证等)

现有系统

数据库结构也很简单(可能太多了):

交易

239,189行

CREATE TABLE `transactions` (
 `Transaction_ID` int(9) NOT NULL auto_increment,
 `Datetime` date NOT NULL,
 `Giver_ID` int(9) NOT NULL,
 `Recipient_ID` int(9) NOT NULL,
 `Points` int(4) NOT NULL,
 `Category_ID` int(3) NOT NULL,
 `Reason` text NOT NULL,
 PRIMARY KEY  (`Transaction_ID`),
 KEY `Giver_ID` (`Giver_ID`),
 KEY `Datetime` (`Datetime`),
 KEY `DatetimeAndGiverID` (`Datetime`,`Giver_ID`),
 KEY `Recipient_ID` (`Recipient_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=249069 DEFAULT CHARSET=latin1

分类

34行

CREATE TABLE `categories` (
 `Category_ID` int(9) NOT NULL,
 `Title` varchar(255) NOT NULL,
 `Description` text NOT NULL,
 `Default_Points` int(3) NOT NULL,
 `Groups` varchar(125) NOT NULL,
 `Display_Start` datetime default NULL,
 `Display_End` datetime default NULL,
 PRIMARY KEY  (`Category_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

奖励

82行

CREATE TABLE `rewards` (
 `Reward_ID` int(9) NOT NULL auto_increment,
 `Title` varchar(255) NOT NULL,
 `Description` text NOT NULL,
 `Image_URL` varchar(255) NOT NULL,
 `Date_Inactive` datetime NOT NULL,
 `Stock_Count` int(3) NOT NULL,
 `Cost_to_User` float NOT NULL,
 `Cost_to_System` float NOT NULL,
 PRIMARY KEY  (`Reward_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=latin1

购买

5,889行

CREATE TABLE `purchases` (
 `Purchase_ID` int(9) NOT NULL auto_increment,
 `Datetime` datetime NOT NULL,
 `Reward_ID` int(9) NOT NULL,
 `Quantity` int(4) NOT NULL,
 `Student_ID` int(9) NOT NULL,
 `Student_Name` varchar(255) NOT NULL,
 `Date_DealtWith` datetime default NULL,
 `Date_Collected` datetime default NULL,
 PRIMARY KEY  (`Purchase_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=6133 DEFAULT CHARSET=latin1

问题

该系统在一段时间内运行良好。 现在,对于某些查询,它开始大量放慢速度。

从本质上讲,我每次需要总访问学生的奖励积分时,所需的查询需要年龄 这是一些示例查询及其运行时:

全校排名前15位的学生(不包括出勤类别)

SELECT CONCAT( s.Firstname,  " ", s.Surname ) AS  `Student` , s.Year_Group AS `Year Group`, SUM( t.Points ) AS  `Points` 
FROM frog_rewards.transactions t
LEFT JOIN frog_shared.student s ON t.Recipient_ID = s.id
WHERE t.Datetime >  '2013-09-01' AND t.Category_ID NOT IN ( 12, 13, 14, 26 )
GROUP BY t.Recipient_ID
ORDER BY  `Points` DESC 
LIMIT 0 , 15

说明1

  • 运行时间: 44.8425秒

SELECT Recipient_ID, SUM(points) AS Total_Points FROM事务GROUP BY Recipient_ID SELECT Recipient_ID, SUM(points) AS Total_Points FROM

说明2

  • 运行时间: 9.8698秒

现在,我意识到,尤其是对于第二个查询,我永远都不应运行将返回如此大量行的调用,但是系统运行所处的框架的局限性意味着,如果我愿意,我别无选择显示学生的总奖励积分,以供教师/教师/年度经理/领导才能查看和分析。

解决时间

幸运的是,我们不得不使用的框架正在发生变化。 现在,我们将使用oAuth而非可怕的,过时的JavaScript小部件格式。

不幸的是,或者,我想很幸运,这意味着我们将不得不重写很多系统。

重写系统时,我打算关注的主要领域之一是数据库结构。 随着时间的流逝,它只会变得越来越大,因此我需要做一些面向未来的工作。

因此,我的主要问题是: 存储学生积分的最有效方法是什么?

我能想出的唯一的想法是有一个单独的表名为totalsStudent_IDPoints领域。 职员每次提出一些要点时,都会在transactions表中添加一行,同时也会更新totals表。

这样有效吗? 还有一个Points_Since_Monday类型字段会有效吗? 我该如何更新/保持呢?

在主要问题之上,如果有人对数据库表的优化提出一般改进的建议,请告诉我。

预先感谢,邓肯

您的设计没有什么特别错误,它应使其与报告一样慢。 我认为在工作中还必须考虑其他因素,例如服务器正在过载或运行缓慢。 只有这样,您才能知道。

为了测试您的设计,我在台式计算机上运行的2008 SQL Server上重新创建了该设计。 我有一台标准计算机,单个硬盘,没有SSD,没有RAID等,因此在适当的数据库服务器上,结果应该更好。 当您使用MySQL时,我不得不对设计进行一些更改,但是所有更改都不会影响性能,只是为了可以在数据库上运行它。

这是我使用的表结构,我不得不猜测您在StudentStaff表中将要拥有的内容,因为您不希望看到它们。 我还自由更改了Giver_IDReceiver_ID的“ Transaction表中的字段名称,因为我假设只有工作人员会给积分,而学生会得到积分。

数据库表

我生成了随机数据,以使表中的行数与您所说的数据库中的行数相同

数据生成

我跑了两个你说要花很长时间的查询,我已经更改了它们以适合我的设计,但是我(希望)结果是一样的

SELECT TOP 15
        Firstname + ' ' + Surname
       ,Year_Group
       ,SUM(Points) AS Points
FROM    points.[Transaction]
        INNER JOIN points.Student ON points.[Transaction].Student_ID = points.Student.Student_ID
WHERE   [Datetime] > '2013-09-01'
        AND Category_ID NOT IN ( 12, 13, 14, 26 )
GROUP BY Firstname + ' ' + Surname
       ,Year_Group
ORDER BY SUM(Points) DESC 


SELECT  Student_ID
       ,SUM(Points) AS Total_Points
FROM    points.[Transaction]
GROUP BY Student_ID

这两个查询在大约1秒内返回了结果。 除了默认情况下在主键上生成的CLUSTERED索引之外,我没有在表上创建任何其他索引。 通过查看执行计划,查询处理器估计实现以下索引可以将查询成本降低81.0309%

CREATE NONCLUSTERED INDEX [<Name of Missing Index>]
ON [points].[Transaction] ([Datetime],[Category_ID])
INCLUDE ([Student_ID],[Points])

执行计划

正如其他人所评论的那样,在花费大量时间重新设计数据库之前,我会在其他地方寻找瓶颈。

更新:

我意识到我从未真正解决过您的特定问题:

存储学生积分的最有效方法是什么?

我唯一能想到的想法是有一个单独的表,称为total,带有Student_ID和Points字段。 职员每次提出一些要点时,都会在交易表中添加一行,同时也会更新总计表。

除非您已经探索了其他所有可能的方法来加快数据库的速度,否则我建议不要单独保留一个点。 单独的计数可能与事务不同步,然后您必须对所有内容进行协调,并找出问题所在以及正确的总数。

在尝试提高速度之前,您应始终专注于保持数据的正确性和一致性。 多数情况下,正确的(规范化)数据模型将足够快地运行。

在我工作过的地方,我们发现加速数据库的最经济有效的方法就是升级硬件。 比花费许多工时重新设计数据库更快,更便宜:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM