如何容纳不经常运行但资源密集的Azure SQL查询

时间:2018-04-26 11:30:43

标签: sql sql-server entity-framework azure azure-sql-database

注意:我在这里提供了我的Azure设置的详细信息,但我不确定该解决方案是否基于Azure。这可能是可以在C#,实体框架或SQL级别解决的问题。

我在Azure App Service上运行.NET Web应用程序,使用Entity Framework以定价级别标准S1(20 DTU)访问Azure SQL DB。 99%的时间,该应用程序在SQL DB上使用的DTU不到1%。但是,当有人登录应用程序的管理门户并运行特定报告时,它会执行资源密集型的查询,并且需要很长时间 - 超过一分钟 - 这是我们无法忍受的。该报告每周只运行几次。我已经尝试过扩展SQL数据库并且发现 - 不出所料 - 在更高的计划中,执行时间达到了一个合理的水平。在标准S4(200 DTU),执行时间下降到20秒,这是不理想的,但我现在可以忍受。但是,当99%的时间只使用DTU的一小部分时,支付S4级是没有意义的。关于如何减少查询执行时间或仅在需要时进行扩展的任何想法?

此报告使用的实体框架代码为:

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

每个连接表中的近似行数: 会话表:200万行 设备表: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更改,代码更改在内的任何内容持开放态度。似乎没有Azure SQL DB的定价消费模型,如果它存在,这可能对我有所帮助。

2 个答案:

答案 0 :(得分:0)

我建议您创建以下索引或将缺少的列添加到退出的索引中。

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定价模型,而不是使用{的blackbox { {1}}。

答案 1 :(得分:0)

我会创建一个非聚集的列存储索引。你正在进行聚合查询。这非常适合您的情况。它会影响插入和插入有些更新,所以你会想要随着时间的推移进行测试,但这是使查询运行得更快的正确方法:

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

我使用您的脚本设置了一个小测试,查询从17ms到6ms。读数从几千下降到大约十二。

你没有包含组织的定义,所以我只是把它弄掉了。您需要确保在列存储索引中包含所有列(这是最佳实践)。