Где делать объединения - на сервере базы данных или на сервере приложений?

StackOverflow https://stackoverflow.com/questions/633211

Вопрос

На данный момент я сталкиваюсь с проблемой производительности (позже это может привести к проблеме масштабирования).Приложение, над которым я работаю, довольно сложное, и оно работает на SQL Server 2005.Мне нужно объединить 6-7 таблиц, чтобы получить нужные данные.На данный момент каждая таблица содержит более 100 000 строк данных.Схема базы данных не может быть изменена (должна оставаться как есть).Поэтому я могу только попытаться оптимизировать, насколько это возможно.2 вещи приходят мне в голову:

  • Старайтесь не подключаться к базе данных и позволить серверу приложений выполнять фильтрацию с помощью LINQ:

    • Плюсы:вы сможете легко масштабироваться, добавив больше серверов приложений.
    • Минусы:больше усилий;Я не уверен, что скорость реагирования снизится.
  • Сервер приложений остается как есть и старается максимально оптимизировать SQL-запрос (больше индексов, чаще перестраивать индекс и т.д.):

    • Плюсы:минимальные усилия
    • Минусы:когда записи в таблице станут больше, проблема вернется снова

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

Это было полезно?

Решение

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

Теперь, если вы собираетесь выполнить перекрестное произведение двух широких таблиц (допустим, что они представляют собой T1 с N1 строками шириной W1 и T2 с N2 строками шириной W2) без фильтрации, тогда СУБД обязана создать и отправить N1 * N2 * (W1 + W2) байт данных по сети, тогда как вы могли бы использовать таблицы отдельно в виде N1 * W1 + N2 * W2 байт данных.Если N1 = N2 = 1 М и W1 = W2 = 100, то это 200 ТБ против 200 МБ передачи данных в пользу кросс-продукта на сервере приложений.Но это не совсем справедливо по отношению к СУБД.Большинство запросов не так уж глупы - они объединяются по столбцам и применяют условия, и оптимизатор СУБД будет изо всех сил (и автоматически) стараться минимизировать проделанную работу.Кроме того, он отправит вам обратно только соответствующие данные;ему не обязательно отправлять все строки, которые не соответствуют вашим критериям.

Чтобы показать альтернативный сценарий (в пользу СУБД), рассмотрим случай, когда T1 имеет N1 = 1 МЛН строк шириной W1 = 100, но T2 имеет N2 = 100 тыс. строк шириной W2 = 50.Существует соединение между двумя таблицами в целочисленном столбце, и, следовательно, в T1 имеется 10 строк для каждой из них в T2.Предположим, что вы отсасываете все T1 и T2 на сервер приложений:для этого требуется N1 * W1 + N2 * W2 = 105 МБ данных.Но условия фильтрации ограничивают данные 1/10 строк в T2, и для каждой строки в T1, которая соответствует строке в T2, фактически существует только 2 строки, которые соответствуют условиям фильтрации.Теперь СУБД будет только передавать N2 * (W1 + W2) / 5 = 3 МБ, что позволяет сэкономить более 100 МБ при передаче данных СУБД.Теперь, если вам удастся проявить смекалку и загрузить только N2 * W2 / 10 = 500 КБ данных, соответствующих значениям в T2, вам все равно придется заставить СУБД выполнить "полусоединение" T1 к значениям, которые вы хотите, чтобы получить правильные строки из T1 на сервер приложений.Если вам нужно только подмножество столбцов, может быть другой набор сбережений.И СУБД, как правило, имеют довольно умные пакеты сортировки;вам понадобится хороший пакет сортировки на вашем сервере приложений, чтобы представить данные в правильном порядке.

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

Другие советы

В целом, я учитываю ряд моментов, когда говорю о масштабе:

  1. Как часто это выполняется?Для запросов с менее частым доступом вы можете смириться с некоторым снижением производительности.

  2. Каковы темпы роста / изменений?Если записи в некоторых из этих таблиц относительно статичны, вы можете рассмотреть возможность кэширования содержимого извне в файле типа dbm (или любом другом эквиваленте Windows).Есть также такие вещи, как memcache, на которые, возможно, стоит обратить внимание.Однако это может быть возможно, а может и не быть.Это основано на выполнении "объединений" в коде приложения.

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

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

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

Как только у вас будет информация о том, что на самом деле делает оптимизатор запросов с запросом, и у вас будут все ваши индексы / статистика / выбор столбцов, у вас будет лучшее представление о том, куда двигаться дальше.Если вы сделаете все возможное в базе данных, вам придется подумать об использовании кэшей данных и делать такие вещи, как переход к меньшему количеству таблиц с более конкретным предложением / better where .

Отказ от ответственности:У меня нет непосредственного опыта работы с SQL Server, но у меня есть большой опыт работы с другими СУБД (Oracle, MySQL, PostgreSQL и т.д.) и архитектурой в целом.

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

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

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

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

Как следует из рекомендаций Microsoft (а также других производителей БД) относительно объединений - используйте их как можно оптимальнее.По моему опыту - более 2-3 объединений в топе для сложных отборов.

Вы упоминаете, что каждая таблица содержит "более 100 000 строк", но вы не упоминаете, сколько данных вы выбираете и насколько сложным является объединение.100 ТЫСЯЧ строк - это не большой для правильно настроенного и проиндексированного SQLServer.У нас есть 17-сторонние соединения, которые возвращают результаты за несколько мс, но они хорошо проиндексированы и выделяют несколько строк.Я бы посмотрел на информацию о профилировании на SQLServer, прежде чем приступать к редизайну вашего приложения.

Не пренебрегайте накладными расходами на передачу данных между серверами.Ethernet довольно быстро ухудшается под нагрузкой (я думаю, что устойчивая скорость передачи составляет что-то около 30% от скорости передачи одного пакета;т. е. ваша ссылка со скоростью 100 Мбит / сек на самом деле будет пропускать только 30 Мб интенсивного трафика).Как только вы разместите свою ссылку на сервере базы данных, добавление дополнительных серверов приложений не будет иметь значения, потому что вы не сможете быстрее выводить данные.

Объединения на сервере приложений также ставят вас во власть самого медленного из них.Мы посмотрели performance tank на клиентском сайте и обнаружили, что произошел сбой основного сервера приложений, и стратегия восстановления клиента заключалась в том, чтобы перевести компьютер в режим сбоя на виртуальную машину, работающую на одном из других серверов.Своего рода изящное решение, но, конечно, не такое эффективное.Я также видел замедления, когда маршрутизаторы выходят из строя, и внезапно все ваши одноранговые серверы оказываются на расстоянии трех или четырех переходов вместо того, чтобы находиться в одной подсети.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top