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

相關文章
相關標籤/搜索