Где делать объединения - на сервере базы данных или на сервере приложений? - PullRequest
3 голосов
/ 11 марта 2009

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

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

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

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

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

Ответы [ 7 ]

4 голосов
/ 11 марта 2009

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

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

Чтобы показать альтернативный сценарий (в пользу СУБД), рассмотрим случай, когда T1 имеет N1 = 1M строк ширины W1 = 100, а T2 имеет N2 = 100K строк ширины 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 до сервера приложений. Если вам нужно только подмножество столбцов, может быть другой набор сбережений. И СУБД, как правило, имеют довольно умные пакеты сортировки; вам понадобится хороший пакет сортировки на вашем сервере приложений для представления данных в правильном порядке.

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

2 голосов
/ 11 марта 2009

В общем, я рассматриваю ряд моментов, когда говорю о масштабе:

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

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

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

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

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

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

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

1 голос
/ 11 марта 2009

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

1 голос
/ 11 марта 2009

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

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

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

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

1 голос
/ 11 марта 2009

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

0 голосов
/ 17 апреля 2009

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

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

0 голосов
/ 17 апреля 2009

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

...