數據庫系統嚴重依賴服務器的資源:CPU,內存和硬盤IO,通常情況下,內存是數據的讀寫性能最高的存儲介質,但是,內存的價格昂貴,這使得系統能夠配置的內存容量受到限制,不能大規模用於數據存儲;並且內存是易失性的,不能持久化存儲數據,這使得內存只能作為運行時的高速緩存,而硬盤是永久存儲數據的理想介質,價格低廉,在系統停電時,能夠保持數據不丟失。但是,硬盤是低速的存儲介質,輸入和輸出(IO)速度比內存低很多。因此,在實際運行的數據庫系統中,相對於內存而言,硬盤的IO有更大可能性成為系統性能的瓶頸。
內存和硬盤都是存儲資源和IO資源,木桶原理適用於SQL Server內部的資源爭用,資源的短板就是系統的瓶頸。由於內存的容量相對較小,IO速度快,因此,內存更有可能成為爭用的存儲資源;而硬盤容量大,IO速度快,因此,硬盤更有可能成為系統爭用IO資源。SQL Server為了平衡存儲和IO資源的爭用,在把數據從硬盤讀取到內存后,會把數據緩存到內存中,當重復訪問數據時,不需要從硬盤,而是直接從內存中獲取。由於這個機制,為系統配置足夠多的內存可以最小化硬盤IO,因為硬盤讀取數據的速度遠遠低於內存,所以,盡可能減少硬盤IO可以在很大程度上提供系統的性能。
一,硬盤IO的延時
對於SQL Server數據庫系統,限制查詢響應的主要因素是硬盤的延時,根據硬盤的物理構造(磁道和扇區),延時可以分為尋道延時和旋轉延時:
- 尋道延時:硬盤的物理刺頭移動並定位到所需數據的時間,
- 旋轉延時:硬盤旋轉到所需數據的時間,通常用MB/S,或IO吞吐量來衡量
在OLTP系統中,數據更新操作較多,每次讀取的數據量少,目標數據的位置相對隨機(隨機讀寫),因此,對於尋道延時要求更高,硬盤需要花費更多的尋道時間。
在DSS/DW系統中,事務的運行時間更長,數據相對靜態,不常更新,讀操作比寫操作的要求更高,順序讀操作占比很高,因此,IO吞吐量更重要,可以通過硬盤的盤面來增加順序訪問的IO吞吐量。
二,根據WaitType偵測IO性能
SQL Server引擎把IO作為一個資源來看待,在多任務的現代數據庫系統中,同一時刻會接收到很多查詢請求,每一個查詢請求都需要申請系統資源(CPU、內存和IO),才能繼續執行下去,然而系統的資源是有限的,當查詢爭用資源時,有些查詢請求資源得到滿足,順利執行下去,有些查詢請求的資源得不到滿足,該查詢就被阻塞,處於等待資源分配的狀態。當出現IO性能問題時,查詢語句會被硬盤IO阻塞,這使得執行計划被迫掛起(或阻塞)來等待資源,SQL Server通過DMV來顯示系統運行的狀態,用等待類型來表示不同的阻塞信息。
1,數據文件的IO
如果SQL Server 出現 IO 性能問題,那么在SQL Server 內部通過DMV sys.dm_exec_requests的wait_type,來反饋 IO 問題。如果查詢請求的wait_type長時間處於PageIOLatch_XX,那么說明系統不能很快把數據讀取到內存中。
PAGEIOLATCH_xx :用於描述數據頁的IO爭用,說明系統正在從硬盤加載數據到內存的Buffer Pool中
當SQL Server 要去讀或寫一個Page的時候,首先會在Buffer Pool里尋找,如果在Buffer Pool中找到了,那么讀寫操作會繼續進行,沒有任何等待。如果沒有找到,那么SQL Server 就會設置Wait_Type為PageIOLatch_EX(寫)或PageIOLatch_SH(讀),然后發起一個異步IO操作,將頁面讀入Buffer Pool中,在IO沒有完成之前,Request將會保持在PageIOLatch_EX(寫)或PageIOLatch_SH(讀)的等待狀態。IO消耗的時間越長,等待的時間越長。
2,日志文件的寫入
日志文件以寫為主,工作量由修改命令激發的事務數量決定。當SQL Server要寫事務到日志文件時,如果Disk 不能及時完成IO請求,那么事務就無法提交,SQL Server 不得不進入WriteLog 等待狀態,直到事務被成功記錄到日志文件中,才會提交當前的事務。
如果request經常出現WriteLog的Wait type,說明事務日志的寫請求不能被Disk及時完成,這種情況,對SQL Server 整體性能影響較大。
WRITELOG:在數據被修改時,在Log Cache和Buffer Cache中都會有記錄,如果在Log Cache中的數據在checkpoint時寫入硬盤,就會發生這種等待。
LOGBUFFER等待:很少出現,當一個任務正在等待存儲日志到Log Buffer中時,就會出現LOGBUFFER等待,出現這種等待,說明日志所在的硬盤無法響應請求。如果把日志文件放在一個非常慢的硬盤上,而數據文件放在一個非常快的硬盤上,就會出現這種等待。
3,AYSNC_IO_COMPLIETION和IO_COMPLIETION也是IO瓶頸的潛在指標
- AYSNC_IO_COMPLIETION:標識任務正在等待IO請求來完成操作,當一個應用程序連接SQL Server,在處理數據時變得非常慢,很可能就會出現這種類型的等待。
- IO_COMPLIETION:發生在一個任務正在等待用於非數據頁IO的IO操作上,非數據頁,一般是指日志文件,通常發生在修改大量修改,或者內存中存在大量的臟數據時。
三,影響讀寫性能的因素
數據庫系統對IO的性能依賴較高,那么影響數據庫系統讀寫性能的因素有哪些呢?
1,物理硬盤的IO能力
機械硬盤的IO速度沒有固態硬盤快,可以考慮把數據庫系統的機械硬盤更新為固態硬盤。
2,內存對硬盤IO的影響
在SQL Server Engine 訪問數據時,如果相應的data不存在於Buffer Pool,那么Buffer Manager 從Disk中的Data File(mdf 或 ndf)中將相應的data page讀取到內存中。SQL Server 將data page緩存起來。理想情況下,只要SQL Server能夠使用的內存充足,SQL Server 會將所有讀取到內存的中Data Page緩存到Buffer Pool中。對於讀取操作,只要相應的數據都緩存在內存中,Select 就不會有任何硬盤IO。
當Buffer Pool空間不足時,SQL Server 激活 LazyWriter,主動將內存中一些很久沒有使用的Data Cache和 Plan Cache 清除,mark為Free buffer,供其它Data Page使用。如果這些Page上的修改還沒有被CheckPoint寫回Disk,那么LazyWrite會將其寫回。
3,碎片和壓縮
如果數據頁面或index 頁面的碎片很多,每個頁面存儲的數據行較少,那么SQL Server 需要讀寫更多的Page。如果數據在頁面里存儲的非常緊湊,存儲相同數據所消耗的Page越少,並且可以充分利用SQL Server 預讀的優勢,減少IO。
壓縮技術不僅使數據占用的Disk 空間減少,而且能夠減少IO。由於數據在寫入Disk之間經過壓縮處理,存儲相同數據所消耗的Page減少,讀取的Data Page會減少。壓縮技術在一定程度上能夠降低IO,但需要付出一定的代價:額外消耗少量的CPU和內存來解壓縮。
4,利用多個物理硬盤實現Data File的並發讀寫
在DB中的FileGroup 創建多個File,將這些File存放到不同的Physical Disk上。File 分布到不同的Physical Disk上,IO也會分布到不同的Physical Disk上,這樣能夠實現數據的並發讀取,提高讀取性能。
對於日志文件,SQL Server會頻繁的寫事務日志。只要數據庫發生修改,就會不斷地寫入日志文件。如果不能及時完成日志文件的IO,會導致事務的延遲提交,對性能的影響較大,所以,盡量將日志文件放到寫入速度快的Disk上。SQL Server 順序寫事務日志,在一個時間點,SQL Server 只會寫一個日志文件。在不同的Physical Disk上創建多個log file對性能基本沒有幫助。
5,工作負載
日志文件以寫為主,工作量由修改命令申請的事務數量決定,日志文件是順序寫的,寫入速度快於隨機寫。如果日志記錄不能及時寫入,那么Request會處於WriteLog等待狀態,對系統整體性能影響較大。
數據文件寫入的數據量由修改量決定,SQL Server除了設置bulk logged 恢復模式之外,沒有太大的調整選項。
數據文件讀取的數據量,由訪問的數據量和Buffer Pool中緩存的數據量共同決定。如果訪問的數據量減少或者內存緩存區增加,都可以降低SQL Server 從Physical Disk讀取的Data Page數量。在內存不變的情況下,可以通過優化查詢語句,減少數據訪問量,來提高SQL Server 數據文件的讀取性能。
四,硬盤IO的性能優化
硬盤IO的性能調優,通常來說,跟Buffer Pool的大小和數據的分布有關
1, Buffer Pool
Buffer Pool是SQL Server數據庫系統的緩沖池,用於緩存從硬盤讀取的數據頁。當SQL Server所需的數據不在內存的Buffer Pool中時,就會觸發硬盤IO,把數據從硬盤中的文件中讀取到內存中的Buffer Pool中。如果所需的數據存在於Buffer Pool中,SQL Server直接從內存中獲取數據,不會觸發任何硬盤的IO操作。因此,內存容量足夠大,硬盤IO將會足夠小。如果系統存在內存壓力,那么SQL Server將會頻繁地觸發硬盤IO,從硬盤文件中獲取數據,這將會增加查詢的響應時間。
2, 多硬盤並發IO
在存儲數據時,把數據分布在不同的物理硬盤上,在讀寫數據時,可以把工作負載分擔到不同的物理硬盤上,多個硬盤並發處理數據,將會大大降低數據的讀寫時間。
因此,在設計數據庫系統時,應該盡量把數據分布到不同的物理硬盤上,並且每個硬盤上的數據量保持均衡,這樣,才能最大化利用多硬盤的優勢,實現數據的讀寫時間最小化。
3,日志文件
當修改數據時,事務會被記錄到日志文件中,事務日志的寫入速度,直接影響了數據更新查詢語句的執行效率。當數據庫中存在大量的修改操作時,應該把日志文件存儲到IO性能最優的硬盤上,以減少日志文件寫入的時間延遲。
4,tempdb數據庫文件
tempdb是數據庫實例中最繁忙的數據庫了,在查詢語句執行的過程中,查詢語句創建的各種臨時表,系統創建的中間表都位於tempdb中,tempdb的數據文件和日志文件的讀寫性能,直接影響了查詢語句的執行時間,應該把tempdb數據庫的數據文件部分到不同的物理硬盤中,並且把tempdb的日志文件存放到IO性能最優的硬盤上去。
簡而言之,對於數據庫系統的優化配置是:
- 在OLTP系統中,合理的配置是把數據文件,日志文件和tempdb的文件分別存放到不同的物理硬盤上,從而分攤硬盤的IO爭用。
- 在OLAP系統中,事務運行時間長,規模大,數據相對靜態,每次返回的數據量較大,對IO吞吐量的要求較高,因此,盡可能分攤硬盤的IO爭用。
5,創建合適的索引
如果一個查詢需要進行表掃描,一般是因為缺失合適的索引或索引統計信息過時,過多的掃描操作會引起內存不足,使得緩存中的數據或執行計划被清除(或者被轉移到硬盤),然后從硬盤加載數據到內存。理想情況下,常用的數據應該盡可能久地駐留在內存中,避免不必要的內存活動。
創建合適的索引,並保證統計信息及時更新,能夠避免不必要的表掃描,只加載小的數據集,能夠減少IO操作的次數,優化IO性能。
6,數據壓縮
數據壓縮會使得相同的存儲空間能夠存儲更多的數據量,一次IO操作能夠加載更多的數據,這也能減少IO操作的次數,優化IO性能。
五,IO統計
IO請求的等待和掛起,數據庫引擎記錄對數據文件和日志文件的IO操作,緩存到函數:sys.dm_io_virtual_file_stats,對於數據文件,數據的物理讀操作更為重要;對於日志文件,數據的讀寫操作都重要:
- io_stall_read_ms:等待讀操作的時間
- io_stall_write_ms:等待寫操作的時間
如果硬盤繁忙,數據庫引擎發送的IO請求,可能會被IO子系統掛起(pending),數據庫引擎把pending的IO請求緩存到視圖:sys.dm_io_pending_io_requests,
- io_pending:指定是否有IO請求掛起或完成
1,查看數據庫文件的IO和等待IO完成的時間
select db_name(vfs.database_id) as db_name, --vfs.file_id, mf.name as file_name, mf.type_desc as file_type, vfs.sample_ms/1000/60/60 as sample_h, vfs.io_stall_read_ms/vfs.num_of_reads as avg_stall_read_ms, vfs.io_stall_write_ms/vfs.num_of_writes as avg_stall_write_ms, vfs.num_of_reads as physical_reads, vfs.num_of_bytes_read/vfs.num_of_reads/1024 as avg_read_kb, vfs.num_of_writes as physical_writes, vfs.num_of_bytes_written/vfs.num_of_writes/1024 as avg_written_kb, cast(vfs.size_on_disk_bytes/1024/1024/1024.0 as decimal(10,2)) as disk_size_gb, --cast(mf.size/1024*8/1024.0 as decimal(10,2)) as file_size_gb, vfs.file_handle from sys.master_files mf cross apply sys.dm_io_virtual_file_stats(mf.database_id,mf.file_id) as vfs where mf.database_id=db_id() --current db order by avg_stall_read_ms desc ,avg_stall_write_ms desc
2,查看pending的IO請求
select db_name(vfs.database_id) as db_name, --vfs.file_id, mf.name as file_name, pr.io_type, sum(pr.io_pending_ms_ticks) as io_pending_ms, pr.io_pending from sys.dm_io_virtual_file_stats(null,null) vfs inner join sys.dm_io_pending_io_requests as pr on vfs.file_handle=pr.io_handle inner join sys.master_files mf on vfs.database_id=mf.database_id and vfs.file_id=mf.file_id group by vfs.database_id, mf.file_id, mf.name, pr.io_type, pr.io_pending order by vfs.database_id, mf.name
3,計划緩存中的邏輯寫排名
select p.name as sp_name ,s.total_logical_reads ,s.total_logical_writes ,s.total_physical_reads ,s.total_elapsed_time ,s.total_worker_time ,s.cached_time ,s.execution_count ,s.type ,s.type_desc from sys.procedures p inner join sys.dm_exec_procedure_stats s on p.object_id=s.object_id where s.database_id=DB_ID() and s.total_logical_writes>0 order by s.total_logical_writes
參考文檔:
Windows Performance Monitor Disk Counters Explained
High Avg Disk Queue Length and finding the Cause
Disk Queue Length vs. Disk Latency Times: Which is Best for Measuring Database Performance