我正在分析来自在线游戏的数百万场比赛的球员数据。我正在尝试以块的形式将数据分页到内存中以减少加载时间,但使用带有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
答案 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();
另请参阅查询计划以了解数据库如何执行查询,正在使用的连接类型等。也许您的原始查询根本不利用排序。