索引调优问题

seb*_*eid 8 index sql-server index-tuning

我正在调整一些索引,看到一些问题想听听你的建议

在 1 个表上有 3 个索引

dbo.Address.IX_Address_ProfileId 
[1 KEY] ProfileId {int 4}
Reads: 0 Writes:10,519

dbo.Address.IX_Address 
[2 KEYS] ProfileId {int 4}, InstanceId {int 4}
Reads: 0 Writes:10,523

dbo.Address.IX_Address_profile_instance_addresstype
[3 KEYS] ProfileId {int 4}, InstanceId {int 4}, AddressType {int 4}
Reads: 149677 (53,247 seek) Writes:10,523
Run Code Online (Sandbox Code Playgroud)

1- 我真的需要前 2 个索引,还是应该删除它们?

2- 有查询运行使用条件 where profileid = xxxx 和其他使用条件 where profileid = xxxx 和 InstanceID=xxxxxx。为什么优化器选择第 3 个索引而不是第 1 个或第 2 个?

此外,我正在运行一个查询,使每个索引上的锁定等待。如果我得到这些计数,我应该怎么做来调整这个索引?

Row lock waits: 484; total duration: 59 minutes; avg duration: 7 seconds; 
Page lock waits: 5; total duration: 11 seconds; avg duration: 2 seconds; 
Lock escalation attempts: 36,949; Actual Escalations: 0.
Run Code Online (Sandbox Code Playgroud)

表结构是

TABLE [dbo].[Address](
[Id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[AddressType] [int] NULL,
[isPreferredAddress] [bit] NULL,
[StreetAddress1] [nvarchar](255) NULL,
[StreetAddress2] [nvarchar](255) NULL,
[City] [nvarchar](50) NULL,
[State_Id] [int] NOT NULL,
[Zip] [varchar](20) NULL,
[Country_Id] [int] NOT NULL,
[CurrentUntil] [date] NULL,
[CreatedDate] [datetime] NOT NULL,
[UpdatedDate] [datetime] NOT NULL,
[ProfileId] [int] NOT NULL,
[InstanceId] [int] NOT NULL,
[County_id] [int] NULL,
 CONSTRAINT [PK__Address__3214EC075E4BE276] PRIMARY KEY CLUSTERED 
(
   [Id] ASC
 )
Run Code Online (Sandbox Code Playgroud)

这是一个例子(这个查询是由 hibernate 创建的,所以看起来很奇怪)

(@P0 bigint)select addresses0_.ProfileId as Profile15_109_1_
, addresses0_.Id as Id1_20_1_
, addresses0_.Id as Id1_20_0_
, addresses0_.AddressType as AddressT2_20_0_
, addresses0_.City as City3_20_0_
, addresses0_.Country_Id as Country_4_20_0_
, addresses0_.County_id as County_i5_20_0_
, addresses0_.CreatedDate as CreatedD6_20_0_
, addresses0_.CurrentUntil as CurrentU7_20_0_
, addresses0_.InstanceId as Instance8_20_0_
, addresses0_.isPreferredAddress as isPrefer9_20_0_
, addresses0_.ProfileId as Profile15_20_0_
, addresses0_.State_Id as State_I10_20_0_
, addresses0_.StreetAddress1 as StreetA11_20_0_
, addresses0_.StreetAddress2 as StreetA12_20_0_
, addresses0_.UpdatedDate as Updated13_20_0_
, addresses0_.Zip as Zip14_20_0_ 
from dbo.Address addresses0_ 
where addresses0_.ProfileId=@P0 
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

(@P0 bigint,@P1 bigint)
select addressdmo0_.Id as Id1_20_
, addressdmo0_.AddressType as AddressT2_20_
, addressdmo0_.City as City3_20_
, addressdmo0_.Country_Id as Country_4_20_
, addressdmo0_.County_id as County_i5_20_
, addressdmo0_.CreatedDate as CreatedD6_20_
, addressdmo0_.CurrentUntil as CurrentU7_20_
, addressdmo0_.InstanceId as Instance8_20_
, addressdmo0_.isPreferredAddress as isPrefer9_20_
, addressdmo0_.ProfileId as Profile15_20_
, addressdmo0_.State_Id as State_I10_20_
, addressdmo0_.StreetAddress1 as StreetA11_20_
, addressdmo0_.StreetAddress2 as StreetA12_20_
, addressdmo0_.UpdatedDate as Updated13_20_
, addressdmo0_.Zip as Zip14_20_ 
from dbo.Address addressdmo0_ 
left outer join dbo.Profile profiledmo1_ 
on addressdmo0_.ProfileId=profiledmo1_.Id 
where profiledmo1_.Id=@P0 and addressdmo0_.InstanceId=@P1
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

Aar*_*ron 6

对问题 1 的回答:

从您发布的内容中,您可以删除前两个索引,因为第三个索引将涵盖您提到的所有查询,并且查询优化器在构建查询计划(基于您发布的计划)时也会看到这一点。

对问题 2 的回答:

它总是使用第三个索引,因为它的索引中已经有更多的数据和两个额外的索引键 ( InstanceId and AddressType)。这使 SQL 无需从主键(执行计划的键查找部分)中提取 InstanceId 和 AddressType 以满足查询。

我的建议是删除前两个索引并使用包含列重建第三个索引以覆盖查询中请求的其他列

Create index IX_Address_profile_instance_addresstype 
on dbo.address  (ProfileId, InstanceId, AddressType) 
include(<put in the remaining columns comma delimited>) 
with (drop_existing=on,sort_in_tempdb=on)
Run Code Online (Sandbox Code Playgroud)

这应该有助于查询,并且应该从查询计划中删除键查找。

看看这些更改后锁是否会脱落,如果没有,我们可以深入挖掘一下。

  • @sebeid 如果一切都是从 hibernate 生成的语句,我会询问您的开发人员,因为这可能是最简单的方法,否则您将需要跟踪或设置一个扩展事件来尝试捕获调用,以便您可以查看所有内容批处理语句。[扩展事件](http://www.brentozar.com/extended-events/) [Profiler 示例](https://www.mssqltips.com/sqlservertutorial/272/profiler-and-server-side-traces/) (2认同)

小智 2

似乎您可以删除索引 1 和 2,因为索引 3 包含您需要的所有信息(列)。另一个索引可能可以作为表示主键的聚集索引。

凭借这些有限的信息,我们只能猜测。如果您需要更多提示,请发布更详细的信息,如完整的表结构(表、索引、键...)、查询和执行计划。