mySQL效率问题 - 如何找到正常化的正确平衡......?

问题描述:

我对与关系型数据库一起工作相当陌生,但已经阅读了几本书并了解了优秀设计的基础知识。mySQL效率问题 - 如何找到正常化的正确平衡......?

我正面临设计决策,我不确定如何继续。以下是我正在构建的一个非常简化的版本:用户可以对照片1-5进行评分,并且我需要显示图片上的平均投票数,同时记录个人投票。例如,12人投1中,7人投2中,等等,等等

我正常化怪胎最初设计表结构是这样的:

Table pictures 
id* | picture | userID | 

Table ratings 
id* | pictureID | userID | rating 

与所有设置的外键约束和一切因为他们应该是。每次有人评价一张照片时,我都会在评分中插入一条新的记录,并用它来完成。

要查找的平均评分的图片大小,我只是运行是这样的:

SELECT AVG(rating) FROM ratings WHERE pictureID = '5' GROUP by pictureID 

有它设置这种方式让我跑我看中的统计数据。我可以很容易地找到谁给某张照片评了一张3,而不是。

现在我想如果有一大堆评级(这是非常可能的,我真的在设计),发现平均会变得非常昂贵和痛苦。

使用非标准化版本似乎更有效。例如:

Table picture 
id | picture | userID | ratingOne | ratingTwo | ratingThree | ratingFour | ratingFive 

要计算平均值,我只需要选择一行。看起来效率更高,但更加丑陋。

有人能指出我该做什么的正确方向吗?我最初的研究表明我必须“找到适当的平衡”,但我该如何去寻找这种平衡?任何文章或额外的阅读信息也将不胜感激。

谢谢。

+0

你有没有遇到性能问题,或者你只是问吗? – Pentium10 2010-03-21 08:32:52

+0

我还没有遇到性能问题。我只是不想设计出可能在高负荷下屈曲的东西。 – Foo 2010-03-21 08:36:56

你规范化的方法有很大的意义,非规范化的方法没有。


根据我的经验(电信绩效管理,每1/4小时的数据点的数十万),我们将做到以下几点:

Table: pictures 
id* | picture | userID | avg_rating | rating_count 

Table: ratings 
id* | pictureID | userID | rating 

对于电信图片评级将被重新计算每天一次,你应该做定期的(例如每小时)或每次插入时(重新计算评级图片,而不是整个表格)。这取决于您获得的评分数量。


在电信我们也保持在什么是你的“照片”表和一个1/4H时间戳的收视率表的等级,日期,但我不认为你需要的详细程度。


的 '非规范化' 是移动calculateable事实(计数(等级)和AVG(评级))到照片表。这可以节省CPU周期,但会增加存储空间。

+0

+1,我会推荐相同的... – 2010-03-21 09:04:41

什么将这些ratingOne评为五个领域包含?收到的票数是多少?那么你不会知道谁投了票。如果你确实需要非规范化,我只需在图片表中添加一个“平均评级”字段,并在投票投票时(并保持评级表的原样)更新。

更一般地说,不要陷入过早优化。尝试编写一个测试脚本,它可以创建100.000张图片和100万个评分(或任何想要支持的数字),并查看您的AVG查询需要多长时间。机会仍然会很快。确保你的“收视率”表有一个pictureID索引,因此数据库不需要遍历百万行。

+0

谢谢。我会记住这一点。我将专注于编写测试用例,并了解下一次如何执行测试用例。 – Foo 2010-03-21 19:54:51

在RDBMS的世界里,非规范化的意思是“我要以提高查询效率的提高维护成本,同时仍然保留了模型的正确性

在你的情况下,效率会略微确实有所上升(因为所有评级总是从相同的数据页面中检索)。

但模型的正确性呢?

有了这个设计,你首先不知道是谁作出了选票(这个信息不再存储),其次,不能评价超过五次。

由于您的初始模型没有任何这些限制,我相信这种非规范化不是您真正想要的。

享受两个世界的好方法是使用Mysql触发器。 http://dev.mysql.com/doc/refman/5.0/en/triggers.html

现在添加一个触发器,当用户对图片进行评级时,它将更新图片表中的avg_rating。 (使用与您所述相同的选项)

现在,当您选择时,您只能在一张桌子上选择。它总是更新。如果你想获得谁可以评价哪张照片的确切信息,你可以从评价表中选择。

这是我应该怎样解决这个问题http://pastie.org/879604

drop table if exists picture; 
create table picture 
( 
picture_id int unsigned not null auto_increment primary key, 
user_id int unsigned not null, -- owner of the picture, the user who uploaded it 
tot_votes int unsigned not null default 0, -- total number of votes 
tot_rating int unsigned not null default 0, -- accumulative ratings 
avg_rating decimal(5,2) not null default 0, -- tot_rating/tot_votes 
key picture_user_idx(user_id) 
)engine=innodb; 

insert into picture (user_id) values 
(1),(2),(3),(4),(5),(6),(7),(1),(1),(2),(3),(6),(7),(7),(5); 


drop table if exists picture_vote; 
create table picture_vote 
( 
picture_id int unsigned not null, 
user_id int unsigned not null,-- voter 
rating tinyint unsigned not null default 0, -- rating 0 to 5 
primary key (picture_id, user_id) 
)engine=innodb; 

delimiter # 

create trigger picture_vote_before_ins_trig before insert on picture_vote 
for each row 
begin 
declare total_rating int unsigned default 0; 
declare total_votes int unsigned default 0; 

select tot_rating + new.rating, tot_votes + 1 into total_rating, total_votes 
    from picture where picture_id = new.picture_id; 

-- counts/stats 
update picture set 
    tot_votes = total_votes, tot_rating = total_rating, 
    avg_rating = total_rating/total_votes 
where picture_id = new.picture_id; 

end# 
delimiter ; 

希望这有助于:)