【问题标题】:Need to improve performance query a table with millions of rows - SQL Server需要提高性能查询具有数百万行的表 - SQL Server
【发布时间】:2017-10-01 09:48:16
【问题描述】:

一旦性能测试(读取和插入)中的负载增加并且我已经尽我所能使用索引,审计跟踪表上的查询就会开始变慢。有了这个查询以及这些表和索引,我还能做什么?

CREATE TABLE [dbo].[mod2] (
  [id] [int] IDENTITY,
  [userId] [int] NOT NULL,
  [epochTime] [bigint] NOT NULL,
  [forecastId] [int] NOT NULL,
  [description] [char](12) NOT NULL,
  [auxText] [text] NULL,
  [auxDate] [date] NULL
);


ALTER TABLE [dbo].[mod2] ADD CONSTRAINT PK_mod2 PRIMARY KEY(ID);

ALTER TABLE [dbo].[mod2]  WITH CHECK
    ADD CONSTRAINT [FK_mod2_forecastId] FOREIGN KEY([forecastId])
    REFERENCES [dbo].[forecast] ([id]);

ALTER TABLE [dbo].[mod2] CHECK CONSTRAINT [FK_mod2_forecastId];

ALTER TABLE [dbo].[mod2]  WITH CHECK
    ADD CONSTRAINT [FK_mod2_userId] FOREIGN KEY([userId])
    REFERENCES [dbo].[user] ([id]);

ALTER TABLE [dbo].[mod2] CHECK CONSTRAINT [FK_mod2_userId];

CREATE NONCLUSTERED INDEX IX_modification_auxDate ON [dbo].[mod2] (auxDate ASC);

CREATE NONCLUSTERED INDEX IX_modification_epochTime ON [dbo].[mod2] (epochTime ASC);

CREATE NONCLUSTERED INDEX IX_modification_description ON [dbo].[mod2] (description ASC);

CREATE NONCLUSTERED INDEX IX_modification_forecastId ON [dbo].[mod2] (forecastId ASC);

CREATE NONCLUSTERED INDEX IX_modification_userId ON [dbo].[mod2] (userId ASC);

这是我的查询:

SELECT name, epochTime, auxDate 
    FROM mod2 WITH (NOLOCK)
    JOIN [user] ON [user].id = mod2.userId 
    WHERE forecastId = ? AND description = ? AND auxDate = ?

这是一个遗留系统,在我将这些索引放在上面并将 description 字段从 VARCHAR 更改为 CHAR 之前它正在爬网

预测和用户id 字段是INT 并且索引类似。

【问题讨论】:

  • 您也可以考虑将 auxText 的数据类型从 text 更改为 varchar(max)。它对这个查询没有帮助,但 text 数据类型已经被弃用了十多年,取而代之的是 varchar(max)。

标签: java sql-server jdbc hikaricp


【解决方案1】:

您可以在这里做一些事情。

一个是确保在user 上的id 字段有一个聚集索引(可能有,但要确保不会受到伤害)。

您输入的单个索引不是很好 - 特别是考虑到您显示的查询模式 - 要么不使用它们(因为它们不包含完整数据),要么可能使用其中一些然后将参考整个表,以便挑选出完成查询所需的剩余数据。

对于这个特定的查询,对于 mod2,我可能会在 userId 上添加一个索引,其中包含 covering forecastIddescriptionauxDate - 这样索引包含所有需要的数据完成查询(在mod2 一侧)。

【讨论】:

  • @Adam - 考虑删除你的一个字段索引,以支持覆盖更多的更少索引。
  • 好的。因此,如果这样做,然后在WHERE 子句中有另一个不使用auxDate 的查询,我应该为其创建第二个覆盖索引还是原始覆盖索引就足够了,即使它包含auxDate
  • @Adam - 如果其他查询仍然使用其他涵盖的字段,则可能仍会使用该索引,因此可能不需要另一个索引。
  • 检查了 [user].[id] 列是否使用 PK 聚集索引编制索引,并创建了索引(编辑了您的答案)并运行了一些性能测试,显示持续时间下降了 2/3,这很好。尽管如此,它仍然是应用程序中最慢的操作,这令人费解,因为其他 2 个主要端点确实写入该表,但现在快了两倍(之前快了 5 倍)。这是否仍然表明存在问题,或者我将不得不忍受这个?
  • @Adam - 检查查询计划,看看什么是和不使用索引,什么是扫描(好)与搜索(不太好)。考虑到插入 one 行与经历数百万行当然会更糟。当你说最慢时,是什么因素?
【解决方案2】:
CREATE NONCLUSTERED INDEX IXmod2_user_desc_forecast_auxdate 
ON [dbo].[mod2] (userId, forecastId, description, auxDate DESC);

查询计划如下所示:

  |--Nested Loops(Inner Join, OUTER REFERENCES:([MyDB].[dbo].[mod2].[id]) OPTIMIZED)
       |--Nested Loops(Inner Join, OUTER REFERENCES:([MyDB].[dbo].[user].[id], [Expr1006]) WITH UNORDERED PREFETCH)
       |    |--Clustered Index Scan(OBJECT:([MyDB].[dbo].[user].  [PK_user]), ORDERED FORWARD)
       |    |--Index Seek(OBJECT:([MyDB].[dbo].[mod2].[IX_mod2_user_desc_forecast_auxdate]), SEEK:([MyDB].[dbo].[mod2].[userId]=[MyDB].[dbo].[user].[id] AND [MyDB].[dbo].[mod2].[forecastId]=(40357) AND [MyDB].[dbo].[mod2].[description]='SAVE' AND [MyDB].[dbo].[mod2].[auxDate]='2017-01-31') ORDERED FORWARD)
       |--Clustered Index Seek(OBJECT:([MyDB].[dbo].[mod2].[PK_mod2]), SEEK:([MyDB].[dbo].[mod2].[id]=[MyDB].[dbo].[mod2].[id]) LOOKUP ORDERED FORWARD)

【讨论】:

    猜你喜欢
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    相关资源
    最近更新 更多