2010-03-21 66 views
6

我對與關係型數據庫一起工作相當陌生,但已經閱讀了幾本書並瞭解了優秀設計的基礎知識。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

回答

4

你規範化的方法有很大的意義,非規範化的方法沒有。


根據我的經驗(電信績效管理,每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

1

什麼將這些ratingOne評爲五個領域包含?收到的票數是多少?那麼你不會知道誰投了票。如果你確實需要非規範化,我只需在圖片表中添加一個「平均評級」字段,並在投票投票時(並保持評級表的原樣)更新。

更一般地說,不要陷入過早優化。嘗試編寫一個測試腳本,它可以創建100.000張圖片和100萬個評分(或任何想要支持的數字),並查看您的AVG查詢需要多長時間。機會仍然會很快。確保你的「收視率」表有一個pictureID索引,因此數據庫不需要遍歷百萬行。

+0

謝謝。我會記住這一點。我將專注於編寫測試用例,並瞭解下一次如何執行測試用例。 – Foo 2010-03-21 19:54:51

1

在RDBMS的世界裏,非規範化的意思是「我要以提高查詢效率的提高維護成本,同時仍然保留了模型的正確性

在你的情況下,效率會略微確實有所上升(因爲所有評級總是從相同的數據頁面中檢索)。

但模型的正確性呢?

有了這個設計,你首先不知道是誰作出了選票(這個信息不再存儲),其次,不能評價超過五次。

由於您的初始模型沒有任何這些限制,我相信這種非規範化不是您真正想要的。

1

享受兩個世界的好方法是使用Mysql觸發器。 http://dev.mysql.com/doc/refman/5.0/en/triggers.html

現在添加一個觸發器,當用戶對圖片進行評級時,它將更新圖片表中的avg_rating。 (使用與您所述相同的選項)

現在,當您選擇時,您只能在一張桌子上選擇。它總是更新。如果你想獲得誰可以評價哪張照片的確切信息,你可以從評價表中選擇。

2

這是我應該怎樣解決這個問題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 ; 

希望這有助於:)