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')
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
)
(
[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 次。
表 '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 次。
表 '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的效率。
有时:聚集索引效率>索引覆盖,具体要看情况,这只是其中的一个例子。