2010-09-20 65 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