Общие сведения о производительности

Производительность базы данных — это обширная и сложная тема, охватывающая весь стек компонентов: базу данных, сетевые подключения, драйвер базы данных и уровни доступа к данным, такие как EF Core. Хотя слои высокого уровня и технологии ORM, такие как EF Core значительно упрощают разработку приложений и повышают удобство сопровождения, они могут быть непрозрачными, скрывая внутренние данные, которые критически важны с точки зрения производительности, например, выполняемые SQL-запросы. В этом разделе приводятся общие сведения о том, как обеспечить высокую производительность с помощью EF Core и как избежать распространенных ошибок, которые могут снизить производительность приложения.

Выявление узких мест и принятие мер для их устранения

Как и в случае с производительностью, важно не стремиться выполнять оптимизацию без наличия данных, свидетельствующих о проблеме; как однажды заметил великий Дональд Кнут, "Преждевременная оптимизация — корень всех зол". В разделе Диагностика производительности обсуждаются различные способы определения того, на что именно приложение тратит время в логике работы с базой данных и как именно выявить конкретные проблемные области. После выявления запроса, который выполняется длительное время, можно предложить различные варианты решения проблемы. Есть ли в вашей базе данных индекс? Следует ли попробовать другие шаблоны запросов?

Всегда проверяйте производительность кода и возможные альтернативы самостоятельно. В разделе "Диагностика производительности" есть пример тестирования производительности с использованием BenchmarkDotNet, который можно использовать в качестве шаблона для собственных тестов производительности. Не думайте, что универсальные и общедоступные тесты производительности могут быть применены к вашему решению как есть; на выбор оптимального решения могут значительно влиять самые разные факторы, такие как задержка базы данных, сложность запросов и фактические объемы данных в таблицах. Например, многие общедоступные тесты производительности выполняются в идеальных сетевых условиях, в которых задержка базы данных практически равна нулю, и с очень легкими запросами, которые не требуют почти никакой обработки (или дисковых операций ввода-вывода) на стороне базы данных. Несмотря на то, что такие тесты производительности полезны для сравнения издержек во время выполнения для различных уровней доступа к данным, различия, которые позволяют выявить эти тесты, обычно незаметны в реальных приложениях, в которых на базу данных ложится настоящая нагрузка, а задержка базы данных представляет собой значительный фактор, влияющий на производительность.

Аспекты производительности при доступе к данным

Общую производительность при доступе к данным можно разделить на следующие обширные категории:

  • Чистая производительность базы данных. В реляционной базе данных EF преобразует запросы LINQ приложения в инструкции SQL, выполняемые базой данных; эти инструкции SQL могут выполняться более или менее эффективно. Нужный индекс в нужном месте может существенно увеличить производительность при выполнении инструкций SQL, а перезапись запроса LINQ может привести к улучшению SQL-запроса, создаваемого EF.
  • Передача данных по сети. Как и в случае любой сетевой системы, важно ограничить объем данных, передаваемых по сети. Ограничение объема данных означает, что вы не только отправляете и загружаете только те данные, которые действительно необходимы, но и избегаете так называемого эффекта "картезианского взрыва" при загрузке связанных сущностей.
  • Сетевые циклы. Помимо объема данных, передаваемых по сети, свой вклад вносят и сетевые циклы, так как время, необходимое для выполнения запроса в базе данных, может быть увеличено на время, требуемое для передачи пакетов между приложением и базой данных. Дополнительные затраты на сетевые циклы в значительной степени зависят от среды; чем дальше расположен сервер базы данных, тем выше задержка и тем больший вклад вносит каждый цикл. С появлением облака приложения все чаще находятся далеко от базы данных, и "общительные" приложения, которые выполняют слишком много циклов, испытывают снижение производительности. Таким образом, важно понимать, когда именно ваше приложение обращается к базе данных, сколько обращений выполняется и можно ли уменьшить это число.
  • Накладные расходы среды выполнения EF. Наконец, сама EF добавляет некоторые затраты времени выполнения к операциям базы данных: EF необходимо компилировать запросы из LINQ в SQL (хотя такая компиляция обычно выполняется только один раз), некоторые задержки возникают из-за отслеживания изменений (его можно отключить) и т. д. На практике накладные расходы EF для реальных приложений, скорее всего, будут незначительными в большинстве случаев, так как время выполнения запроса в базе данных и задержка сети оказывают основное влияние на суммарное время. При этом важно понимать, какие варианты действий у вас есть и как избежать некоторых ловушек.

Узнайте, что происходит внутри

EF позволяет разработчикам сосредоточиться на бизнес-логике путем создания SQL-запросов, материализации результатов и выполнения других задач. Как и любой другой слой или абстракция, EF также позволяет скрыть то, что происходит внутри, например, выполнение фактических SQL-запросов. Производительность не обязательно является критически важным аспектом каждого существующего приложения, но в приложениях, в которых она имеет значение, крайне важно, чтобы разработчик понимал, что именно делает EF, и проверял получающиеся SQL-запросы, соблюдал циклы, чтобы не допустить появления проблемы N+1 и т. д.

Кэш за пределами базы данных

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