Лучший способ улучшить производительность в приложении WPF - PullRequest
0 голосов
/ 21 января 2019

В настоящее время я работаю над приложением WPF , которое было построено с использованием структуры сущностей для доступа к данным ( База данных SQL Server ) (сначала база данных).

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

Однако база данных теперь находится на другом сервере, который работает медленнее. Как вы уже догадались, у приложения теперь большие проблемы с производительностью (более 10 секунд для загрузки десятка строк, то же самое для вставки новых строк, ...).

  • Стоит ли мне продолжать работу со структурой сущностей, но попытаться повысить производительность, изменив базу данных, добавив представления и хранимую процедуру?
  • Должен ли я избавиться от структуры сущностей и использовать только «базовый» код (и одновременно улучшить базу данных)?
  • Есть ли простой ORM, который я мог бы использовать вместо EF?

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

База данных довольно простая (около 10 таблиц), единственное, что может усложнить, - это то, что я храню там файлы. Так что я не уверен, что могу действительно использовать все, что захочу. И я не знаю, важно ли это, но мне нужно отобразить довольно много вычисляемых полей. Любой совет?

Не стесняйтесь задавать любые соответствующие вопросы.

Ответы [ 3 ]

0 голосов
/ 21 января 2019

Основываясь на комментариях, я рискну предположить, что это в основном проблема пропускной способности.

У вас было приложение, которое работало нормально, когда оно находилось в одном месте, возможно, один коммутатор, гигабитный Ethernet и 200 м кабелей.

Теперь, когда приложение пытается отправить или получить данные на / с удаленного сервера, возможно, через общедоступный Интернет через неизвестное количество внутренних прокси-серверов в конфликте с тем, кто знает, что такое другой трафик, и оно не работает так же хорошо.

Вы также упоминаете, что храните файлы в базе данных, и ваша схема имеет такие поля, как Attachment.data и Doc.file_content. Это говорит о том, что вы можете пытаться передавать большие объемы (возможно, мегабайты) данных для простого запроса, и именно здесь вы падаете.

Некоторые общие указатели:

  • Добавление индексов для всех мест, к которым вы присоединяетесь к таблицам или значениям, которые вы обычно запрашивает.
  • Помните о разнице между Ленивый и Стремительный загрузка в Entity Framework. Там нет правильного или неправильного ответа, но вы должны знать, что вы используете, и почему .
  • Разделение любого содержимого файла в свою таблицу с тем же первичным ключом, что и основная таблица или играть с различными классами EF, чтобы убедиться, что вы только получаете файлы когда вам нужно их использовать.
0 голосов
/ 22 января 2019

Для профилирования производительности первое место, которое я рекомендую посмотреть, это профилировщик SQL.Это может захватить точные операторы SQL, которые выполняет EF, и помочь определить возможные виновники производительности.Я расскажу о некоторых из них здесь .Вопросы Схемы, пожалуй, самое подходящее место для начала.Название предназначено для MVC, но большинство элементов относится к WPF и любому приложению.

Хороший, простой профилировщик, который я использую для SQL Server, - это ExpressProfiler.(https://github.com/OleksiiKovalov/expressprofiler)

С переходом на новый сервер и передачей данных по проводам, а не извлечением из локальной базы данных, проблемы производительности, которые вы заметили, скорее всего попадут в категорию«слишком много, слишком часто». Теперь вы будете не только ждать, пока база данных загрузит данные, но и упаковать их в пакет и отправить по проводам. Кроме того, новая база данных представляет собой то же самоеОбъем данных и обслуживать только одного клиента, или в настоящее время обслуживает несколько клиентов? Другие ловушки для разработчиков "работает на моем компьютере", когда локальные базы данных тестирования меньше и не имеют дело с параллельными запросами от сервера. (где блокировки и тому подобное могут повлиять на производительность)

Отсюда, запустите копию приложения с изолированным сервером базы данных (другие клиенты не используют его для уменьшения «шума») с запущенным на нем профилировщиком. На что обращать внимание:

Ленивая загрузка - это случаи, когда у вас есть запросы на загрузку данных, но затем вы видите много(от десятков до сотен) дополнительных запросов.Ваш код может сказать «запустить этот запрос и заполнить эти данные», который, как вы ожидаете, должен быть одним запросом SQL, но при касании лениво-загруженных свойств это может привести к появлению множества других запросов.Решение для отложенной загрузки: если вам нужны дополнительные данные, просто загрузите их с помощью .Include().Если вам нужны только некоторые данные, изучите использование .Select() для выбора моделей просмотра / DTO нужных вам данных, а не полагайтесь на полные сущности.Это исключит сценарии с отложенной загрузкой, но может потребовать некоторых значительных изменений в вашем коде для работы с моделями представления / dtos.Такие инструменты, как Automapper, могут сильно помочь здесь.Прочтите .ProjectTo(), чтобы узнать, как Automapper может работать с IQueryable для устранения отложенных загрузок.

Чтение слишком много - загрузка сущностей может быть дорогой, особенно если вам не нужны все эти данные.Преступления в отношении производительности включают чрезмерное использование .ToList(), которое материализует целые наборы сущностей, для которых требуется подмножество данных, или будет достаточно простой проверки или подсчета.Например, я видел код, который выполняет такие вещи:

var data = context.MyObjects.SingleOrDefault(x => x.IsActive && x.Id = someId);
return (data != null);

Это должно быть:

var isData = context.MyObjects.Where(x => x.IsActive && x.Id = someId).Any();
return isData;

Разница между ними заключается в том, что в первом примере EF будетэффективно выполнять операцию SELECT *, поэтому в случае, если данные присутствуют, они возвращают все столбцы в объект только для последующей проверки наличия объекта.Второй оператор выполнит более быстрый запрос, чтобы просто вернуть обратно, существует строка или нет.

var myDtos = context.MoyObjects.Where(x => x.IsActive && x.ParentId == parentId)
  .ToList()
  .Select( x => new ObjectDto
  {
    Id = x.Id,
    Name = x.FirstName + " " + x.LastName,
    Balance = calculateBalance(x.OrderItems.ToList()),
    Children = x.Children.ToList()
      .Select( c => new ChildDto  
      {
        Id = c.Id,
        Name = c.Name
      }).ToList()
  }).ToList();

Подобные выражения могут продолжаться и становиться довольно сложными, но настоящие проблемы - это .ToList () перед.Select ().Часто они закрадываются, потому что разработчики пытаются сделать что-то, чего не понимает EF, например, вызвать метод.(то есть CalculateBalance ()), и он «работает», сначала вызывая .ToList ().Проблема в том, что вы материализуете всю сущность в этот момент и переключаетесь на Linq2Object.Это означает, что любые «прикосновения» к связанным данным, таким как .Children, теперь будут вызывать ленивую загрузку, и снова дальнейшие вызовы .ToList() могут насыщать больше данных в памяти, которые в противном случае могли бы быть уменьшены в запросе.Виновник, за которым нужно следить, это .ToList() звонки и попытка их удаления.Выберите более простые значения перед вызовом .ToList (), а затем подайте эти данные в модели представлений, где модели представлений могут вычислять результирующие данные.

Худший виновник, подобный этому, я видел из-за того, что разработчик хотел использоватьфункция в предложении Where:

var data = context.MyObjects.ToList().Where(x => calculateBalance(x) > 0).ToList();

Этот первый оператор ToList() попытается насытить всю таблицу до объектов в памяти.Большое влияние на производительность помимо времени / памяти / пропускной способности, необходимых для загрузки всех этих данных, заключается просто в количестве блокировок, которые база данных должна сделать для надежного чтения / записи данных.Чем меньше строк вы «трогаете» и чем короче вы их касаетесь, тем приятнее ваши запросы будут проигрываться при одновременных операциях нескольких клиентов.Эти проблемы значительно усиливаются по мере того, как системы переходят на использование большим количеством пользователей.

При условии, что вы устранили лишние ленивые нагрузки и ненужные запросы, следующее, на что нужно обратить внимание, - это производительность запросов.Для операций, которые кажутся медленными, скопируйте оператор SQL из профилировщика и запустите его в базе данных, просматривая план выполнения.Это может дать подсказки об индексах, которые вы можете добавить для ускорения запросов.Опять же, использование .Select() может значительно повысить производительность запросов за счет более эффективного использования индексов и уменьшения объема данных, которые необходимо откатить серверу.

Для хранения файлов: хранятся ли они в виде столбцов в соответствующей таблице, илив отдельной таблице, которая связана с соответствующей записью?Под этим я подразумеваю, что если у вас есть запись счета-фактуры, а также копия файла счета-фактуры, сохраненная в базе данных, это:

счета-фактуры

  • InvoiceId
  • InvoiceNumber
  • ...
  • InvoiceFileData

или

Счета

  • InvoiceId
  • InvoiceNumber
  • ...

InvoiceFile

  • InvoiceId
  • InvoiceFileData

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

Если вы используете GUID для ключей (в отличие от целых / длинных), используете ли вы newsequentialid ()?(при условии, что SQL Server) Ключи настроены на использование newid () или в коде, Guid.New () приведет к фрагментации индекса и снижению производительности.Если вы заполняете идентификаторы через значения по умолчанию для базы данных, переключите их на использование newsequentialid (), чтобы уменьшить фрагментацию.Если вы заполняете идентификаторы с помощью кода, обратите внимание на написание генератора Guid, который имитирует newsequentialid () (SQL Server) или шаблон, подходящий для вашей базы данных.SQL Server и Oracle хранят / индексируют значения GUID по-разному, поэтому наличие статически-подобной части байтов UUID в старших и младших байтах данных будет способствовать повышению производительности индексирования.Также обратите внимание на обслуживание индекса и другие задания по обслуживанию базы данных, чтобы обеспечить эффективную работу сервера базы данных.

Когда дело доходит до настройки индекса, отчеты сервера базы данных - ваши друзья.После того, как вы удалили большинство или, по крайней мере, некоторых серьезных нарушителей производительности из своего кода, следующий шаг - это посмотреть на реальное использование вашей системы.Здесь лучше всего узнать, куда направить ваши исследования кода / индекса, - это наиболее часто используемые и проблемные запросы, которые определяет сервер базы данных.Там, где это EF-запросы, вы обычно можете провести обратный инжиниринг на основе тех таблиц, за которые отвечает EF-запрос.Захватите эти запросы и проведите их через план выполнения, чтобы увидеть, есть ли индекс, который может помочь.Индексирование - это то, что разработчики либо забывают, либо преждевременно беспокоятся.Слишком много индексов может быть так же плохо, как слишком мало.Я считаю, что лучше всего следить за использованием в реальных условиях, прежде чем принимать решение о том, какие индексы добавить.

Надеемся, что это должно дать вам представление о том, что нужно искать, и поднять скорость этой системы на ступеньку выше.:)

0 голосов
/ 21 января 2019

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

По моему опыту, структура сущностей - этоХороший вариант для такого рода приложений, но вам нужно понять, как это работает.

Кроме того, какую структуру сущностей вы используете?последняя версия 6.2 и имеет некоторые улучшения производительности, которых нет у olders, поэтому, если вы используете старую версию, я предлагаю обновить ее

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