SQL Server 优化合理使用聚集索引

      今天在监控数据库时,发现一个语句的执行时间比较长,IO次数也比较多。语句如下:

select * from Docbase this_ where 
 id 
in ( select objid from Categorylink categorylink where 
categorylink.objtype
='Docbase' and (categorylink.categoryid = 
'4028827015a82d020115d07b6b604689')) and pid is null and isdelete=0 and
exists(select 'X' from Permissiondetail p where p.objid=this_.id and 
p.objtable
='docbase'and  p.userid in('','ISALLUSER','402882ed0eb78aae010ec124f7fe5c87',
'402881a10d33db44010d3402fc2b00c3'and 10 between p.minseclevel and p.maxseclevel and p.opttype > 2 ) and not exists (select 'X' from Delobj del where del.objid=this_.id and del.objtable='Docbase'

   由于表Permissiondetail表数据有很多,大约有几百万条数据,先前建立一个索引来达到索引覆盖,避免表扫描。

CREATE  INDEX [IX_PERMISSIONDETAIL_mu] ON [dbo].[permissiondetail] 
(
    
[objid] ASC,[objtable]ASC,[userid] ASC,[minseclevel] ASC,[maxseclevel] ASC,[opttype] ASC
)

   执行结果如下:

(495 行受影响)
表 
'delobj'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'docbase'。扫描计数 1,逻辑读取 659 次,物理读取 0 次,预读 0 次,lob 逻辑读取 85 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'categorylink'。扫描计数 3,逻辑读取 36 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'Permissiondetailworkflowbase'。扫描计数 1,逻辑读取 30690 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

  后来发现这样的优化效果也是不明细,因为这里运行的速度也不是很快,因为数据库是Index scan,他会对索引的全部数据进行扫描。一旦数据量大,也很慢,而且这个索引的长度太长了,影响DML语句。后来通过分析表的数据分布。和userid的数据情况,在userid里建立一个聚集索引,效率会大于索引覆盖。

CREATE clustered INDEX [IX_PERMISSIONDETAIL_userid] ON [dbo].[permissiondetail] ( [userid] ASC)

   执行语句结果:

(495 行受影响)
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'Permissiondetail'。扫描计数 1988,逻辑读取 10106 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'docbase'。扫描计数 1,逻辑读取 659 次,物理读取 0 次,预读 0 次,lob 逻辑读取 87 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'categorylink'。扫描计数 1,逻辑读取 12 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'delobj'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    结果:是IO次数比先前提高了3倍,而且DML的影响是比较少的。

  总结:

    这个案例有个特殊性,就是对Permissiondetail大表会有查询很多的数据,这时如果不能达到索引覆盖,执行的时间比较长,但是这样的做饭牺牲了很多其他的性能:插入,更新和删除数据,不是最优的。我们更改了索引,将userid改成聚集索引,一样达到了提高效率的方法,同时也提高了DML的效率。

   有时:聚集索引效率>索引覆盖,具体要看情况,这只是其中的一个例子。

原文地址:https://www.cnblogs.com/zping/p/1303813.html