Общие сведения о производительности
Производительность базы данных — это обширная и сложная тема, охватывающая весь стек компонентов: базу данных, сетевые подключения, драйвер базы данных и уровни доступа к данным, такие как EF Core. Хотя слои высокого уровня и технологии ORM, такие как EF Core значительно упрощают разработку приложений и повышают удобство сопровождения, они могут быть непрозрачными, скрывая внутренние данные, которые критически важны с точки зрения производительности, например, выполняемые SQL-запросы. В этом разделе приводятся общие сведения о том, как обеспечить высокую производительность с помощью EF Core и как избежать распространенных ошибок, которые могут снизить производительность приложения.
Выявление узких мест и принятие мер для их устранения
Как и в случае с производительностью, важно не стремиться выполнять оптимизацию без наличия данных, свидетельствующих о проблеме; как однажды заметил великий Дональд Кнут, "Преждевременная оптимизация — корень всех зол". В разделе Диагностика производительности обсуждаются различные способы определения того, на что именно приложение тратит время в логике работы с базой данных и как именно выявить конкретные проблемные области. После выявления запроса, который выполняется длительное время, можно предложить различные варианты решения проблемы. Есть ли в вашей базе данных индекс? Следует ли попробовать другие шаблоны запросов?
Всегда проверяйте производительность кода и возможные альтернативы самостоятельно. В разделе "Диагностика производительности" есть пример тестирования производительности с использованием BenchmarkDotNet, который можно использовать в качестве шаблона для собственных тестов производительности. Не думайте, что универсальные и общедоступные тесты производительности могут быть применены к вашему решению как есть; на выбор оптимального решения могут значительно влиять самые разные факторы, такие как задержка базы данных, сложность запросов и фактические объемы данных в таблицах. Например, многие общедоступные тесты производительности выполняются в идеальных сетевых условиях, в которых задержка базы данных практически равна нулю, и с очень легкими запросами, которые не требуют почти никакой обработки (или дисковых операций ввода-вывода) на стороне базы данных. Несмотря на то что такие тесты производительности полезны для сравнения издержек во время выполнения для различных уровней доступа к данным, различия, которые позволяют выявить эти тесты, обычно незаметны в реальных приложениях, в которых на базу данных ложится настоящая нагрузка, а задержка базы данных представляет собой значительный фактор, влияющий на производительность.
Аспекты производительности при доступе к данным
Общую производительность при доступе к данным можно разделить на следующие обширные категории:
- Чистая производительность базы данных. В реляционной базе данных EF преобразует запросы LINQ приложения в инструкции SQL, выполняемые базой данных; эти инструкции SQL могут выполняться более или менее эффективно. Нужный индекс в нужном месте может существенно увеличить производительность при выполнении инструкций SQL, а перезапись запроса LINQ может привести к улучшению SQL-запроса, создаваемого EF.
- Передача данных по сети. Как и в случае любой сетевой системы, важно ограничить объем данных, передаваемых по сети. Ограничение объема данных означает, что вы не только отправляете и загружаете только те данные, которые действительно необходимы, но и избегаете так называемого эффекта "картезианского взрыва" при загрузке связанных сущностей.
- Сетевые циклы. Помимо объема данных, передаваемых по сети, свой вклад вносят и сетевые циклы, так как время, необходимое для выполнения запроса в базе данных, может быть увеличено на время, требуемое для передачи пакетов между приложением и базой данных. Дополнительные затраты на сетевые циклы в значительной степени зависят от среды. Чем дальше расположен сервер базы данных, тем больше задержка и тем больше затрат влечет за собой каждый цикл. С появлением облака приложения все чаще находятся далеко от базы данных, и "общительные" приложения, которые выполняют слишком много циклов, испытывают снижение производительности. Таким образом, важно понимать, когда именно ваше приложение обращается к базе данных, сколько обращений выполняется и можно ли уменьшить это число.
- Накладные расходы среды выполнения EF. Наконец, EF добавляет некоторые затраты на среду выполнения в операции базы данных: EF необходимо скомпилировать запросы из LINQ to SQL (хотя это обычно следует сделать только один раз), отслеживание изменений добавляет некоторые издержки (но может быть отключено) и т. д. На практике затраты EF для реальных приложений, скорее всего, будут незначительными в большинстве случаев, так как время выполнения запросов в базе данных и задержке сети доминирует общее время; но важно понять, какие варианты и как избежать некоторых ошибок.
Узнайте, что происходит внутри
EF позволяет разработчикам сосредоточиться на бизнес-логике путем создания SQL-запросов, материализации результатов и выполнения других задач. Как и любой другой слой или абстракция, EF также позволяет скрыть то, что происходит внутри, например, выполнение фактических SQL-запросов. Производительность не обязательно является критически важным аспектом каждого существующего приложения, но в приложениях, в которых она имеет значение, крайне важно, чтобы разработчик понимал, что именно делает EF, и проверял получающиеся SQL-запросы, соблюдал циклы, чтобы не допустить появления проблемы N+1 и т. д.
Кэш за пределами базы данных
Наконец, самый эффективный способ взаимодействия с базой данных — не взаимодействовать с ней совсем. Другими словами, если доступ к базе данных является узким местом производительности в приложении, может быть целесообразно кэшировать определенные результаты за пределами базы данных, чтобы максимально сократить время выполнения запросов. Хотя кэширование увеличивает сложность, это особенно важный компонент любого масштабируемого приложения: хотя уровень приложения можно легко масштабировать путем добавления дополнительных серверов для обработки повышенной нагрузки, масштабирование уровня базы данных обычно осуществляется намного сложнее.