Что определяет «слишком много запросов к базе данных» в коде?


17

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

В основном это сводится к следующим двум мнениям о вызовах базы данных: 1. Сделайте один большой вызов, чтобы получить все, что может понадобиться для сокращения базы данных, количество вызовов БД. 2. Выполните меньшие отдельные вызовы, основываясь на том, что требуется для уменьшения размера. Вызовы БД

Где это особенно важно, это общий код. Мы будем использовать пример класса Employee, так как это довольно просто.

Предположим, что у вашего класса Employee есть 10 атрибутов значений (имя, фамилия, адресат и т. Д.), А затем 2 атрибута класса ... 1, указывающий на класс Department, а затем 1 супервизор, указывающий на другой объект Employee.

В мышлении № 1 вы должны сделать один вызов, который возвращает данные о сотрудниках, а также поля, необходимые для заполнения атрибутов Department и Supervisor ... или, по крайней мере, поля, которые чаще всего используются из этих подобъектов.

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

Позиция 2 довольно прямолинейна ... минимизируйте размер запросов и количество объектов базы данных, которые необходимо обрабатывать каждый раз, когда выполняется один из этих запросов. Позиция # 1 состоит в том, что даже если бы это могло быть реализовано должным образом, сам факт, что код должен был бы сделать многократные соединения, вызовет большую нагрузку на соединение между веб-сервером и базой данных, а не уменьшит его.

Движущая сила исследований заключается в том, что объем трафика между нашим веб-сервером и сервером базы данных выходит из-под контроля.


7
По моему опыту нет «правильного ответа» на это. Существует баланс между задержкой и пропускной способностью. Низкая задержка может выдержать множество маленьких запросов или даже один большой; однако, ссылки с высокой задержкой имеют тенденцию быть лучше перемещая много данных сразу. Тем не менее, если пропускная способность низкая в конфигурации с высокой задержкой, вам лучше выбирать меньшие порции, чтобы быть более отзывчивыми.

3
Вероятно, связано с n + 1 проблемой stackoverflow.com/questions/97197/…
Валера Колупаев

@Valera: для удобства вот ссылка, размещенная на этот вопрос: realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects
rwong

4
«Объем трафика между нашим веб-сервером и сервером базы данных выходит из-под контроля». Что это обозначает? Можете ли вы быть конкретным в том, что реальная проблема? У вас есть проблемы с производительностью? Вы сделали профилирование и измерение? Пожалуйста, предоставьте фактические результаты реальных измерений как часть вопроса. В противном случае мы просто догадываемся.
С.Лотт

Ответы:


8

Если движущей силой этого вопроса является слишком большой трафик, изучали ли вы кеширование часто используемых объектов? Например: ПОСЛЕ того, как вы получаете объекты Employee, Department и Supervisor, возможно, было бы неплохо добавить их в кэш, чтобы в случае их повторного запроса в ближайшем будущем они уже находились в кэше и не нуждались в извлечении. опять таки. Конечно, кэш должен позволить истечь редко используемым объектам, а также должен иметь возможность удалять объекты, которые были изменены приложением и сохранены обратно в базу данных.

В зависимости от того, какой язык и фреймворки вы используете, уже может существовать фреймворк для кеширования, который может выполнять некоторые (или большинство) из того, что вам нужно. Если вы используете Java, вы можете заглянуть в Apache Commons-Cache (я не использовал его некоторое время, и, хотя он выглядит бездействующим, он все еще доступен для использования, и он был довольно приличным в последний раз, когда я его использовал).


3

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

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


Что представляет собой плохо написанный вызов БД?
Ню Эверест

3

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


3

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

  1. Мера в первую очередь! Я даже не буду смотреть на код, который «может быть медленным», если я на самом деле не вижу трафик, идущий к этому ресурсу, и этот ресурс медленно реагирует.
  2. 1 запрос = K запросов. Количество обращений к базе данных полностью определяется типом запрашиваемого ресурса; и никогда по характеру запроса или состояния этого ресурса; В вашем примере это, вероятно, не более 3 запросов: 1 для сотрудников, 1 для отделов и 1 для руководителей; Неважно, сколько там каждого.
  3. Не спрашивайте, что вы не будете использовать . Если речь идет об HTTP, то нет смысла запрашивать данные на потом; нет позже; каждый запрос начинается с чистого листа. Иногда мне нужно большинство столбцов из таблицы, но иногда мне нужен только один или два; когда я точно знаю, какие поля мне нужны, я попрошу об этом.
  4. Кидай железо в проблему. Серверы дешевы; Иногда вы можете получить достаточную производительность, просто переместив базу данных в более удобное окно; или отправка некоторых запросов в реплику только для чтения.
  5. Сначала аннулируйте кеш, затем реализуйте кеширование. Сильное желание поместить часто используемые или трудные для запроса данные в кеш; но слишком часто исключаются неиспользуемые данные или устаревшие замененные данные. Если вы знаете, как извлечь данные из кэша; тогда вы можете положить его в кеш; Если оказывается, что сделать кэш недействительным дороже, чем просто выполнить запрос; тогда вам не нужен кеш.

2

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

Один звонок для всех 3 объектов:

  • будет работать быстрее
  • получит именно то, что вам нужно в том случае, если вам это нужно
  • вероятно, будет использоваться только в одном случае (хотя это может быть очень распространенным случаем)
  • будет сложнее поддерживать
  • придется обслуживать чаще (так как он изменится, если какая-либо из трех схем объекта или необходимые данные изменятся)

Один звонок на объект (всего 3 звонка)

  • Дает вам универсальный вызов для заполнения одного экземпляра каждого типа объекта; затем они могут использоваться независимо
  • Будет более понятным, так как структура запроса будет проще.
  • Будет медленнее (не обязательно в 3 раза медленнее, но накладные расходы увеличиваются для тех же данных)
  • Может вызвать проблемы с извлечением ненужных данных (извлечение всей записи, когда вам нужно одно поле, расточительно)
  • Может вызвать проблемы N + 1, когда существует отношение многие-к-одному, если запрос на одну запись отправляется N раз, по одному на запись в коллекции.

В ответ на пару ваших вопросов (№ 3 и 5 во втором списке) ... Что, если руководитель и отдел используются только 1/3 (или меньше) времени? Что, если код был разработан, чтобы получить все дочерние элементы, как только объект List <> был закодирован, чтобы содержать их в первый раз? ... это облегчит большую часть осторожности?
user107775 26.09.11

Если вспомогательные объекты нужны лишь редко, то в общем случае это будет выполняться быстрее (меньше данных для извлечения), но в худшем случае будет медленнее (те же данные или больше извлекаются, используя три раза накладные расходы связи с вашего компьютера). Что касается проблемы N + 1, вам просто нужно иметь возможность спроектировать запрос, который извлекает список объектов, чтобы можно было сделать это на основе внешнего ключа на «одну» сторону отношения, а затем вытянуть несколько строк. из результата запроса. Вы не можете использовать версию запроса, которая должна иметь первичный ключ записи.
KeithS

1

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

Поэтому, если вам не нужны данные, не тратьте их на память, чтобы избежать повторного путешествия позже. Но если вам нужен объем данных, вы должны минимизировать количество обращений к БД.

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

РЕДАКТИРОВАТЬ: Имейте в виду, что этот курс зависит также от вашей ситуации. Например, если это WebApp, у вас должны быть другие соображения, чем для настольного приложения, которое обращается к БД в вашей сети, а не через Интернет для WepApp.


Как насчет того, если вы пишете общий код, и вы не уверены в том, каким образом ваш код будет использоваться. Возможно, вы бы никогда не представили, что кому-то не нужен Supervisor, но оказывается, что приложение, над которым вы работаете, является единственным, которое нуждается в нем. Конечно, вы могли бы написать отдельные функции ... одну, чтобы не включать ее, а другую, чтобы включить ее, но в какой момент ваш общий код начинает требовать слишком много подробных знаний для использования?
user107775 26.09.11

@ user107775 Я обычно пишу только две функции для каждого случая; один, который возвращает только значения свойств, и другой, который возвращает класс со всеми связанными классами. Это потому, что большинство раз вам нужны только свойства. Таким образом, вам не нужно детальное знание, только один получает основы, а другой все. Я считаю, что это разумный баланс. (Однако в некоторых особых случаях требуется дополнительная оптимизация, но это в каждом конкретном случае).
AJC

1

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

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

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

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


1

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

Было бы интересно узнать, сколько из этого исходит от памяти БД против диска. Нет ничего, что могло бы заставить меня почувствовать, что отдел более или менее вероятно изменится по сравнению с адресом.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.