Оптимальная стратегия для повышения производительности в операциях поиска - SQL Server 2008 - PullRequest
0 голосов
/ 10 октября 2011

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

[user] (approx. 40,000 rows, 37 cols):

id               INT (pk)
content_group_id INT (fk)
[username]       VARCHAR(20)
...

[content_group] (approx. 200,000 rows, 5 cols):

id    INT (pk)
title VARCHAR(20)
...

[content] (approx. 1,000,000 rows, 12 cols):

id                  INT (pk)
content_group_id    INT (fk)
content_type_id     INT (fk)
content_sub_type_id INT (fk)
...

[content_type] (2 rows, 3 cols)

id INT (pk)
...

[content_sub_type] (8 rows, 3 cols)
id              INT (pk)
content_type_id INT (fk)
...

Мы ожидаем, что количество этих строк значительно возрастет (в частности, user, content_group и таблицы содержимого). Да, в пользовательской таблице довольно много столбцов - и мы определили некоторые, которые можно переместить в другие таблицы. Есть также несколько индексов, которые мы применили к затронутым таблицам, которые помогли.

Большие проблемы с производительностью - это хранимые процедуры, которые мы используем для поиска пользователей (которые включают присоединения к таблице содержимого в поле content_group_id). Мы пытались изменить предложения WHERE и AND, используя различные подходы, и мы думаем, что получили их настолько хорошо, насколько можем, но все же это слишком медленно.

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

Итак, каковы наши варианты? Мы можем думать о нескольких, но все приходят с плюсами и минусами:

Денормализация структуры таблицы

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

Плюсы:

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

Минусы:

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

Больше денормализации структуры таблицы

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

Плюсы:

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

Против

  • То же, что и выше: дополнительные поля для поддержки в пользовательской таблице, изменения в SQL и коде веб-сайта.

Создание промежуточного слоя индексации

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

Плюсы:

  • Это хорошее долгосрочное решение. Lucene существует для улучшения производительности поисковой системы.

Минусы:

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

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

Есть ли другие подходы, которые могут работать для нас? Есть ли какие-либо дополнительные плюсы и / или минусы с подходами, которые я изложил выше, которые могут повлиять на наши решения?

1 Ответ

1 голос
/ 10 октября 2011

поиск не кластеризованного индекса из таблицы содержимого с использованием content_sub_type_id. Затем следует Hash Match на content_group_id для таблицы содержимого

Это описание будет означать, что ваш дорогой запрос фильтрует таблицу content на основе полей из content_type:

select ...
from content c
join content_type ct on c.content_type_id = ct.id
where ct.<field> = <value>;

Этот дизайн таблицы и возникающая в результате проблема, на самом деле, довольно распространены. Проблемы возникают в основном из-за очень низкой селективности справочных таблиц (content_type имеет 2 строки, поэтому селективность content_type_id в содержимом, вероятно, составляет 50%, огромна). Есть несколько решений, которые вы можете попробовать:

1) Организовать таблицу content в кластеризованном индексе с content_type_id в качестве ведущего ключа. Это позволило бы объединению выполнять сканирование диапазона, а также избежать поиска ключа / закладки для полноты проекции. Изменение кластерного индекса может повлиять на другие запросы, поэтому его необходимо тщательно проверить. Очевидно, что первичный ключ в content должен быть принудительно введен с некластеризованным ограничением.

2) Предварительно прочитайте значение content_type_id и затем сформулируйте запрос без объединения между content и content_type:

select ...
from content c
where c.content_type_id = @contentTypeId;

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

3) Денормализовать content_Type в контент. Вы упоминаете денормализацию, но ваше предложение денормализовать контент для пользователей не имеет большого смысла для меня. Удалите таблицу content_type, вставьте поля content_type в саму таблицу content и решите все проблемы денормализации.

4) Предварительное объединение в материализованном виде. Вы говорите, что уже пробовали это, но я сомневаюсь, что вы пробовали правильное материализованное представление. Вам также нужно понимать, что только Enterprise Edition автоматически использует материализованный индекс представления, для всех других выпусков требуется подсказка NOEXPAND :

create view vwContentType 
with schemabinding
as 
select content_type_id, content_id
from dbo.content c
join dbo.content_type_id ct on c.content_type_id = ct.content_type_id;

create unique clustered index cdxContentType on vwContentType (content_type_id, content_id);

select ...
from content c
join vwContentType ct with (noexpand)
on ct.content_id = c.content_id
where ct.content_type_id = @contentTypeId;

Решения 2), 3) и 4) в основном академические. Учитывая очень низкую селективность content_type_id, ваше единственное решение, которое имеет постоянный шанс, - сделать его ведущим ключом в кластерном индексе content. Я не расширил анализ до content_Sub_type, но я готов поспорить, что только с 8 строками у него та же самая проблема, которая потребует его вставки также в кластерный индекс (возможно, как второй ведущий ключ).

...