[英]Database design for optimisation
几年前,我为11-16岁的学生设计了PHP,JavaScript和MySQL的奖励系统。
前提很简单:
数据库结构也很简单(可能太多了):
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
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
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
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
该系统在一段时间内运行良好。 现在,对于某些查询,它开始大量放慢速度。
从本质上讲,我每次需要总访问学生的奖励积分时,所需的查询需要年龄 。 这是一些示例查询及其运行时:
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
SELECT Recipient_ID, SUM(points) AS Total_Points FROM
事务GROUP BY Recipient_ID
SELECT Recipient_ID, SUM(points) AS Total_Points FROM
现在,我意识到,尤其是对于第二个查询,我永远都不应运行将返回如此大量行的调用,但是系统运行所处的框架的局限性意味着,如果我愿意,我别无选择显示学生的总奖励积分,以供教师/教师/年度经理/领导才能查看和分析。
幸运的是,我们不得不使用的框架正在发生变化。 现在,我们将使用oAuth而非可怕的,过时的JavaScript小部件格式。
不幸的是,或者,我想很幸运,这意味着我们将不得不重写很多系统。
重写系统时,我打算关注的主要领域之一是数据库结构。 随着时间的流逝,它只会变得越来越大,因此我需要做一些面向未来的工作。
因此,我的主要问题是: 存储学生积分的最有效方法是什么?
我能想出的唯一的想法是有一个单独的表名为totals
有Student_ID
和Points
领域。 职员每次提出一些要点时,都会在transactions
表中添加一行,同时也会更新totals
表。
这样有效吗? 还有一个Points_Since_Monday
类型字段会有效吗? 我该如何更新/保持呢?
在主要问题之上,如果有人对数据库表的优化提出一般改进的建议,请告诉我。
预先感谢,邓肯
您的设计没有什么特别错误,它应使其与报告一样慢。 我认为在工作中还必须考虑其他因素,例如服务器正在过载或运行缓慢。 只有这样,您才能知道。
为了测试您的设计,我在台式计算机上运行的2008 SQL Server上重新创建了该设计。 我有一台标准计算机,单个硬盘,没有SSD,没有RAID等,因此在适当的数据库服务器上,结果应该更好。 当您使用MySQL时,我不得不对设计进行一些更改,但是所有更改都不会影响性能,只是为了可以在数据库上运行它。
这是我使用的表结构,我不得不猜测您在Student
和Staff
表中将要拥有的内容,因为您不希望看到它们。 我还自由更改了Giver_ID
和Receiver_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.