Как лучше всего оптимизировать этот маленький кусочек кода C # Linq - PullRequest
1 голос
/ 10 января 2012

Если вы загляните на нашу домашнюю страницу аркады:

http://www.scirra.com/arcade

В правом верхнем углу есть поле с изображением последних людей, которые играли в эту игру.В профилировщике, который я использую, он показывает, что для его запуска требуется 900 мс, что составляет около 80% от общего времени загрузки страницы.

Запрос относительно прост:

// Recent players
using (MainContext db = new MainContext())
{
    var q = (from c in db.tblArcadeGamePlays
                join a in db.tblProfiles on c.UserID equals a.UserID
                where c.UserID != 0
                select new
                {
                    c.UserID,
                    c.tblForumAuthor.Username,
                    a.EmailAddress,
                    Date = (from d in db.tblArcadeGamePlays where d.UserID == c.UserID orderby d.Date descending select new { d.Date }).Take(1).Single().Date
                })
    .Distinct()
    .OrderByDescending(c => c.Date)
    .Take(16);

Ноон слишком медленный для моих нужд.

Выходной кэш для этого не подходит, потому что было бы неплохо, чтобы этот ящик работал в режиме реального времени.Кроме того, 900 мс при нормальной загрузке страницы слишком медленны даже для одного пользователя время от времени, поэтому мы хотели бы избежать этого, если это возможно.

У кого-нибудь есть какие-либо идеи о том, как я могу ускорить это?На данный момент две мои идеи состоят в том, чтобы иметь:

  • Новая таблица базы данных, в которой хранятся последние игроки, что устраняет необходимость в объединении
  • Поле, хранящееся где-то, содержащее HTML этогокоробка, каждая новая игра, которая случается, восстанавливает это поле
  • Комбинация обоих

Оба вида ужасны!Любая помощь приветствуется.

По запросу, результаты linqpad

Лямбда

TblArcadeGamePlays
   .Join (
      TblProfiles, 
      c => c.UserID, 
      a => a.UserID, 
      (c, a) => 
         new  
         {
            c = c, 
            a = a
         }
   )
   .Where (temp0 => (temp0.c.UserID != 0))
   .Select (
      temp0 => 
         new  
         {
            UserID = temp0.c.UserID, 
            Username = temp0.c.User.Username, 
            EmailAddress = temp0.a.EmailAddress, 
            Date = TblArcadeGamePlays
               .Where (d => (d.UserID == temp0.c.UserID))
               .OrderByDescending (d => d.Date)
               .Select (
                  d => 
                     new  
                     {
                        Date = d.Date
                     }
               )
               .Take (1)
               .Single ().Date
         }
   )
   .Distinct ()
   .OrderByDescending (c => c.Date)
   .Take (16)

SQL

-- Region Parameters
DECLARE @p0 Int = 0
-- EndRegion
SELECT TOP (16) [t6].[UserID], [t6].[Username], [t6].[EmailAddress], [t6].[value] AS [Date2]
FROM (
    SELECT DISTINCT [t5].[UserID], [t5].[Username], [t5].[EmailAddress], [t5].[value]
    FROM (
        SELECT [t0].[UserID], [t2].[Username], [t1].[EmailAddress], (
            SELECT [t4].[Date]
            FROM (
                SELECT TOP (1) [t3].[Date]
                FROM [tblArcadeGamePlays] AS [t3]
                WHERE [t3].[UserID] = [t0].[UserID]
                ORDER BY [t3].[Date] DESC
                ) AS [t4]
            ) AS [value]
        FROM [tblArcadeGamePlays] AS [t0]
        INNER JOIN [tblProfile] AS [t1] ON [t0].[UserID] = [t1].[UserID]
        INNER JOIN [tblForumAuthor] AS [t2] ON [t2].[Author_ID] = [t0].[UserID]
        ) AS [t5]
    WHERE [t5].[UserID] <> @p0
    ) AS [t6]
ORDER BY [t6].[value] DESC

План запроса

enter image description here

Ответы [ 4 ]

5 голосов
/ 10 января 2012

Я был бы готов поспорить, что довольно неплохо, что практически вся задержка, которую вы видите, связана с самой базой данных, а не с LINQ (делая это вопросом оптимизации базы данных, а не вопросом оптимизации LINQ).

Я бы использовал linqpad, чтобы посмотреть, какой запрос генерируется (см .: http://www.thereforesystems.com/view-t-sql-query-generated-by-linq-to-sql-using-linqpad/),, и опубликовать его здесь. План запроса от запуска этого запроса в SQL Management Studio (при условии, что вы используете SQL Сервер) также будет полезно.

ОК, учитывая изменения, попробуйте что-то вроде этого. Это должно значительно упростить запрос:

using (MainContext db = new MainContext())
{
    var latestIds = db.tblArcadeGamePlays.OrderByDescending(c => c.Date).Select(c => c.UserID).Distinct().Take(16); // These are the 16 most recent player Ids.
    // join them here to the rest of those player's data
    var playerData = ... // you'll need to fill in here some by filtering the other data you want using latestIds.Contains
}
1 голос
/ 10 января 2012

Возможно, это не проблема, но ваш запрос даты может быть упрощен до следующего:

Date = (from d in db.tblArcadeGamePlays 
        where d.UserID == c.UserID 
        orderby d.Date descending 
        select d.Date).First()

Возможно, круглый запрос сбил с толку оптимизатор. В противном случае я соглашаюсь с другими ответами, проверяю сгенерированный запрос SQL и проверяю ваши индексы.

Кроме того, вы уверены, что Distinct необходим и что он делает то, что вы думаете? Он будет отфильтровывать только дубликаты, имеющие одинаковое значение для каждого поля / столбца.

0 голосов
/ 10 января 2012

Запустите мастер настройки базы данных SQL Server (в SQL Management Studio / Tools) для вашей базы данных с этими запросами и позвольте ему создавать статистику и индексы для настройки вашей базы данных на производительность. Сколько людей порекомендует это? Это работает.

0 голосов
/ 10 января 2012

Убедитесь, что у вас есть индексы с обеих сторон объединения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...