--调试语句性能前记得清空执行计划
每次执行需优化SQL前,带上清除缓存的设置SQL。
平常在进行SQL Server性能优化时,为了确保真实还原性能问题,我们需要关闭SQL Server自身的执行计划及缓存。可以通过以下设置清除缓存。
1 DBCC DROPCLEANBUFFERS --清除缓冲区
2 DBCC FREEPROCCACHE --删除计划高速缓存中的元素
开启查询IO读取统计、查询时间统计。
SET STATISTICS TIME ON --执行时间
SET STATISTICS IO ON --IO读取
--查询当前的事务
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
select t2.session_id as SPID, t2.transaction_id, transaction_begin_time, N'已执行'+ltrim(datediff(mi,transaction_begin_time,getdate()))+N'分钟' as mi, case transaction_type when 1 then N'读/写事务' when 2 then N'只读事务' when 3 then N'系统事务' when 4 then N'分布式事务' end tran_Type, case transaction_state when 0 then N'事务尚未完全初始化' when 1 then N'事务已初始化但尚未启动' when 2 then N'事务处于活动状态' when 3 then N'事务已结束。该状态用于只读事务' when 4 then N'已对分布式事务启动提交进程' when 5 then N'事务处于准备就绪状态且等待解析' when 6 then N'事务已提交' when 7 then N'事务正在被回滚' when 0 then N'事务已回滚' end transaction_state, client_net_address, client_tcp_port, program_name, t2.text from sys.dm_tran_active_transactions t1 join ( select a.session_id, transaction_id, client_net_address, client_tcp_port, text,c.program_name from sys.dm_tran_session_transactions a join ( select session_id,a2.text,client_net_address,client_tcp_port from sys.dm_exec_connections a1 cross apply sys.dm_exec_sql_text(a1.most_recent_sql_Handle) a2 ) b on a.session_id=b.session_id left join sys.dm_exec_sessions c on a.session_id=c.session_id where is_user_transaction=1 )t2 on t1.transaction_ID=t2.transaction_ID ORDER BY t2.transaction_id
--死锁相关,转自http://www.blogjava.net/parable-myth/archive/2007/10/15/153010.html if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_lockinfo]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[p_lockinfo] GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO /*--处理死锁 查看当前进程,或死锁进程,并能自动杀掉死进程 因为是针对死的,所以如果有死锁进程,只能查看死锁进程 当然,你可以通过参数控制,不管有没有死锁,都只查看死锁进程 感谢: caiyunxia,jiangopen 两位提供的参考信息 --邹建 2004.4--*/ /*--调用示例 exec p_lockinfo --*/
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
create proc p_lockinfo @kill_lock_spid bit=1, --是否杀掉死锁的进程,1 杀掉, 0 仅显示 @show_spid_if_nolock bit=1 --如果没有死锁的进程,是否显示正常进程信息,1 显示,0 不显示 as declare @count int,@s nvarchar(1000),@i int select id=identity(int,1,1),标志, 进程ID=spid,线程ID=kpid,块进程ID=blocked,数据库ID=dbid, 数据库名=db_name(dbid),用户ID=uid,用户名=loginame,累计CPU时间=cpu, 登陆时间=login_time,打开事务数=open_tran, 进程状态=status, 工作站名=hostname,应用程序名=program_name,工作站进程ID=hostprocess, 域名=nt_domain,网卡地址=net_address into #t from( select 标志='死锁的进程', spid,kpid,a.blocked,dbid,uid,loginame,cpu,login_time,open_tran, status,hostname,program_name,hostprocess,nt_domain,net_address, s1=a.spid,s2=0 from master..sysprocesses a join ( select blocked from master..sysprocesses group by blocked )b on a.spid=b.blocked where a.blocked=0 union all select '|_牺牲品_>', spid,kpid,blocked,dbid,uid,loginame,cpu,login_time,open_tran, status,hostname,program_name,hostprocess,nt_domain,net_address, s1=blocked,s2=1 from master..sysprocesses a where blocked<>0 )a order by s1,s2 select @count=@@rowcount,@i=1 if @count=0 and @show_spid_if_nolock=1 begin insert #t select 标志='正常的进程', spid,kpid,blocked,dbid,db_name(dbid),uid,loginame,cpu,login_time, open_tran,status,hostname,program_name,hostprocess,nt_domain,net_address from master..sysprocesses set @count=@@rowcount end if @count>0 begin create table #t1(id int identity(1,1),a nvarchar(30),b Int,EventInfo nvarchar(255)) if @kill_lock_spid=1 begin declare @spid varchar(10),@标志 varchar(10) while @i<=@count begin select @spid=进程ID,@标志=标志 from #t where id=@i insert #t1 exec('dbcc inputbuffer('+@spid+')') if @标志='死锁的进程' exec('kill '+@spid) set @i=@i+1 end end else while @i<=@count begin select @s='dbcc inputbuffer('+cast(进程ID as varchar)+')' from #t where id=@i insert #t1 exec(@s) set @i=@i+1 end select a.*,进程的SQL语句=b.EventInfo from #t a join #t1 b on a.id=b.id end GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO
----索引执行情况
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SELECT objects.name , databases.name , indexes.name , user_seeks , user_scans , user_lookups , partition_stats.row_count FROM sys.dm_db_index_usage_stats stats LEFT JOIN sys.objects objects ON stats.object_id = objects.object_id LEFT JOIN sys.databases databases ON databases.database_id = stats.database_id LEFT JOIN sys.indexes indexes ON indexes.index_id = stats.index_id AND stats.object_id = indexes.object_id LEFT JOIN sys.dm_db_partition_stats partition_stats ON stats.object_id = partition_stats.object_id AND indexes.index_id = partition_stats.index_id WHERE 1 = 1 --AND databases.database_id = 7 AND objects.name IS NOT NULL AND indexes.name IS NOT NULL AND user_scans>0 ORDER BY user_scans DESC ,stats.object_id ,indexes.index_id
----最占用CPU
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SELECT TOP 100 execution_count, total_logical_reads /execution_count AS [Avg Logical Reads], total_elapsed_time /execution_count AS [Avg Elapsed Time], db_name(st.dbid) as [database name], object_name(st.dbid) as [object name], object_name(st.objectid) as [object name 1], SUBSTRING(st.text, (qs.statement_start_offset / 2) + 1, ((CASE statement_end_offset WHEN - 1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1) AS statement_text FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st WHERE execution_count > 100 AND qs.creation_time > dateadd(day,-3,getdate()) ORDER BY 1 DESC;
---执行时间最长的命令
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SELECT TOP 10 COALESCE(DB_NAME(st.dbid), DB_NAME(CAST(pa.value as int))+'*','Resource') AS DBNAME, SUBSTRING(text, -- starting value for substring CASE WHEN statement_start_offset = 0 OR statement_start_offset IS NULL THEN 1 ELSE statement_start_offset/2 + 1 END, -- ending value for substring CASE WHEN statement_end_offset = 0 OR statement_end_offset = -1 OR statement_end_offset IS NULL THEN LEN(text) ELSE statement_end_offset/2 END - CASE WHEN statement_start_offset = 0 OR statement_start_offset IS NULL THEN 1 ELSE statement_start_offset/2 END + 1 ) AS TSQL, total_logical_reads/execution_count AS AVG_LOGICAL_READS FROM sys.dm_exec_query_stats CROSS APPLY sys.dm_exec_sql_text(sql_handle) st OUTER APPLY sys.dm_exec_plan_attributes(plan_handle) pa WHERE attribute = 'dbid' ORDER BY AVG_LOGICAL_READS DESC ;
----缺索引
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SELECT TOP 30 [Total Cost] = ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans),0) , avg_user_impact, TableName = statement, [EqualityUsage] = equality_columns , [InequalityUsage] = inequality_columns, [Include Cloumns] = included_columns FROM sys.dm_db_missing_index_groups g INNER JOIN sys.dm_db_missing_index_group_stats s ON s.group_handle = g.index_group_handle INNER JOIN sys.dm_db_missing_index_details d ON d.index_handle = g.index_handle ORDER BY [Total Cost] DESC;
--经常更新却很少使用的索引
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT DB_NAME() AS DatabaseName , SCHEMA_NAME(o.Schema_ID) AS SchemaName , OBJECT_NAME(s.[object_id]) AS TableName , i.name AS IndexName , s.user_updates , s.system_seeks + s.system_scans + s.system_lookups AS [System usage] INTO #TempUnusedIndexes FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id INNER JOIN sys.objects o ON i.object_id = O.object_id WHERE 1=2 EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempUnusedIndexes SELECT TOP 20 DB_NAME() AS DatabaseName , SCHEMA_NAME(o.Schema_ID) AS SchemaName , OBJECT_NAME(s.[object_id]) AS TableName , i.name AS IndexName , s.user_updates , s.system_seeks + s.system_scans + s.system_lookups AS [System usage] FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id INNER JOIN sys.objects o ON i.object_id = O.object_id WHERE s.database_id = DB_ID() AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 AND s.user_seeks = 0 AND s.user_scans = 0 AND s.user_lookups = 0 AND i.name IS NOT NULL ORDER BY s.user_updates DESC' SELECT TOP 20 * FROM #TempUnusedIndexes WHERE DatabaseName = 'agilepoint50' ORDER BY [user_updates] DESC DROP TABLE #TempUnusedIndexes
---维护代价最高的索引
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT DB_NAME() AS DatabaseName , SCHEMA_NAME(o.Schema_ID) AS SchemaName , OBJECT_NAME(s.[object_id]) AS TableName , i.name AS IndexName , (s.user_updates ) AS [update usage] , (s.user_seeks + s.user_scans + s.user_lookups) AS [Retrieval usage] , (s.user_updates) - (s.user_seeks + s.user_scans + s.user_lookups) AS [Maintenance cost] , s.system_seeks + s.system_scans + s.system_lookups AS [System usage] , s.last_user_seek , s.last_user_scan , s.last_user_lookup INTO #TempMaintenanceCost FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id INNER JOIN sys.objects o ON i.object_id = O.object_id WHERE 1=2 EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempMaintenanceCost SELECT TOP 20 DB_NAME() AS DatabaseName , SCHEMA_NAME(o.Schema_ID) AS SchemaName , OBJECT_NAME(s.[object_id]) AS TableName , i.name AS IndexName , (s.user_updates ) AS [update usage] , (s.user_seeks + s.user_scans + s.user_lookups) AS [Retrieval usage] , (s.user_updates) - (s.user_seeks + user_scans + s.user_lookups) AS [Maintenance cost] , s.system_seeks + s.system_scans + s.system_lookups AS [System usage] , s.last_user_seek , s.last_user_scan , s.last_user_lookup FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id INNER JOIN sys.objects o ON i.object_id = O.object_id WHERE s.database_id = DB_ID() AND i.name IS NOT NULL AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 AND (s.user_seeks + s.user_scans + s.user_lookups) > 0 ORDER BY [Maintenance cost] DESC' SELECT top 20 * FROM #TempMaintenanceCost WHERE DatabaseName ='agilepoint50' ORDER BY [Maintenance cost] DESC DROP TABLE #TempMaintenanceCost
---表及数据库的空间占用
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
set nocount on declare @db varchar(20) set @db = db_name() dbcc updateusage(@db) with no_infomsgs go create table #tblspace ( 数据表名称 varchar(50) null, 记录笔数 int null, 保留空间 varchar(15) null, 数据使用空间 varchar(15) null, 索引使用空间 varchar(15) null, 未使用空间 varchar(15) null, ) declare @tblname varchar(50) declare curtbls cursor for select table_name from information_schema.tables where table_type = 'base table' open curtbls Fetch next from curtbls into @tblname while @@fetch_status = 0 begin insert #tblspace exec sp_spaceused @tblname fetch next from curtbls into @tblname end close curtbls deallocate curtbls select * from #tblspace order by convert(int,left(保留空间,len(保留空间)-2)) desc drop table #tblspace
--检测死锁
--如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程?
--这时我们可以使用以下存储过程来检测,就可以查出引起死锁的进程和SQL语句。SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
use master go create procedure sp_who_lock as begin declare @spid int,@bl int, @intTransactionCountOnEntry int, @intRowcount int, @intCountProperties int, @intCounter int create table #tmp_lock_who ( id int identity(1,1), spid smallint, bl smallint) IF @@ERROR<>0 RETURN @@ERROR insert into #tmp_lock_who(spid,bl) select 0 ,blocked from (select * from sysprocesses where blocked>0 ) a where not exists(select * from (select * from sysprocesses where blocked>0 ) b where a.blocked=spid) union select spid,blocked from sysprocesses where blocked>0 IF @@ERROR<>0 RETURN @@ERROR -- 找到临时表的记录数 select @intCountProperties = Count(*),@intCounter = 1 from #tmp_lock_who IF @@ERROR<>0 RETURN @@ERROR if @intCountProperties=0 select '现在没有阻塞和死锁信息' as message -- 循环开始 while @intCounter <= @intCountProperties begin -- 取第一条记录 select @spid = spid,@bl = bl from #tmp_lock_who where Id = @intCounter begin if @spid =0 select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10)) + '进程号,其执行的SQL语法如下' else select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '被' + '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下' DBCC INPUTBUFFER (@bl ) end -- 循环指针下移 set @intCounter = @intCounter + 1 end drop table #tmp_lock_who return 0 end
--杀死锁和进程
--如何去手动的杀死进程和锁?最简单的办法,重新启动服务。但是这里要介绍一个存储过程,通过显式的调用,可以杀死进程和锁。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
use master go if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[p_killspid] GO create proc p_killspid @dbname varchar(200) --要关闭进程的数据库名 as declare @sql nvarchar(500) declare @spid nvarchar(20) declare #tb cursor for select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname) open #tb fetch next from #tb into @spid while @@fetch_status=0 begin exec('kill '+@spid) fetch next from #tb into @spid end close #tb deallocate #tb go
--用法
exec p_killspid 'newdbpy'
--查看锁信息
--如何查看系统中所有锁的详细信息?在企业管理管理器中,我们可以看到一些进程和锁的信息,这里介绍另外一种方法。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
create table #t(req_spid int,obj_name sysname) declare @s nvarchar(4000) ,@rid int,@dbname sysname,@id int,@objname sysname declare tb cursor for select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid from master..syslockinfo where rsc_type in(4,5) open tb fetch next from tb into @rid,@dbname,@id while @@fetch_status=0 begin set @s='select @objname=name from ['+@dbname+']..sysobjects where id=@id' exec sp_executesql @s,N'@objname sysname out,@id int',@objname out,@id insert into #t values(@rid,@objname) fetch next from tb into @rid,@dbname,@id end close tb deallocate tb select 进程id=a.req_spid ,数据库=db_name(rsc_dbid) ,类型=case rsc_type when 1 then 'NULL 资源(未使用)' when 2 then '数据库' when 3 then '文件' when 4 then '索引' when 5 then '表' when 6 then '页' when 7 then '键' when 8 then '扩展盘区' when 9 then 'RID(行 ID)' when 10 then '应用程序' end ,对象id=rsc_objid ,对象名=b.obj_name ,rsc_indid from master..syslockinfo a left join #t b on a.req_spid=b.req_spid go drop table #t