今天在监控数据库时,发现一个语句的执行时间比较长,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的效率。
有时:聚集索引效率>索引覆盖,具体要看情况,这只是其中的一个例子。