Являются ли вызовы базы данных Mutliple действительно значимыми с сетевым вызовом для веб-API?

16

У одного из моих работодателей мы работали над API REST (но это также относится и к SOAP). Клиент, который является пользовательским интерфейсом приложения, будет выполнять вызовы через Интернет (локальная сеть в типичных производственных развертываниях) в API. API будет делать вызовы в базу данных.

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

Но так ли это важно? Учтите, что пользовательский интерфейс должен выполнить сетевой вызов API; это довольно большой (порядка миллисекунд). Базы данных оптимизированы для того, чтобы хранить вещи в памяти и выполнять чтение очень и очень быстро (например, SQL Server загружает и сохраняет все в оперативной памяти и потребляет почти всю свободную оперативную память, если это возможно).

TLDR: действительно ли важно беспокоиться о множественных вызовах базы данных, когда мы уже совершаем сетевой вызов по локальной сети? Если так, то почему?

Чтобы было ясно, я говорю о порядке величины - я знаю, что это зависит от специфики (аппаратное обеспечение, выбор API и БД и т. Д.) Если у меня есть вызов, который занимает O (миллисекунды), выполняет оптимизацию для БД звонки, которые принимают на порядок меньше, на самом деле имеют значение? Или проблема не в этом?

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

ashes999
источник
Разве нет другого сетевого вызова между уровнем API и базой данных?
Знак
4
Что показали твои временные тесты?
Дэн Пичельман,
@Sign Нет сетевого вызова между API и БД. Они гарантированно будут на одной машине, насколько я понимаю.
ashes999
@ DanPichelman, это то, что я тоже спрашиваю. Никто, кажется, не берет и рассчитывает производительность; мы просто получаем требования «исправить производительность в X, объединив все вызовы БД в один вызов».
ashes999

Ответы:

25

Но так ли это важно? Учтите, что пользовательский интерфейс должен выполнить сетевой вызов API; это довольно большой (порядка миллисекунд). Базы данных оптимизированы для того, чтобы хранить вещи в памяти и выполнять чтение очень и очень быстро (например, SQL Server загружает и сохраняет все в оперативной памяти и потребляет почти всю свободную оперативную память, если это возможно).

Логика

В теории вы правы. Однако есть несколько недостатков с этим обоснованием:

  1. Из того, что вы заявили, неясно, действительно ли вы тестировали / профилировали свое приложение. Другими словами, знаете ли вы , что передача по сети из приложения в API - самый медленный компонент? Поскольку это интуитивно понятно, легко предположить, что это так. Тем не менее, при обсуждении производительности, вы никогда не должны предполагать. У моего работодателя я лидер по производительности. Когда я впервые присоединился, люди продолжали говорить о CDN, репликации и т. Д., Основываясь на интуиции о том, какими должны быть узкие места. Оказывается, самыми большими проблемами с производительностью были плохо выполняемые запросы к базе данных.

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

  3. Такое мышление предполагает один процесс за раз, или, другими словами, без параллелизма. Предполагается, что один запрос не может влиять на выполнение другого запроса. Совместно используемые ресурсы, такие как дисковый ввод-вывод, пропускная способность сети, пулы соединений, память, циклы ЦП и т. Д. Следовательно, сокращение использования общего ресурса одним вызовом базы данных может предотвратить замедление других запросов. Когда я впервые присоединился к своему нынешнему работодателю, руководство полагало, что настройка 3-секундного запроса к базе данных была пустой тратой времени. 3 секунды это так мало, зачем тратить на это время? Разве нам не лучше с CDN или компрессией или чем-то еще? Но если я смогу выполнить 3-секундный запрос за 1 секунду, скажем, с помощью добавления индекса, это на 2/3 меньше блокирования, на 2/3 меньше времени, занимаемого потоком, и, что более важно, меньше данных, считываемых с диска,

Теория

Существует распространенное мнение, что производительность программного обеспечения зависит только от скорости .

С точки зрения скорости, вы правы. Система работает так же быстро, как ее самый медленный компонент. Если вы профилировали свой код и обнаружили, что Интернет - самый медленный компонент, то все остальное, очевидно, не самая медленная часть.

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

Предположения

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

Откуда это чертовски

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

Вот почему:

  1. Базы данных отлично подходят для чтения данных. Это двигатели хранения. Однако ваша бизнес-логика живет в вашем приложении. Если вы создаете правило, согласно которому каждый вызов API приводит к одному вызову базы данных, ваша бизнес-логика может оказаться в базе данных. Может быть, это нормально. Многие системы делают это. Но некоторые этого не делают. Это о гибкости.
  2. Иногда, чтобы добиться хорошего разделения, нужно разделить 2 вызова базы данных. Например, возможно, каждый HTTP-запрос направляется через общий фильтр безопасности, который проверяет из БД, что у пользователя есть права доступа. Если это так, перейдите к выполнению соответствующей функции для этого URL. Эта функция может взаимодействовать с базой данных.
  3. Вызов базы данных в цикле. Вот почему я спросил, сколько это кратно. В приведенном выше примере у вас будет 2 вызова базы данных. 2 в порядке. 3 может быть хорошо. N не в порядке. Если вы вызываете базу данных в цикле, вы теперь сделаете производительность линейной, что означает, что она будет занимать больше времени, чем больше входных данных цикла. Строго говоря, говоря, что время сети API является самым медленным, полностью игнорирует аномалии, например, 1% вашего трафика, что занимает много времени из-за еще не обнаруженного цикла, который вызывает базу данных 10000 раз.
  4. Иногда есть вещи, в которых ваше приложение лучше, например, сложные вычисления. Возможно, вам потребуется прочитать некоторые данные из базы данных, выполнить некоторые вычисления, а затем на основе результатов передать параметр во второй вызов базы данных (возможно, чтобы записать некоторые результаты). Если вы объединяете их в один вызов (например, хранимую процедуру) только ради одного вызова базы данных, вы заставляете себя использовать базу данных для чего-то, в чем сервер приложений мог бы быть лучше.
  5. Балансировка нагрузки: у вас есть 1 база данных (предположительно) и несколько серверов приложений с балансировкой нагрузки. Следовательно, чем больше работы выполняет приложение и чем меньше база данных, тем легче ее масштабировать, поскольку обычно проще добавить сервер приложений, чем настроить репликацию базы данных. Исходя из предыдущего пункта, может иметь смысл выполнить SQL-запрос, затем выполнить все вычисления в приложении, которое распределено по нескольким серверам, и затем записать результаты после завершения. Это может повысить пропускную способность (даже если общее время транзакции одинаково).

TL; DR

TLDR: действительно ли важно беспокоиться о множественных вызовах базы данных, когда мы уже совершаем сетевой вызов по локальной сети? Если так, то почему?

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

Brandon
источник
3

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

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

brianfeucht
источник
1
Это хороший комментарий о нашей практике низкой производительности, но он не отвечает на мой вопрос о том, стоит ли беспокоиться о вызовах БД, когда у меня уже есть сетевой вызов.
ashes999
1
В общем, я обнаружил, что выполнение нескольких вызовов базы данных не представляет проблемы. В основном это связано с пулами соединений и небольшой задержкой между БД и веб-сервером. Есть момент, когда выполнение нескольких вызовов БД отрицательно скажется на производительности, но у меня нет для вас жесткого числа. Все зависит от среды и приложения. Только измерение даст вам ответ, который вы ищете.
Brianfeucht
Это не должно (обязательно) зависеть от специфики, потому что я говорю о порядке величины.
ashes999
Грубые предположения (нужно измерить): Среднее время подключения к БД с веб-сервера: 2 мс. Среднее время подключения к веб-серверу с клиента: 20 мс. Таким образом, если предположить, что числа, которые я случайно выбрал из эфира, верны, вы можете сделать 10 вызовы базы данных за время, необходимое для выполнения одного вызова веб-службы. Предполагая, что запросы к базе данных занимают одинаковое количество времени. Эти цифры чрезвычайно зависят от окружающей среды. Если клиент, выполняющий вызов веб-службы, является локальным, он может упасть на несколько порядков.
Brianfeucht
2

Мы не можем вам сказать.

Мы не знаем, как выглядят ваши запросы. Мы не знаем, сколько времени они занимают. Мы не знаем, сколько накладных расходов связано с каждым запросом к вашему API-серверу. Мы не знаем, как географически рассредоточены ваши клиенты. И т.п.

Если это сценарий, который требует оптимизации, и в котором вы можете решить, следует ли разделить или объединить вызовы вместе, вам нужно сравнить его двумя способами : решить, для чего вы оптимизируете (задержка пользовательского интерфейса, загрузка ЦП сервера, конкуренция, и т. д.) и выберите тот, который лучше достигает цели оптимизации.


Кроме того, я могу добавить с относительной уверенностью только одно :

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

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

svidgen
источник
1

Две мысли:

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

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

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

Ричард
источник
Я согласен с вами по поводу измерения производительности, что, похоже, никто не делает. Там нет никаких доказательств того, что это быстрее, но это просто продолжает появляться. Производительность возникает как проблема, когда у нас есть несколько вызовов, которые могут сделать, скажем, 1000 дБ SELECTс.
ashes999
@ ashes999, несмотря на то, что вы можете набирать скорость, глядя на количество вызовов в дБ, это, скорее всего, встречается в стратегии индексирования и т. д., а не в количестве вызовов. Как все указали, посмотрите на данные о производительности.
Ричард
Ричард, я согласен, и я действительно знаю это. Мой вопрос заключается в том, почему разные люди постоянно поднимают вопрос о том, что «множественные вызовы БД медленны», когда речь идет о сетевом вызове. Я действительно не понимаю, как это может быть значительным.
ashes999
@ ashes999 Извините, может быть, вам стоит немного подробнее рассказать о сетевом вызове, так как это кажется очевидным, я чувствую, что есть немного больше в вашем вопросе. Я чувствую, что мы что-то упускаем в ваших вопросах. Вы всегда будете испытывать некоторую задержку в сети, и каждый вызов потенциально увеличивается в «х» раз для каждого вызова (в простых терминах). Утверждение с номиналом является истинным, несколько сетевых вызовов будут выполняться медленнее, чем один сетевой вызов БД. Вот почему я предлагаю один вызов хранимой процедуры, которая может сделать несколько вызовов в БД без вызовов нескольких сетей.
Ричард
1

Если база данных находится на сервере, отличном от вашей службы REST, каждый вызов базы данных будет приводить к передаче данных в обе стороны, что может значительно снизить производительность:

Однажды я наблюдал, как один вызов веб-сервиса транслировался примерно в 500 запросов к базе данных - вряд ли это было проблемой, когда и веб-сервис, и база данных расположены на одном компьютере, но время отклика составляло 6-7 секунд, когда они находились на разных машины.

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

Астропоезд
источник
1

У нас есть пара приложений, которые очень, очень болтливы. Существует база данных для каждого. Не замужем. Маленький. Вещь. Обслуживание справочных данных снова и снова и снова является основной частью рабочей нагрузки в системе. Все это планирование рабочих потоков, получение и удаление блокировок, проверка кэша плана и т. Д. Суммируются, даже если нет фактического дискового ввода-вывода. Конфликт выше, потому что транзакции должны удерживать блокировки между несколькими вызовами БД, поэтому пропускная способность намного ниже, чем могла бы быть. Этим командам сейчас приходится покупать новые, очень дорогие серверы БД из-за этого.

Таким образом, хотя большая часть прошедшего времени в текущей конфигурации вашей системы занята вызовами API REST, игнорирование производительности на уровне БД сохраняет проблемы на будущее.

Майкл Грин
источник
0

Представленный путь оптимизации - просто неправильный взгляд на вещи.

Вызовы API должны быть атомарными. Другими словами, я должен быть в состоянии сделать 1 вызов веб-API для выполнения нужного мне действия. Будь то выборка данных, обновление записи или что-то еще. НИКОГДА не следует предпринимать более одного звонка, чтобы вызвать действие. И попытки использовать транзакции между несколькими вызовами следует избегать, как чума.

Иногда одно действие довольно сложно. Например, выборка данных, которые объединяются из нескольких источников: опять же, это должен быть один вызов. Либо все работает, либо все не работает.

Теперь сказать, что один вызов API должен выполнять только один запрос к БД, немного глупо. Как вы указали, накладные расходы на распределение вызовов по сети часто на порядок дороже с точки зрения общего времени.

Я могу несколько понять их утверждение, что один запрос может выполняться быстрее, чем несколько; но это создает ложное впечатление, поскольку игнорирует общую БД и нагрузку на сеть. Только путем профилирования различных способов извлечения данных из БД вы можете понять, в чем на самом деле проблема. Я уверен, что у каждого есть история, когда определенный запрос, выполненный в 100 раз чаще, чем ожидалось, убивал систему, пока не был установлен правильный индекс ...

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

Не я
источник