2010-09-20 72 views
0

我有一個使用JomSocial的Joomla網站。我有一個.NET網絡應用程序,我正在努力,最終將取代Joomla,因爲我更喜歡.NET over PHP。現在我有.NET用戶正在使用的移動網站。LINQ性能問題

LINQ to Entity使開發非常迅速,但我現在正在嘗試解決性能問題。相互發送消息是#1活動,迄今爲止發送了超過40k條消息。這也是我遇到性能問題的地方。以下是JomSocial用於存儲消息的兩個表格。下面是我正在使用的當前LINQ代碼,它返回我想要的結果,僅需兩秒即可完成。

我認爲通過列名你可能會弄清楚數據是什麼樣子,但如果沒有,我可以創建一些,然後在這裏發佈一些,因爲我必須用盡一點點。我應該提到的是,我正在使用帶有.NET 3.5和MySQL w/MySQL .NET Connector的實體框架。

表:

delimiter $$ 
CREATE TABLE `jos_community_msg` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `from` int(10) unsigned NOT NULL, 
    `parent` int(10) unsigned NOT NULL, 
    `deleted` tinyint(3) unsigned DEFAULT '0', 
    `from_name` varchar(45) NOT NULL, 
    `posted_on` datetime DEFAULT NULL, 
    `subject` tinytext NOT NULL, 
    `body` text NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `parent` (`parent`), 
    KEY `deleted` (`deleted`), 
    KEY `from` (`from`) 
) ENGINE=MyISAM AUTO_INCREMENT=340 DEFAULT CHARSET=utf8$$ 

delimiter $$ 
CREATE TABLE `jos_community_msg_recepient` (
    `msg_id` int(10) unsigned NOT NULL, 
    `msg_parent` int(10) unsigned NOT NULL DEFAULT '0', 
    `msg_from` int(10) unsigned NOT NULL, 
    `to` int(10) unsigned NOT NULL, 
    `bcc` tinyint(3) unsigned DEFAULT '0', 
    `is_read` tinyint(3) unsigned DEFAULT '0', 
    `deleted` tinyint(3) unsigned DEFAULT '0', 
    UNIQUE KEY `un` (`msg_id`,`to`), 
    KEY `msg_id` (`msg_id`), 
    KEY `to` (`to`), 
    KEY `idx_isread_to_deleted` (`is_read`,`to`,`deleted`), 
    KEY `from` (`msg_from`), 
    KEY `parent` (`msg_parent`), 
    KEY `deleted` (`deleted`), 
    KEY `to_deleted` (`deleted`,`to`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8$$ 

LINQ:

var messages = (
    from b in context.jos_community_msg 
    join i in (
     from i in context.jos_community_msg_recepient 
     join a in context.jos_community_msg on i.msg_id equals a.id 
     where i.to == userId && && a.deleted == 0 
     group a by a.parent into g 
     select g.Max(p => p.id)) on b.id equals i 
    join a in context.jos_community_msg_recepient on i equals a.msg_id 
    orderby b.id descending 
    select new MessageHeaderItem() 
    { 
     IsDeleted = false, 
     IsRead = (a.is_read.Value == 0) ? false : true, 
     MessageId = b.parent, 
     Sent = b.posted_on.Value, 
     Subject = b.subject, 
     UserId = a.msg_from 
    }); 

    total = messages.Count(); 
    return messages.Skip(start).Take(max).ToList(); 

我已經嘗試了一堆的變化,但沒有任何已經快成功了。使用子選擇對性能不利,但我不知道如何從該表中獲取消息鏈中的最後一條消息。

更新: 這裏是正在生成的SQL:

SELECT 
`Limit1`.`C1`, 
`Limit1`.`C2`, 
`Limit1`.`C3`, 
`Limit1`.`parent`, 
`Limit1`.`posted_on`, 
`Limit1`.`subject`, 
`Limit1`.`msg_from`, 
`Limit1`.`C4`, 
`Limit1`.`C5`, 
`Limit1`.`C6` 
FROM (SELECT 
`Extent1`.`id`, 
`Extent1`.`parent`, 
`Extent1`.`posted_on`, 
`Extent1`.`subject`, 
`Extent6`.`msg_from`, 
1 AS `C1`, 
cast(0 as decimal(0,0)) AS `C2`, 
CASE WHEN (0 = (`Extent6`.`is_read`)) THEN (cast(0 as decimal(0,0))) ELSE (cast(1 as decimal(0,0))) END AS `C3`, 
'Test' AS `C4`, 
'' AS `C5`, 
'' AS `C6` 
FROM `jos_community_msg` AS `Extent1` INNER JOIN (SELECT 
(SELECT 
Max(`Extent5`.`id`) AS `A1` 
FROM (SELECT 
     `jos_community_msg_recepient`.`bcc`, 
     `jos_community_msg_recepient`.`deleted`, 
     `jos_community_msg_recepient`.`is_read`, 
     `jos_community_msg_recepient`.`msg_from`, 
     `jos_community_msg_recepient`.`msg_id`, 
     `jos_community_msg_recepient`.`msg_parent`, 
     `jos_community_msg_recepient`.`to` 
     FROM `jos_community_msg_recepient` AS `jos_community_msg_recepient`) AS `Extent4` INNER JOIN `jos_community_msg` AS `Extent5` ON (`Extent4`.`msg_id` = `Extent5`.`id`) OR ((`Extent4`.`msg_id` IS NULL) AND (`Extent5`.`id` IS NULL)) 
WHERE ((`Extent4`.`to` = 62) AND (0 = (`Extent5`.`deleted`))) AND ((`Extent5`.`parent` = `Project2`.`parent`) OR ((`Extent5`.`parent` IS NULL) AND (`Project2`.`parent` IS NULL)))) AS `C1` 
FROM (SELECT 
62 AS `p__linq__5`, 
`Distinct1`.`parent` 
FROM (SELECT DISTINCT 
`Extent3`.`parent` 
FROM (SELECT 
     `jos_community_msg_recepient`.`bcc`, 
     `jos_community_msg_recepient`.`deleted`, 
     `jos_community_msg_recepient`.`is_read`, 
     `jos_community_msg_recepient`.`msg_from`, 
     `jos_community_msg_recepient`.`msg_id`, 
     `jos_community_msg_recepient`.`msg_parent`, 
     `jos_community_msg_recepient`.`to` 
     FROM `jos_community_msg_recepient` AS `jos_community_msg_recepient`) AS `Extent2` INNER JOIN `jos_community_msg` AS `Extent3` ON (`Extent2`.`msg_id` = `Extent3`.`id`) OR ((`Extent2`.`msg_id` IS NULL) AND (`Extent3`.`id` IS NULL)) 
WHERE (`Extent2`.`to` = 62) AND (0 = (`Extent3`.`deleted`))) AS `Distinct1`) AS `Project2`) AS `Project3` ON (`Extent1`.`id` = `Project3`.`C1`) OR ((`Extent1`.`id` IS NULL) AND (`Project3`.`C1` IS NULL)) INNER JOIN (SELECT 
     `jos_community_msg_recepient`.`bcc`, 
     `jos_community_msg_recepient`.`deleted`, 
     `jos_community_msg_recepient`.`is_read`, 
     `jos_community_msg_recepient`.`msg_from`, 
     `jos_community_msg_recepient`.`msg_id`, 
     `jos_community_msg_recepient`.`msg_parent`, 
     `jos_community_msg_recepient`.`to` 
     FROM `jos_community_msg_recepient` AS `jos_community_msg_recepient`) AS `Extent6` ON (`Project3`.`C1` = `Extent6`.`msg_id`) OR ((`Project3`.`C1` IS NULL) AND (`Extent6`.`msg_id` IS NULL)) 
ORDER BY 
`id` DESC LIMIT 0,16) AS `Limit1`; 

下面是從MySQL的解釋:

1 PRIMARY <derived2> ALL     16 
    2 DERIVED <derived3> ALL     55 Using temporary; Using filesort 
    2 DERIVED Extent1 eq_ref PRIMARY PRIMARY 4 Project3.C1 1 Using where 
    2 DERIVED <derived9> ALL     333 Using where; Using join buffer 
    9 DERIVED jos_community_msg_recepient ALL     333 
    3 DERIVED <derived6> ALL     55 
    6 DERIVED <derived7> ALL     55 
    7 DERIVED <derived8> ALL     333 Using where; Using temporary 
    7 DERIVED Extent3 eq_ref PRIMARY,deleted PRIMARY 4 Extent2.msg_id 1 Using where; Distinct 
    8 DERIVED jos_community_msg_recepient ALL     333 
    4 DEPENDENT SUBQUERY Extent5 ref PRIMARY,parent,deleted parent 4 Project2.parent 2 Using where 
    4 DEPENDENT SUBQUERY <derived5> ALL     333 Using where; Using join buffer 
    5 DERIVED jos_community_msg_recepient ALL     333 

回答

0

你的LINQ查詢看起來不錯。您使用投影到DTO(MessageHeaderItem),它允許LINQ to Entities創建非常優化的查詢。但是,您應該使用SQL事件探查器來檢查執行的實際SQL查詢。也許LINQ to Entities在封面下引發了很多疑問。您也可能需要進行一些索引調整。將執行的查詢從SQL分析器複製到SQL調整嚮導(SQL Management Studio的一部分),並查看它提供的建議。

+0

這實際上是與MySQL。我將使用慢查詢日誌中的計數部分更新我的帖子。也許我需要在LINQ中改變一些東西。 – GregInWI2 2010-09-20 16:25:09

+0

對不起。雖然您當然不能使用Microsoft工具,但我的建議仍然存在。找出執行確切的查詢並查看問題出在哪裏。你將不得不分析這個。也有用於MySql的工具。 – Steven 2010-09-20 16:31:33

+0

對,我明白這一點。這些信息現在可以在這裏找到。我很想知道我可以在LINQ中更改哪些內容,以便生成更好的查詢。如果什麼都不能改變,那麼我想我必須使用動態SQL或過程。 – GregInWI2 2010-09-20 16:39:37