Как разместить SQL-запрос Azure, который запускается нечасто, но требует значительных ресурсов - PullRequest
0 голосов
/ 26 апреля 2018

ПРИМЕЧАНИЕ. Я привожу здесь подробные сведения о настройке Azure, но я не уверен, что это будет решение на основе Azure. Это может быть проблема, которая может быть решена на уровне C #, Entity Framework или SQL.

У меня есть веб-приложение .NET, работающее в службе приложений Azure, использующее Entity Framework для доступа к базе данных SQL Azure SQL на уровне цены Standard S1 (20 DTU). В 99% случаев приложение использует менее 1% DTU в базе данных SQL. Однако, когда кто-то входит в портал администратора приложения и запускает определенный отчет, он выполняет запрос, который требует очень много ресурсов и занимает очень много времени - более минуты - с которым мы не можем жить. Этот отчет запускается только несколько раз в неделю. Я попытался расширить базу данных SQL и обнаружил, что неудивительно, что при более высоких планах время выполнения достигает некоторого разумного уровня. На стандартном S4 (200 DTU) время выполнения падает до 20 секунд, что не является идеальным, но сейчас я могу жить с этим. Однако не имеет смысла платить за уровень S4, когда в 99% случаев он будет использовать лишь долю процента DTU. Любые идеи о том, как я могу уменьшить время выполнения запроса или масштабировать только при необходимости?

Код Entity Framework, используемый для этого отчета:

class MyAppModelContainer : DbContext 
{
    public virtual ObjectResult<GetOrganizationList_Result> GetOrganizationList()
    {
        return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<GetOrganizationList_Result>("GetOrganizationList");
    }
}

Модель, используемая для получения результатов:

public partial class GetOrganizationList_Result
{
    public int id { get; set; }
    public string Name { get; set; }
    public Nullable<int> DeviceCounts { get; set; }
    public Nullable<int> EmailCounts { get; set; }
}

Хранимая процедура:

CREATE PROCEDURE [dbo].[GetOrganizationList]
AS
BEGIN
    SELECT o.Id,o.Name,COUNT(distinct s.DeviceId) as DeviceCounts, COUNT(distinct d.userid) as EmailCounts
    FROM Sessions s
    INNER JOIN Devices d on d.Id = s.DeviceId
    RIGHT OUTER JOIN Organizations o on o.id=s.OrganizationId
    GROUP BY o.Id,Name
END

Примерное количество строк в каждой из соединяемых таблиц: Таблица сессий: 2 миллиона строк Таблица устройств: 166 000 строк Таблица пользователей: 88 000 строк

Вот определения таблиц и индексы:

CREATE TABLE [dbo].[Sessions] (
    [Id]             INT      IDENTITY (1, 1) NOT NULL,
    [DeviceId]       INT      NULL,
    [StartTime]      DATETIME NOT NULL,
    [OrganizationId] INT      NOT NULL,
    CONSTRAINT [PK_Sessions] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_DeviceSession] FOREIGN KEY ([DeviceId]) REFERENCES [dbo].[Devices] ([Id]),
    CONSTRAINT [FK_OrganizationSession] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organizations] ([Id])
);

CREATE NONCLUSTERED INDEX [IX_FK_DeviceSession]
    ON [dbo].[Sessions]([DeviceId] ASC);

CREATE NONCLUSTERED INDEX [IX_FK_OrganizationSession]
    ON [dbo].[Sessions]([OrganizationId] ASC);

CREATE NONCLUSTERED INDEX [IX_Sessions_OrganizationId_Include_DeviceId]
    ON [dbo].[Sessions]([OrganizationId] ASC)
    INCLUDE([DeviceId]);    

CREATE NONCLUSTERED INDEX [IX_Sessions_OrganizationId_DeviceId] ON [dbo].[Sessions]
(
    [DeviceId] ASC,
    [OrganizationId] ASC,
    [StartTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

CREATE TABLE [dbo].[Devices] (
    [Id]         INT        IDENTITY (1, 1) NOT NULL,
    [UserId]     INT        NULL,
    [MACAddress] NCHAR (12) NOT NULL,
    CONSTRAINT [PK_Devices] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_UserDevice] FOREIGN KEY ([UserId]) REFERENCES [dbo].[Users] ([Id]),
    CONSTRAINT [IX_Unique_MACAddress] UNIQUE NONCLUSTERED ([MACAddress] ASC)
);

CREATE NONCLUSTERED INDEX [IX_FK_UserDevice]
    ON [dbo].[Devices]([UserId] ASC);

CREATE TABLE [dbo].[Users] (
    [Id]    INT            IDENTITY (1, 1) NOT NULL,
    [Email] NVARCHAR (250) NOT NULL,
    [Sex]   TINYINT        NOT NULL,
    [Age]   SMALLINT       NOT NULL,
    [PhoneNumber] NCHAR (10)     NOT NULL DEFAULT '' ,
    [Name] NVARCHAR(100) NOT NULL DEFAULT '', 
    CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [IX_Unique_Email_PhoneNumber] UNIQUE NONCLUSTERED ([Email] ASC, [PhoneNumber] ASC)
);

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

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

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

Я бы создал некластеризованный индекс columnstore. Вы делаете совокупные запросы. Это идеально подходит для вашей ситуации. Это в некоторой степени повлияет на вставки и обновления, поэтому вы захотите протестировать их с течением времени, но это правильный путь, чтобы этот запрос выполнялся намного быстрее:

CREATE NONCLUSTERED COLUMNSTORE INDEX ixtest
ON dbo.Organizations
(
    id,
    Name --plus whatever other columns are in the table
);

Я настроил небольшой тест с использованием ваших сценариев, и запрос пошел с 17 мс до 6 мс. Показания сократились с нескольких тысяч до двенадцати.

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

0 голосов
/ 26 апреля 2018

Я бы предложил создать следующие индексы или добавить недостающие столбцы в ваши существующие индексы.

CREATE NONCLUSTERED INDEX [NIX_Session_Device_OrganizationId]
ON [dbo].[Sessions] ([DeviceId] , [OrganizationId]);


CREATE NONCLUSTERED INDEX [NIX_Device_ID_UserID]
ON [dbo].[Devices] ([Id], [userid]);


CREATE NONCLUSTERED INDEX [NIX_Organizations]
ON [dbo].[Organizations] ([Id] , [Name]);

200 DTU не большое число, 2oo DTU означают, что вы уже находитесь на уровне обслуживания S4, все, что выше, приведет вас к S6.

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

...