Как я могу улучшить свой запрос на выборку для хранения больших версионных наборов данных? - PullRequest
0 голосов
/ 27 марта 2010

На работе мы создаем большие многостраничные веб-приложения, состоящие в основном из радио и флажков. Основная цель каждого приложения - сбор данных, но когда пользователи возвращаются на страницу, которую они посетили ранее, мы сообщаем им их предыдущие ответы. В худшем случае, у нас может быть до 900 различных переменных и около 1,5 миллионов пользователей.

По нескольким причинам имеет смысл использовать подход «только вставка» для хранения данных (в отличие от обновления на месте), чтобы мы могли фиксировать исторические данные о повторных взаимодействиях с переменными. В результате мы можем получить несколько ответов на пользователя для каждой переменной.

Наша таблица для сбора ответов выглядит примерно так:

CREATE TABLE [dbo].[results](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [userid] [int] NULL,
    [variable] [varchar](8) NULL,
    [value] [tinyint] NULL,
    [submitted] [smalldatetime] NULL)

Где id служит первичным ключом.

Практически каждый запрос приводит к серии операторов вставки (по одному на каждую переданную переменную), а затем мы запускаем команду select для получения предыдущих ответов для следующей страницы (что-то вроде этого):

SELECT t.id, t.variable, t.value
    FROM results t WITH (NOLOCK)
    WHERE t.userid = '2111846' AND 
    (t.variable='internat' OR t.variable='veteran' OR t.variable='athlete') AND
    t.id IN (SELECT MAX(id) AS id
        FROM results WITH (NOLOCK)
        WHERE userid = '2111846' AND (t.variable='internat' OR t.variable='veteran' OR t.variable='athlete')
        GROUP BY variable)

Который в этом случае будет возвращать самые последние ответы для переменных "internat", "veteran" и "athlete" для пользователя 2111846.

Мы следовали советам инструментов настройки базы данных при индексации таблиц, и, по нашим данным, это наиболее эффективная версия запроса на выборку, которую нам удалось найти. Несмотря на это, кажется, что при приближении к 1 миллиону записей наблюдается значительное снижение производительности (а это может быть примерно в 150 раз). У нас есть довольно элегантное решение для разделения данных между несколькими таблицами, которое работает довольно хорошо, но я открыт для любых советов о том, как я могу построить лучшую версию запроса select. Мы часто используем эту структуру для хранения большого количества независимых точек данных, и нам нравятся ее преимущества.

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

Заранее спасибо.

NB. Поскольку в этом случае мы подчеркиваем создание избыточного чтения и поскольку мы никогда не обновляемся на месте, кажется, что нет никакого наказания (и некоторого преимущества) за использование директивы NOLOCK в этом случае.

1 Ответ

3 голосов
/ 27 марта 2010

Не используйте подсказки NOLOCK. Вместо этого используйте изоляцию моментального снимка , просто включите READ_COMMITED_SNAPSHOT ON в базе данных.

Кроме того, вы говорите, что хотите «самый последний», но выбираете MAX по идентификатору, который является ключом IDENTITY. Разве вы не должны использовать дату и время для «недавних»? Идентификатор будет указывать только порядок вставки как «недавний», что может не иметь значения для модели предметной области.

Чтобы вернуть нужные данные, лучше всего создать соответствующий кластеризованный ключ:

CREATE TABLE [dbo].[results](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [userid] [int] NULL,
    [variable] [varchar](8) NULL,
    [value] [tinyint] NULL,
    [submitted] [smalldatetime] NULL);

create clustered index cdxResults on results ([userid], variable, id DESC);

Если у вас есть таблица возможных типов переменных, тогда запрос можно оптимизировать с помощью MAX внутри оператора APPLY:

SELECT t.id, t.variable, t.value
FROM ( 
  SELECT 'internat' as variable
  UNION ALL SELECT 'veteran'
  UNION ALL SELECT 'athlete'
) as v
CROSS APPLY (
   SELECT TOP(1) id, variable, value
   FROM results 
   WHERE userid = @userid
   AND variable = v.variable
   ORDER BY id DESC
) as t

В этом запросе будет выполнено 3 поиска в кластеризованном индексе, время ответа всегда будет постоянным O (1), независимо от размера данных. То есть. то же самое время для 100 строк в таблице 1 миллион или 1 миллиард.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...