Linq to Entity Paging With Large dataset太慢了

时间:2014-02-04 08:48:22

标签: c# mysql linq-to-entities

我正在分析来自在线游戏的数百万场比赛的球员数据。我正在尝试以块的形式将数据分页到内存中以减少加载时间,但使用带有skip / take的OrderBy需要花费太长时间(即使是较小的查询也需要20多分钟)。

这是我的疑问:

var playerMatches = (from p in context.PlayerMatchEntities
                     join m in context.MatchEntities 
                        on p.MatchId equals m.MatchId
                     where m.GameMode == (byte) gameMode
                        && m.LobbyType == (byte) lobbyType
                     select p)
                     .OrderBy(p => p.MatchId)
                     .Skip(page - 1 * pageSize)
                     .Take(pageSize)
                     .ToList();

MatchId已编入索引。

每场比赛有10名球员,我目前在PlayerMatch表中有330万场比赛,有3300万行,但数据正在不断收集。

有没有办法解决OrderBy造成的大幅性能下降?

This post类似,但似乎没有得到解决。

修改

这是生成的SQL查询:

SELECT
`Project1`.`AccountId`, 
`Project1`.`MatchId`, 
`Project1`.`PlayerSlot`, 
`Project1`.`HeroId`, 
`Project1`.`Item_0`, 
`Project1`.`Item_1`, 
`Project1`.`Item_2`, 
`Project1`.`Item_3`, 
`Project1`.`Item_4`, 
`Project1`.`Item_5`, 
`Project1`.`Kills`, 
`Project1`.`Deaths`, 
`Project1`.`Assists`, 
`Project1`.`LeaverStatus`, 
`Project1`.`Gold`, 
`Project1`.`GoldSpent`, 
`Project1`.`LastHits`, 
`Project1`.`Denies`, 
`Project1`.`GoldPerMin`, 
`Project1`.`XpPerMin`, 
`Project1`.`Level`, 
`Project1`.`HeroDamage`, 
`Project1`.`TowerDamage`, 
`Project1`.`HeroHealing`
FROM (SELECT
`Extent2`.`AccountId`, 
`Extent2`.`MatchId`, 
`Extent2`.`PlayerSlot`, 
`Extent2`.`HeroId`, 
`Extent2`.`Item_0`, 
`Extent2`.`Item_1`, 
`Extent2`.`Item_2`, 
`Extent2`.`Item_3`, 
`Extent2`.`Item_4`, 
`Extent2`.`Item_5`, 
`Extent2`.`Kills`, 
`Extent2`.`Deaths`, 
`Extent2`.`Assists`, 
`Extent2`.`LeaverStatus`, 
`Extent2`.`Gold`, 
`Extent2`.`GoldSpent`, 
`Extent2`.`LastHits`, 
`Extent2`.`Denies`, 
`Extent2`.`GoldPerMin`, 
`Extent2`.`XpPerMin`, 
`Extent2`.`Level`, 
`Extent2`.`HeroDamage`, 
`Extent2`.`TowerDamage`, 
`Extent2`.`HeroHealing`
FROM `match` AS `Extent1` INNER JOIN `playermatch` AS `Extent2` ON `Extent1`.`MatchId` = `Extent2`.`MatchId`
 WHERE ((`Extent1`.`GameMode`) = 2) AND ((`Extent1`.`LobbyType`) = 7)) AS `Project1`
 ORDER BY 
`Project1`.`MatchId` ASC LIMIT 0,1000

2 个答案:

答案 0 :(得分:1)

另一种方法可能是让VIEW执行连接并索引相应的列,然后创建一个使用VIEW的表值函数,并返回仅包含页面数据的TABLE。 您将不得不手动编写分页的SQL查询,但我认为它会更快。 我没有尝试过这样的东西,所以我不能确定它会有很大的提升速度。

答案 1 :(得分:0)

您没有提供足够的信息来帮助您,所以我建议。 避免顺序的一种方法是将行存储在订单中已有的表中。我建议'MatchId'是MatchEntities的主键和聚集索引。这意味着MatchEntities.MatchId存储物理排序。如果您切换连接流以首先拉出已排序的流,然后再添加添加流,则可以避免昂贵的排序。

像这样:

var playerMatches = (from m in context.MatchEntities     // note the switch: MatchEntities goes first
                 join p in context.PlayerMatchEntities
                    on p.MatchId equals m.MatchId
                 where m.GameMode == (byte) gameMode
                    && m.LobbyType == (byte) lobbyType
                 select p)
                 // .OrderBy(p => p.MatchId) // no need for this any more
                 .Skip(page - 1 * pageSize)
                 .Take(pageSize)
                 .ToList();

另请参阅查询计划以了解数据库如何执行查询,正在使用的连接类型等。也许您的原始查询根本不利用排序。