Sql Server优化之路

  本文只限coder级别层次上对Sql Server的优化处理简结,为防止专业DB人士有恶心、反胃等现象,请提早关闭此页面。html

  首先得有一个测试库,使用数据生成计划生成测试数据库(参考:http://developer.51cto.com/art/201102/245165.htm),或者下一个MS白给的库(AdventureWorks2008)。sql

1、架构设计数据库

 库表的合理设计对项目后期的响应时间和吞吐量起到相当重要的地位,它直接影响到了业务所需处理的sql语句的复杂程度,为提升数据库的性能,更多的把逻辑主外键、级联删除、减小check约束、给null字段添加default值等操做放到了程序端;就如,虽然修改存储过程有时候能够避免发布程序,但过多的逻辑判断也随之带来了性能问题;因此出发点不一样 取其平衡就好。windows

2、语句优化缓存

  

  优化sql语句最基本的原则就是将sql语句简单化,将一个复杂的sql语句拆解执行,如图能够看出咱们所执行的sql语句都是通过查询优化器分析获得高效的执行计划,那么简单的sql语句很大程度上提升了执行效率。服务器

  一、select多线程

    a、执行一条合理的sql语句,IO和数据的显示可能占据了整个过程的90%,而Sql Server后台的处理却少之又少架构

select colum1,colum2,colum3 from tb

    b、对于实时性不强的数据可使用with (nolock),使用强制索指导执行计划并发

select * from Person.Person with (nolock) 
select * from Person.Person with (index(PK_Person_BusinessEntityID))

    c、避免子查询函数

select * from Person.Password where BusinessEntityID in (select BusinessEntityID from Person.Person where FirstName='Ken');
--替换为
with tb(BusinessEntityID) as (select BusinessEntityID from Person.Person where FirstName='Ken')
select * from Person.Password where exists(select 1 from tb where BusinessEntityID=Person.Password.BusinessEntityID);

  二、where

    a、致使index scan或table scan

select * from tb where like '%value%' -- like 'value%'
select * from tb where colum1<>0
select * from tb where colum1=1 or colum2=2 --colum1或colum2没有索引则致使全表扫描
--尽可能使用exists代替in
select * from tb1 where colum in (select colum from tb2); -- select * from tb1 where exists(select 1 from tb2 where colum=tb1.colum);

    b、在有索引的字段中避免使用函数和表达式,致使索引没法使用

select * from tb where datediff(mm,'2015-1-1',colum1)=1;
select * from tb where substring(colum1,1,6)='value';

  三、jion

--join链接最好不要超过5个表,有更新的大数据表先放进临时表,而后再join
select BusinessEntityID,FirstName into #tmptable from Person.Person;
select * from Person.Password,#tmptable where Person.Password.BusinessEntityID=#tmptable.BusinessEntityID
drop table #tmptable

  四、insert

--批量插入数据,select into必定比逐条insert快
insert into tb values(colum1,colum2,colum3),(colum1,colum2,colum3),(colum1,colum2,colum3);
--文件批处理bulk insert和openrowset
https://technet.microsoft.com/zh-cn/library/ms188365(v=sql.105).aspx

  五、procedure,存储过程优势是执行速度快,由于它是预编译过的,而且执行以后会缓存到plan cache中。

--一、由于参数值的改变会致使从新生成执行计划,缓存过多执行计划,致使效率变低。
execute proc_tb_xx with recompile  --强制在执行存储过程时对其从新编译
create proc pro_tb_xx with recompile  --不为该存储过程缓存计划,每次执行存储过程时都必须对其从新编译(致使存储过程变慢)

--二、数据库进行了索引或其余会影响数据库统计的更改后,已编译的存储过程和触发器可能会失去效率。
execute sp_recompile N'object';  --经过对做用于表上的存储过程和触发器进行从新编译,能够从新优化查询
--object:当前数据库中存储过程、触发器、表或视图的限定或未限定名称;object 是存储过程或触发器的名称,则该存储过程或触发器将在下次运行时从新编译。若是 object 是表或视图的名称,则全部引用该表或视图的存储过程或触发器都将在下次运行时从新编译。

  六、漏洞注入,动态语句参数化查询时不要忘记sp_executesql代替exec

create proc proc_xxx
@addressid int,
@city nvarchar(16)
as
begin
	declare @sql nvarchar(1148);
	set @sql='select * from (select *,num=(row_number() over(order by AddressID asc)) from Person.Address where 1=1';
	if(@city<>'')
	begin
		set @sql=@sql+' and City like @city';
	end
	if(@addressid<>0)
	begin
		set @sql=@sql+' and ID=@addressid';
	end
	set @sql=@sql+' ) A where A.num between @sindex and @eindex';
	exec sp_executesql @sql,N'@city nvarchar(64),@addressid int,@sindex int,@eindex int',@city,@addressid,@sindex,@eindex;
end

3、索引优化

  众所周知,索引能够很大程度提高查询的效率,有时候经过添加一个索引 性能能够起到数以百倍的提高;但由于业务数据过大,过多的索引反而事到其反,一些做用不大的索引维护时也占用了性能的开销;这时就须要分析索引的使用状况,删除一些做用不大的索引,经过SQL Server提供的系统动态管理视图分析便可。

在建立汇集索引以前,应先了解您的数据是如何被访问的。可考虑将汇集索引用于:
包含大量非重复值的列。
使用下列运算符返回一个范围值的查询:BETWEEN>>=<<=。
被连续访问的列。
返回大型结果集的查询。
常常被使用联接或 GROUP BY 子句的查询访问的列;通常来讲,这些是外键列。对 ORDER BYGROUP BY 子句中指定的列进行索引,可使 SQL Server 没必要对数据进行排序,由于这些行已经排序。这样能够提升查询性能。
OLTP 类型的应用程序,这些程序要求进行很是快速的单行查找(通常经过主键)。应在主键上建立汇集索引。
 
汇集索引不适用于:
频繁更改的列
这将致使整行移动(由于 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,由于在大数据量事务处理系统中数据是易失的。
宽键
来自汇集索引的键值由全部非汇集索引做为查找键使用,所以存储在每一个非汇集索引的叶条目内。

  一、建立索引的关键在于减小sql语句执行时的逻辑读取次数,逻辑次数读取越少,执行所需的内容和cup时间也越少,则sql语句执行的越快;若是逻辑读取次数过大,返回数据较少则需考虑索引优化。

set statistics io on
go
select * from Production.WorkOrder where WorkOrderID=1
go
set statistics io off
--表 'WorkOrder'。扫描计数 0,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

  对查询次数最多且显示scan的where条件进行优化。

--显示预计的执行计划 showplan_all、showplan_text、showplan_xml
set showplan_all on 
go
select * from Production.WorkOrder where WorkOrderID=1
go
set showplan_all off
--显示真实的执行计划 statistics profile、statistics xml
set statistics profile on
go
select * from Production.WorkOrder where OrderQty=8
go
set statistics profile off

  二、分析所缺乏的索引,经过语句查询自从上次SQL Server服务重启以后到当前时间为止所有数据库中可能缺乏哪些索引。

select  b.name ,  --数据库名称
        a.statement ,  --缺乏索引表的名称
        a.equality_columns ,  --常常用于等值比较的列名,如 ID=value
        inequality_columns ,  --常常用于不等值比较的列名,如 ID>value ID<>value
        included_columns  --建议在索引中涵盖或者包含的列
from    sys.[dm_db_missing_index_details] a
        join sys.databases b on a.database_id = b.database_id

  三、分析索引的使用状况,经过语句查询自从上次SQL Server服务重启以后到当前时间为止数据库中全部索引的使用状况。

    a、seek过少,而scans或update过大,证实索引不被常用,而是用于修改和全表扫描,那么就能够考虑删除此索引了;

    b、seek过多,而scans和update也过大,维护索引成本较高,就要考虑权衡利弊了。

--更新表索引的统计信息
update statistics tablename with fullscan

select
     db_name() as DBNAME,  --数据库名称
 object_name(a.object_id) as table_name,  --表名称
 coalesce(name,'object with no clustered index') as index_name, --索引名称
 type_desc as index_type,  --索引类型
 user_seeks,  --使用索引查询的次数
 user_scans,  --使用全表扫描的次数
 user_lookups,  --使用书签的次数,使用书签会形成二次IO,考虑是否加入非汇集索引
 user_updates  --索引的更新次数
from sys.dm_db_index_usage_stats a inner join sys.indexes b
on a.index_id = b.index_id  and a.object_id = b.object_id
where database_id = db_id('AdventureWorks2008')

  四、对于一些再也不修改的历史数据,只为查询出报表等能够新建存储创建列存储索引-Apollo,而列存储索引也是自Sql Server2012以后引入主要处理海量数据仓储的高效查询;并且在查询优化器运行查询时也会优先访问列存储索引,其次才是基于行的汇集和非汇集索引

--列存储索引的限制,只支持一些经常使用的业务数据类型(int, real, string, money, datetime, decimal <= 18),Sql Server2012以后才加入了更新
--建立列存储索引
create nonclustered columnstore index cs_index on tb(colum);
--强制索引
select * from tb with (index(cs_index));
--禁用索引(Sql Server2014已支持数据的读和写)
alter index cs_index on tb disable
--重建列存储索引
alter index cs_index on tb rebuild

4、并发控制

  与并发密不可分的就是事务和锁了,在大并发事务争抢资源之下,数据库锁应运而生;Sql Server在处理过程当中会对锁定行或索引范围放置意向锁,当意向锁升级时,会减小锁的数量,也是对性能提高时Sql Server进行的锁升级,同时也是一个信号量,标识着程序设计、编码或配置方面须要优化。同理,优化时咱们也是经过锁提示让Sql Server执行时采用咱们业务所须要的锁,即便个别锁有时候就能够对事务和程序起到相当重要的性能提升;同时避免死锁的出现,经过sql profile和活动监视器跟踪和处理死锁。

按同一顺序访问对象。
避免事务中的用户交互。
保持事务简短并处于一个批处理中。
使用较低的隔离级别。
使用基于行版本控制的隔离级别。
将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。
使用快照隔离。
使用绑定链接。

5、存储优化

  磁盘IO有瓶颈,平常的windows操做已经证实了多线程读多个小文件比读一个大文件来的快,即便能够数据库缓存或主从库提升查询效率,可是在数以百G的数据面前,没有强悍的配置 主从同步快照一次的时间就够瞧的,拆库拆表就成了最好的选择。

--建立分区文件组
alter database Test add filegroup testgf1
alter database Test add filegroup testgf2
--建立分区文件
alter database Test add file 
(
	name=testdata1,
	filename='D:\testdb\testdata1.ndf',
	size=5MB,
	maxsize=100MB,
	filegrowth=5MB
) to filegroup testgf1;
alter database Test add file 
(
	name=testdata2,
	filename='E:\testdb\testdata2.ndf',
	size=5MB,
	maxsize=100MB,
	filegrowth=5MB
) to filegroup testgf2
--建立分区函数
create partition function testRangePF(int) as range left for values(1000,2000);
--建立分区方案
create partition scheme testRangePS as partition testRangePF to (testgf1,testgf2);
--建立分区表
create table tb(...) on testRangePS(colum);

  参考:https://msdn.microsoft.com/zh-cn/library/ms188730(v=sql.110).aspx

6、服务器优化

  硬件无非处理器、内存、SSD、磁盘分区、负载、主从、集群这些的,能力有限,点到为止了。

7、调优

  一、经过windows的事件查看器能够查看到Sql Server的异常日志信息。

  二、经过windows性能监视器也能够添加监视内存、CUP和线程的使用状况。

  3、活动监视器是最简洁也是最直观查看Sql Server当前执行状况的工具了,无论CPU、IO占用比,仍是耗资源的语句,以及等待的操做都一目了然,有等待或者死锁状况也能经过链接进程能够找到链接用户以及致使此状况发生的链接进程及所执行的sql操做。

  四、Sql Profiler是sql执行事件跟踪最经常使用的工具了,能够跟踪查看到参数化后的sql语句,方便进行执行计划分析。

  五、数据库引擎优化顾问,最常的使用应该就是经过Sql Profiler对线上的系统进行一段时间的跟踪,而后保存工做负荷文件导入到引擎优化顾问中作全方位的分析。

 

欢迎转载,来爬我啊:http://www.cnblogs.com/NotAnEmpty/p/5441127.html

相关文章
相关标签/搜索