Оптимизация моей таблицы и / или запроса Azure SQL PaaS для повышения производительности

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

Мой сценарий создания таблицы в настоящее время выглядит так:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

IF NOT EXISTS (select * from sysobjects where name='records' and xtype='U')
CREATE TABLE [dbo].[records](
    [TripID] varchar(255) NOT NULL,
    [RecordTimeUTC] datetime2(0) NOT NULL,
    [TimeOfDaySeconds] [int] NOT NULL,
    [T0Latitude] [float] NOT NULL,
    [T0Longitude] [float] NOT NULL,
    [T1Latitude] [float] NULL,
    [T1Longitude] [float] NULL,
    [T2Latitude] [float] NULL,
    [T2Longitude] [float] NULL,
    [T3Latitude] [float] NULL,
    [T3Longitude] [float] NULL,
    [T4Latitude] [float] NULL,
    [T4Longitude] [float] NULL,
    [T5Latitude] [float] NULL,
    [T5Longitude] [float] NULL,
    [VehicleID] [int] NULL,
    [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
) ON [PRIMARY]
GO

IF NOT EXISTS (select * from sys.indexes where name='TripIDRecordTimeIndex' and object_id = OBJECT_ID('dbo.records'))
    CREATE INDEX TripIDRecordTimeIndex ON records (TripID, RecordTimeUTC desc)
GO

IF NOT EXISTS (select * from sys.indexes where name='TripIDIndex' and object_id = OBJECT_ID('dbo.records'))
    CREATE INDEX TripIDIndex ON records (TripID)
GO

IF NOT EXISTS (select * from sys.indexes where name='RecordTimeUTCIndex' and object_id = OBJECT_ID('dbo.records'))
    CREATE INDEX RecordTimeUTCIndex ON records (RecordTimeUTC desc)
GO

IF NOT EXISTS (select  * from sys.objects where name like 'UniqueConstraint2' and parent_object_id = OBJECT_ID('dbo.records'))
    ALTER TABLE [dbo].[records] ADD CONSTRAINT UniqueConstraint2 UNIQUE(VehicleID, RecordTimeUTC desc);
GO

IF NOT EXISTS (select * from sys.indexes where name='VehicleIDIndex' and object_id = OBJECT_ID('dbo.records'))
    CREATE INDEX VehicleIDIndex ON records (VehicleID)
GO

В настоящее время в моей таблице около 60 миллионов записей, размер которой меньше 50 ГБ.

Запрос на извлечение данных занимает очень много времени. В настоящее время это занимает более часа. Я не уверен, что основной причиной является дизайн моей таблицы или дизайн запроса (хотя, вероятно, и то, и другое).

Мне нужно извлечь последнее количество X элементов для каждого TripID в указанном мной наборе TripID. Существует около 10 тысяч различных идентификаторов, и я обычно хочу запросить примерно половину из них. X также различается между ними, поэтому мой лучший способ запроса в настоящее время - сгенерировать скрипт, который выглядит примерно так:

SELECT rs.* FROM (SELECT *, ROW_NUMBER() over (Partition BY TripID ORDER BY RecordTimeUTC DESC ) AS Rank FROM records where TripID in (20141000,20441000,30011022,30011021,30011008,30012029,30012028,30012027,30011007,30011019,30011018,30012026,30012025,30012024,30011017,30011016,30012023,30012022,30011015,30011014,30012021,30012020,30011013,30011012,30013000,30013001,30013019,30013009,30011011,30011010,30011009,30013008,30013007,30012010,30012009,30013005,30013004,30013003,30012014,30012019,30013021,30013020,30011006,30011004,30012018,30012017,30012016,30013006,30011003,30011002,30012015,30012013,30013013,30013002,30011001,30011000,30011020,30012012,30012011,30011005,30011030,30012001,30012008,30012007,30011029,30011028,30012006,30012005,30011031,30011027,30012004,30012003,30011026,30011025,30011024,30012002,30012000,30012031,30011023,30012030,30015005,30016006,30016013,30016012,30014020,30014019,30014018,30016011,30016010,30014017,30014016,30016009,30016008,30014015,30014013,30014012,30016005,30016004,30016003,30014010,30014009,30016002,30016001,30014008,30014007,30016000,30016007,30014006,30014005,30014004,30014003,30014002,30014001,30014000,30014023,30014014,30015012,30015004,30015003,30013018,30013017,30015002,30015001,30013016,30013015,30013014,30015000,30015013,30015011,30013012,30013011,30015010,30015009,30013010,30014011,30015008,30015007,30014022,30014021,30015006,33651001,33661006)) rs WHERE Rank <= 690
 UNION 
SELECT rs.* FROM (SELECT *, ROW_NUMBER() over (Partition BY TripID ORDER BY RecordTimeUTC DESC ) AS Rank FROM records where TripID in (20431003,20431002,20431001,20432003,20432002,20432001,30221001,33861002,33861003)) rs WHERE Rank <= 855
 UNION 
SELECT rs.* FROM (SELECT *, ROW_NUMBER() over (Partition BY TripID ORDER BY RecordTimeUTC DESC ) AS Rank FROM records where TripID in (20171029,20171030,20002002,26122001)) rs WHERE Rank <= 45
 UNION 
...

(Приведенный выше запрос возвращает 690 экземпляров каждой поездки в первом списке, 855 экземпляров каждого во втором, 45 в третьем и т. Д. Запрос намного больше этого - это всего лишь его фрагмент. В общем, я извлечь 10-15 миллионов строк)

Как уже упоминалось, моя работа ужасна. Это облачная штука? Это дизайн? Стоит ли использовать кластерный индекс? (Пробовал это для TripID, но это было еще хуже). Могу я как-нибудь улучшить свой запрос? Извлечь такое же количество экземпляров для каждого идентификатора, например, а затем отфильтровать?

Я заметил, что у меня есть пара дополнительных индексов, которые могут не использоваться в моем запросе. Я просто попытался добавить еще, так как производительность вставки не является проблемой. В моем запросе планировалось использовать TripIDRecordTimeIndex.

Даже при масштабировании плана данных в Azure SQL до S7 (800 DPU) я не могу заставить это работать быстро. Любая обратная связь приветствуется.

Изменить: я недавно изменил TripID с int на varchar (255) - это нарушит мою производительность?

Edit2: План выполнения:  фрагмент плана выполнения

Ссылка для скачивания полного плана выполнения

Edit3: Выяснили, что добавление кавычек ('') вокруг идентификаторов TripID, которые я запрашиваю, значительно улучшило производительность!

Edit4: Я добавил индекс, как это было предложено TheGameiswar - и разница в день и ночь! Спасибо! Прилагается новый план выполнения.

Новый план выполнения


person WPFUser    schedule 20.11.2017    source источник
comment
Это очень широкий вопрос. Для начала смотрели ли вы на план выполнения SQL для своих запросов?   -  person Rob Reagan    schedule 20.11.2017
comment
Я очень мало знаю об этой области. Я добавил копию плана выполнения для части плана выполнения в конце вопроса. Я понимаю, что это широкий вопрос, но причина в том, что я не знаю, где искать. Спасибо за ваш отзыв.   -  person WPFUser    schedule 20.11.2017
comment
Изображение плана - это не план запроса. Вам необходимо указать ссылку на файл: meta.stackexchange.com/questions/47689/   -  person Grant Fritchey    schedule 20.11.2017
comment
Общая идея при изучении плана выполнения - увидеть шаги, которые занимают больше всего времени в вашем запросе. В вашем случае есть два сканирования кластерного индекса. Поскольку в вашей таблице 60 миллионов строк, это будет больно. Вы можете прочитать о поиске по индексу и сканировании здесь: blog.sqlauthority.com/2007/03/30/   -  person Rob Reagan    schedule 20.11.2017
comment
Вы полностью просматриваете свои таблицы, а затем фильтруете их. Производительность будет только хуже. Вы должны иметь возможность фильтровать данные перед их извлечением. Однако вы говорите, что требования должны иметь доступ к половине данных. Это всегда приводит к сканированию. Это проблема дизайна, а не облака.   -  person Grant Fritchey    schedule 20.11.2017
comment
И еще один момент: если данные являются целыми числами, сохраните их как целое число, а не как VARCHAR (255). Это для строк, как это предложение, а не для чисел.   -  person Grant Fritchey    schedule 20.11.2017
comment
Добавлена ​​ссылка на полный тариф (правда 25 мб ..). Я узнаю больше о поиске и сканировании. Спасибо за ссылку @RobReagan.   -  person WPFUser    schedule 20.11.2017
comment
@GrantFritchey На самом деле мне не нужен доступ к половине данных. Только последние ~ 1k записей для каждой из примерно половины всех уникальных TripID в моей таблице.   -  person WPFUser    schedule 20.11.2017
comment
@GrantFritchey Сначала считалось, что данные являются целыми числами, но недавно я обнаружил данные, которые могут быть varchar, поэтому я предпочитаю обрабатывать оба случая. Если это сильно повлияет на производительность, я могу его переделать.   -  person WPFUser    schedule 20.11.2017


Ответы (1)


SELECT rs.* FROM (SELECT *, 
ROW_NUMBER() over (Partition BY TripID ORDER BY RecordTimeUTC DESC ) 
AS Rank FROM records where TripID in (20141000,20441000,30011022,30011021,30011008,30012029,30012028,30012027,30011007,30011019,30011018, 30012026,30012025.....)) rs WHERE Rank <= 690

Индексы, которые у вас есть, бесполезны для следующей части запроса ...

SELECT *, 
ROW_NUMBER() over (Partition BY TripID ORDER BY RecordTimeUTC DESC ) 
AS Rank FROM records where TripID in

Я бы создал индекс, как показано ниже

create index nci_sometst on table (tripid,recorddatetime)
include(<remaining columsn you are selecting>)

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

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

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

введите здесь описание изображения

Даже без предупреждений о преобразовании типов данных ваш запрос не будет использовать какой-либо из эффективных индексов.

person TheGameiswar    schedule 20.11.2017
comment
Это абсолютно помогло! На создание потребовалось время, но как только все запросы будут созданы, все запросы будут практически мгновенными! Спасибо! PS: Я запрашиваю каждый TripID только один раз, поэтому не думаю, что временная таблица помогла бы. - person WPFUser; 21.11.2017
comment
Рад, что это помогло вам, если вы можете предоставить более подробную информацию, которая будет полезна и в будущем .._ 1_ - person TheGameiswar; 21.11.2017