索引的重要性
数据库性能优化中索引绝对是一个重量级的因素,能够说,索引使用不当,其它优化措施将毫无心义。
聚簇索引(Clustered Index)和非聚簇索引 (Non- Clustered Index)
最通俗的解释是:聚簇索引的顺序就是数据的物理存储顺序,而对非聚簇索引的索引顺序与数据物理排列顺序无关。举例来讲,你翻到新华字典的汉字“爬”那一页就是P开头的部分,这就是物理存储顺序(聚簇索引);而不用你到目录,找到汉字“爬”所在的页码,而后根据页码找到这个字(非聚簇索引)。
下表给出了什么时候使用聚簇索引与非聚簇索引:数据库
动做 |
使用聚簇索引 |
使用非聚簇索引 |
列常常被分组排序 |
应 |
应 |
返回某范围内的数据 |
应 |
不该 |
一个或极少不一样值 |
不该 |
不该 |
小数目的不一样值 |
应 |
不该 |
大数目的不一样值 |
不该 |
应 |
频繁更新的列 |
不该 |
应 |
外键列 |
应 |
应 |
主键列 |
应 |
应 |
频繁修改索引列 |
不该 |
应 |
聚簇索引的惟一性
正式聚簇索引的顺序就是数据的物理存储顺序,因此一个表最多只能有一个聚簇索引,由于物理存储只能有一个顺序。正由于一个表最多只能有一个聚簇索引,因此它显得更为珍贵,一个表设置什么为聚簇索引对性能很关键。
初学者最大的误区:把主键自动设为聚簇索引
由于这是SQLServer的默认主键行为,你设置了主键,它就把主键设为聚簇索引,而一个表最多只能有一个聚簇索引,因此不少人就把其余索引设置为非聚簇索引。这个是最大的误区。甚至有的主键又是无心义的自动增量字段,那样的话Clustered index对效率的帮助,彻底被浪费了。
刚才说到了,聚簇索引性能最好并且具备惟一性,因此很是珍贵,必须慎重设置。通常要根据这个表最经常使用的SQL查询方式来进行选择,某个字段做为聚簇索引,或组合聚簇索引,这个要看实际状况。
事实上,建表的时候,先须要设置主键,而后添加咱们想要的聚簇索引,最后设置主键,SQLServer就会自动把主键设置为非聚簇索引(会自动根据状况选择)。若是你已经设置了主键为聚簇索引,必须先删除主键,而后添加咱们想要的聚簇索引,最后恢复设置主键便可。
记住咱们的最终目的就是在相同结果集状况下,尽量减小逻辑IO。
咱们先从一个实际使用的简单例子开始。
一个简单的表:函数
CREATE TABLE [dbo].[Table1](
工具
[ID] [int] IDENTITY(1,1) NOT NULL,
oop
[Da性能
ta1] [int] NOT NULL DEFAULT ((0)),
测试
[Da字体
ta2] [int] NOT NULL DEFAULT ((0)),
优化
[Daspa
ta3] [int] NOT NULL DEFAULT ((0)),
.net
[Name1] [nvarchar](50) NOT NULL DEFAULT (''),
[Name2] [nvarchar](50) NOT NULL DEFAULT (''),
[Name3] [nvarchar](50) DEFAULT (''),
[DTAt] [datetime] NOT NULL DEFAULT (getdate())
复制代码
declare @i int
set @i = 1
while @i < 100000
begin
insert into Table1 ([Da
ta1] ,[Da
ta2] ,[Da
ta3] ,[Name1],[Name2] ,[Name3])
values(@i , 2* @i ,3*@i, CAST(@i AS NVARCHAR(50)), CAST(2*@i AS NVARCHAR(50)), CAST(3*@i AS NVARCHAR(50)))
set @i = @i + 1
end
update table1 set dtat= DateAdd (s, da
ta1, dtat)
复制代码
打开查询分析器的IO统计和时间统计:
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
复制代码
显示实际的“执行计划”:
咱们最经常使用的SQL查询是这样的:
SELECT * FROM Table1 WHERE Da
ta1 = 2 ORDER BY DTAt DESC;
复制代码
先在Table1设主键ID,系统自动为该主键创建了聚簇索引。
而后执行该语句,结果是:
Table 'Table1'. Scan count 1, logical reads 911, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 7 ms.
复制代码
CREATE NONCLUSTERED INDEX [N_Da
ta1] ON [dbo].[Table1]
(
[Da
ta1] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [N_DTat] ON [dbo].[Table1]
(
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
复制代码
再次执行该语句,结果是:
Table 'Table1'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 39 ms.
复制代码
能够看到设立了索引反而没有任何性能的提高并且消耗的时间更多了,继续调整。
而后咱们删除全部非聚簇索引,并删除主键,这样全部索引都删除了。创建组合索引Data1和DTAt,最后加上主键:
CREATE CLUSTERED INDEX [C_Da
ta1_DTat] ON [dbo].[Table1]
(
[Da
ta1] ASC,
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
复制代码
再次执行语句:
Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
复制代码
能够看到只有聚簇索引seek了,消除了index scan和nested loop,并且执行时间也只有1ms,达到了最初优化的目的。
组合索引小结
小结以上的调优实践,要注意聚簇索引的选择。首先咱们要找到咱们最多用到的SQL查询,像本例就是那句相似的组合条件查询的状况,这种状况最好使用组合聚簇索引,并且最多用到的字段要放在组合聚簇索引的前面,不然的话就索引就不会有好的效果,看下例:
查询条件落在组合索引的第二个字段上,引发了index scan,效果很很差,执行时间是:
Table 'Table1'. Scan count 1, logical reads 238, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 22 ms.
复制代码
而若是仅查询条件是第一个字段也没有问题,由于组合索引最左前缀原则,实践以下:
Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
复制代码
从中能够看出,最多用到的字段要放在组合聚簇索引的前面。
Index seek 为何比 Index scan好?
索引扫描也就是遍历B树,而seek是B树查找直接定位。
Index scan多半是出如今索引列在表达式中。数据库引擎没法直接肯定你要的列的值,因此只能扫描整个整个索引进行计算。index seek就要好不少.数据库引擎只须要扫描几个分支节点就能够定位到你要的记录。回过来,若是汇集索引的叶子节点就是记录,那么Clustered Index Scan就基本等同于full table scan。
一些优化原则
一、缺省状况下创建的索引是非聚簇索引,但有时它并非最佳的。在非群集索引下,数据在物理上随机存放在数据页上。合理的索引设计要创建在对各类查询的分析和预测上。通常来讲:
a.有大量重复值、且常常有范围查询( > ,< ,> =,< =)和order by、group by发生的列,可考
虑创建群集索引;
b.常常同时存取多列,且每列都含有重复值可考虑创建组合索引;
c.组合索引要尽可能使关键查询造成索引覆盖,其前导列必定是使用最频繁的列。索引虽有助于提升性能但不是索引越多越好,刚好相反过多的索引会致使系统低效。用户在表中每加进一个索引,维护索引集合就要作相应的更新工做。
二、ORDER BY和GROPU BY使用ORDER BY和GROUP BY短语,任何一种索引都有助于SELECT的性能提升。
三、多表操做在被实际执行前,查询优化器会根据链接条件,列出几组可能的链接方案并从中找出系统开销最小的最佳方案。链接条件要充份考虑带有索引的表、行数多的表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数肯定,乘积最小为最佳方案。
四、任何对列的操做都将致使表扫描,它包括数据库函数、计算表达式等等,查询时要尽量将操做移至等号右边。
五、IN、OR子句常会使用工做表,使索引失效。若是不产生大量重复值,能够考虑把子句拆开。拆开的子句中应该包含索引。
Sql的优化原则2:
一、只要能知足你的需求,应尽量使用更小的数据类型:例如使用MEDIUMINT代替INT
二、尽可能把全部的列设置为NOT NULL,若是你要保存NULL,手动去设置它,而不是把它设为默认值。
三、尽可能少用VARCHAR、TEXT、BLOB类型
四、若是你的数据只有你所知的少许的几个。最好使用ENUM类型
使用SQLServer Profiler找出数据库中性能最差的SQL
首先打开SQLServer Profiler:
而后点击工具栏“New Trace”,使用默认的模板,点击RUN。
也许会有报错:"only TrueType fonts are supported. There id not a TrueType font"。不用怕,点击Tools菜单->Options,从新选择一个字体例如Vendana 便可。(这个是微软的一个bug)
运行起来之后,SQLServer Profiler会监控数据库的活动,因此最好在你须要监控的数据库上多作些操做。等以为差很少了,点击中止。而后保存trace结果到文件或者table。
这里保存到Table:在菜单“File”-“Save as ”-“Trace table”,例如输入一个master数据库的新的table名:profileTrace,保存便可。
找到最耗时的SQL:
use master
select * from profiletrace order by duration desc;
复制代码
找到了性能瓶颈,接下来就能够有针对性的一个个进行调优了。
对使用SQLServer Profiler的更多信息能够参考:
http://www.codeproject.com/KB/database/DiagnoseProblemsSQLServer.aspx
使用SQLServer Database Engine Tuning Advisor数据库引擎优化顾问
使用上述的SQLServer Profiler获得了trace还有一个好处就是能够用到这个优化顾问。用它能够偷点懒,获得SQLServer给您的优化顾问,例如这个表须要加个索引什么的…
首先打开数据库引擎优化顾问:
而后打开刚才profiler的结果(咱们存到了master数据库的profileTrace表):
点击“start analysis”,运行完成后查看优化建议(图中最后是建议创建的索引,性能提高72%)
这个方法能够偷点懒,获得SQLServer给您的优化顾问。