Что быстрее, один большой запрос или много маленьких запросов?

68

Я работал на разные компании, и я заметил, что некоторые из них предпочитают иметь представления, которые присоединятся к столу со всеми его «родственниками». Но затем в приложении несколько раз нам нужно использовать только 1 столбец.

Так будет ли быстрее просто делать простые выборки, а затем «присоединять» их к системному коду?

Система может быть php, java, asp, любой язык, который подключается к базе данных.

Таким образом, вопрос заключается в том, что быстрее перейти от серверной части (php, java, asp, ruby, python ...) к базе данных, выполнить один запрос, чтобы получить все необходимое, или перейти со стороны сервера в базу данных и запустить запрос, который только получить столбцы из одной таблицы в то время?

sudo.ie
источник
2
Какую реализацию «SQL» вы используете? MySQL, Microsoft SQL Server, Oracle, Postgresql и т. Д.? Пожалуйста, обновите ваш тег.
RLF
1
Mysql и Postgresql
sudo.ie
6
Мой опыт показывает, что MySQL не любит сложные запросы и обычно быстрее с очень простыми запросами (но больше). Оптимизатор запросов Postgres намного лучше, и там, как правило, эффективнее выполнять один большой запрос.
a_horse_with_no_name
3
@a_horse_with_no_name Это очень широкое обобщение, особенно в контексте этого вопроса. Оптимизатор MySQL действительно очень прост по своей конструкции и может вызвать проблемы с объединениями и подзапросами, особенно в старых версиях MySQL, которые в противном случае приводят к более быстрым планам в PostgreSQL, в то время как MySQL может быть очень быстрым для чистой загрузки OLTP. Однако в контексте вопроса один большой запрос будет быстрее, скажем, в худшем из возможных сценариев, SELECT внутри цикла программирования (независимо от используемой СУБД).
января
2
@jynus: ну, вопрос является очень широким (плюс я сказал: «в моем опыте» , - другие люди могут иметь различный опыт). Запрос внутри LOOP никогда не является хорошей идеей и почти всегда является результатом плохого дизайна или отсутствия понимания, как работать с реляционной базой данных.
a_horse_with_no_name

Ответы:

69

Что бы ответить на ваш вопрос - тема «СОВМЕСТНОЕ ДЕКОМПОЗИЦИЯ».

Согласно странице 209 Книги

Высокая производительность MySQL

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

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

Вы можете запустить эти запросы:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

С какой стати ты это сделал? На первый взгляд это выглядит расточительно, потому что вы увеличили количество запросов, не получив ничего взамен. Однако такая реструктуризация может дать существенные преимущества в производительности:

  • Кэширование может быть более эффективным. Многие приложения кэшируют «объекты», которые отображаются непосредственно в таблицы. В этом примере, если объект с тегом mysqlуже кэширован, приложение пропустит первый запрос. Если вы найдете в кэше сообщения с идентификатором 123, 567 или 908, вы можете удалить их из IN()списка. Кэш запросов также может извлечь выгоду из этой стратегии. Если часто изменяется только одна из таблиц, декомпозиция объединения может уменьшить количество недействительных кэшей.
  • Выполнение запросов по отдельности может иногда уменьшить конкуренцию за блокировку
  • Выполнение объединений в приложении облегчает масштабирование базы данных путем размещения таблиц на разных серверах.
  • Сами запросы могут быть более эффективными. В этом примере использование IN()списка вместо объединения позволяет MySQL сортировать идентификаторы строк и извлекать строки более оптимально, чем это возможно при объединении.
  • Вы можете уменьшить избыточный доступ к строкам. Выполнение объединения в приложении означает получение каждой строки только один раз, тогда как объединение в запросе - это, по сути, денормализация, которая может многократно обращаться к одним и тем же данным. По той же причине такая реструктуризация может также уменьшить общий сетевой трафик и использование памяти.
  • В некоторой степени вы можете рассматривать эту технику как реализацию хэш-соединения вручную вместо алгоритма вложенных циклов, который MySQL использует для выполнения соединения. Хеш-соединение может быть более эффективным.

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

НАБЛЮДЕНИЯ

Мне нравится первый пункт, потому что InnoDB немного неуклюж, когда проверяет кеш запросов.

Что касается последнего пункта, я написал сообщение от 11 марта 2013 г. ( есть ли разница в выполнении между условием JOIN и условием WHERE? ), В котором описывается алгоритм вложенного цикла. Прочитав его, вы увидите, насколько хорошей может быть декомпозиция соединения.

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

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

Для тех разработчиков, которые готовы ...

ДАЙТЕ ЭТО ПОПРОБУЙТЕ !!!

RolandoMySQLDBA
источник
3
Что касается этой ссылки об изменении на 3 запроса ... Я знаю и уважаю Барона, Вадима и Питера, но я не согласен с этим вводящим в заблуждение предложением. Большинство аргументов в пользу расставания настолько редки, что о них не стоит упоминать. Придерживайтесь одного запроса с JOINs, а затем давайте работать над его улучшением.
Рик Джеймс
2
@RickJames Я согласен с духом вашего комментария. На протяжении многих лет я видел работу по разложению соединений для одних и неудачу для других. Даже при правильном наборе навыков SQL это может сработать против вас, если декомпозиция соединения не будет выполнена правильно. У моего нынешнего работодателя многие сотрудники любят увеличивать и уменьшать масштаб, особенно когда используется устаревший код и имеются глубокие карманы. С теми, у кого вкус икры, но яичный салат, разложение соединения может стоить риска, но должно быть сделано правильно.
RolandoMySQLDBA
Мне бы очень хотелось посмотреть, как это работает в среде Oracle, если бы у меня были права и время.
Рик Хендерсон
Еще один способ сделать это может быть быстрее, это то, что если вы делаете упорядочивание, то для упорядочения меньших списков будет меньше вычислений, чем для упорядочения одного большого списка.
Эван Сироки
24

В Postgres (и, вероятно, в любой СУБД в аналогичной степени, MySQL в меньшей степени) почти все запросы выполняются намного быстрее.

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

Не говоря уже о дополнительной работе, которую нужно выполнить в клиенте, объединяя результаты, которые обычно гораздо медленнее. СУБД специализируется на таких задачах, а операции основаны на исходных типах данных. Запрещается textприводить к промежуточным результатам или возвращать их для собственных типов клиентов, что может даже привести к менее правильным (или неправильным!) Результатам. Подумайте о числах с плавающей точкой ...

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

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

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

Связанный вопрос по SO:

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

Эрвин Брандштеттер
источник
Просто из любопытства, что ты считаешь очень большим ?
Саблефост
@Sablefoste: очень многое зависит от ваших шаблонов доступа. Критическая точка - это когда параллельные транзакции начинают стоять в очереди, ожидая снятия блокировок. Или, если вы накапливаете достаточно блокировок, чтобы съесть значительную часть ваших ресурсов. Или, если ваши запросы выполняются достаточно долго, чтобы помешать автовакууму ...
Эрвин Брандштеттер
Но если мы возьмем несколько типичную ситуацию - запрос, который использует внешнее соединение и возвращает много избыточных данных для «родительской» таблицы, которая затем должна быть проанализирована и отсортирована приложением (скорее всего, некоторой библиотекой ORM) по сравнению с small select, который сначала выбирает все требуемые идентификаторы, а затем другой, меньший, с IN () вместо внешнего соединения? Не будет ли второй подход более эффективным (учитывая, что процессор и приложение потребляют процессор и пропускную способность связи)?
JustAMartin
1
@JustAMartin: Это похоже на запрос, который почти наверняка быстрее обрабатывается планировщиком запросов СУБД - при условии правильных запросов. Относительно returns lots of redundant data for "parent" table: Зачем вам возвращать избыточные данные? Верните только те данные, которые вам нужны.
Эрвин Брандштеттер
1
При внешнем объединении RDBMS возвращает данные из родительской таблицы, дублированные для каждого присоединенного дочернего элемента, что означает некоторые накладные расходы на сеть и память, а затем дополнительный анализ в инструменте ORM для удаления дублирующих родительских значений и сохранения только одного родителя с n дочерними элементами. Таким образом, с помощью одного запроса мы экономим на эффективной работе планировщика запросов СУБД, уменьшаем сетевые (или локальные каналы) запросы, но теряем дополнительные ненужные данные и перемещаем данные в библиотеке ORM. Я думаю, это как всегда - измерить перед оптимизацией.
JustAMartin