性能調優1:緩存


在執行任何查詢時,SQL Server都會把數據讀取到內存,在使用完數據之后,數據不會被立即刪除,而是緩存在內存Buffer中,當再次獲取相同的數據時,如果所需數據全部緩存在內存中,那么SQL Server不會產生Disk IO操作(把數據從硬盤導入到內存),而是直接從內存中獲取數據。由於查詢內存中的數據,速度非常快,SQL Server引擎會立即返回查詢結果,緩存數據是是SQL Server的一種性能優化機制。

一,主要的內存消費者

緩存數據需要消耗內存,主要的內存消費者(Memory Consumer)是數據緩存、計划緩存、授予內存和日志緩存,SQL Server還會緩存其他類型的數據,這些數據占比較低,本文不作考慮。

1,數據緩存(Data Cache)

Data Cache是存儲數據頁(Data Page)的緩沖區,當SQL Server需要讀取數據文件(File)中的數據頁(Data Page)時,SQL Server會把整個Page都調入內存(內存中的一個Page叫做buffer),Page是數據訪問的最小單元。

當用戶修改了某個Page上的數據時,SQL Server 會先在內存中修改Buffer,但是不會立即將這個數據葉寫回硬盤,而是等到CheckPoint或lazy Writer進程運行時集中處理。當用戶讀取某個Page后,如果SQL Server沒有內存壓力,它不會在內存中刪除這個Page,因為內存中的數據頁始終存放着數據的最新狀態,如果有其他用戶使用這個Page,SQL Server 不需要從硬盤中讀取一次,節省語句執行的時間。理想情況是SQL Server將用戶需要訪問的所有數據都緩存在內存中,SQL Server 永遠不需要去硬盤讀取數據,只需要在CheckPoint 或 lazy Write運行時把修改過的頁面寫回硬盤即可。

Buffer Pool 用於管理數據緩存,可以從 sys.dm_os_buffer_descriptions 中查詢相關的信息。

2,查詢計划緩存(Query Plan Cache)

計划緩存用於存儲查詢語句和存儲過程的執行計划,便於計划的重用,因為編譯查詢語句產生執行計划是一個非常耗費資源的過程,如果執行計划被緩存起來,下次使用時就不需要重新編譯(Compile)和重新生成,SQL Server引擎會直接復用已緩存的執行計划,可以通過sys.dm_exec_cached_plans 來查詢計划緩存的信息。

對於Ad-Hoc查詢,有一個特殊的優化選項:

執行計划生成后會存儲在plan cache中,如果計划緩存從來都沒有被重用過,這將會造成內存資源的浪費。當執行代碼時,會產生一個hash值,用於匹配計划緩存中的hash值,相同的hash值代表語句是相同的。如果執行一個存儲過程,那么會按照存儲過程名稱來創建hash值;如果是執行Ad-Hoc代碼,那么會按照整個TSQL語句來創建Hash值,只有Ad-Hoc語句中有一點改變,都會產生不同的Hash值,導致執行計划無法重用。針對這類問題,可以考慮使用存儲過程或者參數化的Ad-Hoc。

“針對即席工作負載進行優化”是一個Server級別的性能優化選項,用於提高包含許多臨時批處理的工作負載的計划緩存的效率,如果把該選項設置為True,則數據庫引擎在首次編譯批處理時只保留計划緩存中的一個存根,而不是存儲整個執行計划。當再次調用該批處理時,數據庫引擎識別出該批處理在之前被執行過,進而從計划緩存中刪除該執行計划的存根,並把完全編譯的執行計划添加到計划緩存中。當非參數化的Ad-Hoc查詢較多時,可以避免計划緩存存儲過多的不會被復用的執行計划。

3,授予內存(Granted Memory)

授予內存是已經分配給查詢的那部分內存,可以通過sys.dm_exec_query_memory_grants查看,另外,RESOURCE_SEMAPHORE等待狀態是針對memory grant的,所以,如果在sys.dm_os_wait_stats 中看到這個等待類型存在很久,並且處於前列,說明系統當前無可用的內存分配給查詢,系統很有可能存在內存壓力。

4,Log Cache

在數據被修改時,SQL Server會記錄數據修改的日志,這些日志首先緩存在Log Cache中,然后積累到一定的數量或等很小的時間間隔后,寫入到日志文件中。通常來說,任何一個數據修改,在Log Cache和Buffer Cache中都會有記錄,Log Cache中的數據修改會在checkpoint被觸發時寫入到日志文件中去。

二,查看內存消耗

SQL Server的內存管理是一套完整的機制,只有內存書記員(Memory Clerk)能夠分配內存,Memory Clerk會記錄已經分配內存的數量,任何一個需要使用內存的對象,必須創建自己的Memory Clerk,並使用該Memory clerk來分配內存。

1,查看Memory clerk分配的內存量

每一個對象通過Memory Clerk來分配內存的,內存消費者Buffer Pool的clerk是MEMORYCLERK_SQLBUFFERPOOL,計划緩存的clerk是MEMORYCLERK_SQLQUERYPLAN

select memory_node_id, 
    type,
    pages_kb,
    virtual_memory_reserved_kb,
    virtual_memory_committed_kb,
    shared_memory_reserved_kb,
    shared_memory_committed_kb,
    page_size_in_bytes
from sys.dm_os_memory_clerks 
where type in( 'MEMORYCLERK_SQLQUERYPLAN','MEMORYCLERK_SQLBUFFERPOOL')

2,統計Memory Clerk分配的內存總量

select mc.type,mc.name,
    sum(mc.pages_kb) as AllocatedPages_KB,
    sum(mc.virtual_memory_reserved_kb) as VM_Reserved_KB,
    sum(mc.virtual_memory_committed_kb) as VM_Committed_KB,
    --sum(mc.shared_memory_reserved_kb) as ShareMem_Reserved_KB,
    --sum(mc.shared_memory_committed_kb) as ShareMem_Committed_KB,
    max(mc.page_size_in_bytes)/1024 as SinglePageSize_KB
from sys.dm_os_memory_clerks mc
group by mc.type,mc.name
order by AllocatedPages_KB desc,mc.type,mc.name

消耗內存較大的Clerk是:

  • MEMORYCLERK_SQLBUFFERPOOL:是Buffer Pool占用的內存大小
  • MEMORYCLERK_SQLQUERYPLAN:是計划緩存占用的內存大小
  • OBJECTSTORE_LOCK_MANAGER:鎖結構使用的內存,當發生嚴重的鎖阻塞時,這表明系統中,存儲大量鎖,造成鎖管理占用大量的內存;
  • CACHESTORE_OBJCP:觸發器和存儲過程等模塊(Module)的執行計划占用的緩存空間;
  • CACHESTORE_SQLCP:動態TSQL語句,即席(Adhoc)查詢和預編譯(Prepared) TSQL的執行計划緩存;
  • CACHESTORE_COLUMNSTOREOBJECTPOOL:列存儲索引(ColumnStore Index)占用的緩存

3,查看緩存中的數據頁

當數據頁從硬盤讀取到內存之后,該數據頁被復制到緩沖池(Buffer Pool),供SQL Server重用。每個緩存的數據頁都有一個緩存描述器(Buffer Descriptor),用戶唯一標識內存中的數據頁,在SQL Server實例中緩存的每一個數據頁,都能從 sys.dm_os_buffer_descriptors 查看緩存描述的信息。

select object_name(p.object_id) as object_name
    ,o.type_desc
    ,i.name as index_name
    ,count(0) as buffer_counts
    ,cast(sum(bd.free_space_in_bytes)/(8*1024.0)/count(0) as decimal(10,4))*100 as free_space_ratio
    ,sum(cast(bd.is_modified as int)) as dirty_pages
    ,sum(bd.row_count) as row_counts
from sys.allocation_units au 
inner join sys.dm_os_buffer_descriptors bd
    on au.allocation_unit_id=bd.allocation_unit_id
inner join sys.partitions p 
    on au.container_id=p.hobt_id
inner join sys.indexes i 
    on p.object_id=i.object_id and p.index_id=p.index_id
inner join sys.objects o
    on p.object_id=o.object_id
where bd.database_id=db_id()
    and o.type<>N'S'
group by p.object_id
    ,o.type_desc
    ,i.name 
order by buffer_counts desc
    ,object_name

4,查看計划緩存

產生執行計划是十分消耗CPU資源的,SQL Server會在內存的Plan Cache中存儲每個查詢計划(Query Plan),及其占用的內存空間,重用次數等信息。

select cp.objtype,cp.cacheobjtype,
    sum(cp.size_in_bytes) as TotalSize_B,
    COUNT(cp.bucketid) as CacheCounts,
    sum(cp.refcounts) as TotalRefCounts,
    sum(cp.usecounts) as TotalUseCounts
from sys.dm_exec_cached_plans cp 
group by cp.objtype,cp.cacheobjtype
order by TotalSize_B desc

5,查看各個數據占用的內存buffer

select iif(d.database_id=32767,'Resource DB',db_name(d.database_id)) as db
    ,sum(d.row_count) as row_count
    ,count(0) as buffer_pages
    ,count(0)*8/1024 as buffer_mb
from sys.dm_os_buffer_descriptors d
where d.database_id between 5 and 32767
group by d.database_id
order by buffer_mb desc

三,清空緩存

在調優存儲過程性能時,清空緩存是必需的,緩沖池(Buffer Pool)是SQL Server的緩存管理器,包含了SQL Server的絕大部分緩存數據(Cache),例如,執行計划緩存(Plan cache),數據緩存(Data cache)等。

清空緩存常用的命令有如下三個:

CHECKPOINT
DBCC DROPCLEANBUFFERS DBCC FREEPROCCACHE

Checkpoint和DBCC DROPCLEANBUFFERS 用於清理數據緩存(Data Cache)中的臟頁(dirty pages)和干凈頁(clean pages),而DBCC FREEPROCCACHE 用於清空所有的計划緩存(Plan Cache)。

1,清空數據緩存

checkpoint 用於將臟頁(Dirty Pages)寫入硬盤,臟頁(Dirty Pages)是指數據頁讀入緩存后,被修改過,導致內存中數據頁和硬盤中的數據頁中的內容不同;干凈頁(Clean Pages)是指數據頁被讀入緩存后,沒有被修改過,所以,內存中的數據頁和硬盤中的數據頁中的內容相同。不管是Dirty pages 還是 Clean pages 都是Data Cache,在性能調優時,都必須從內存中清理掉,否則,查詢性能將忽略掉數據從硬盤加載到內存的IO消耗,影響查詢語句的執行情況。

CHECKPOINT 命令用於產生冷緩存(Cold buffer Cache),該命令將當前數據庫產生的所有臟頁寫入到硬盤,並清理內存buffer;在執行CHECKPOINT命令之后,執行 DBCC DROPCLEANBUFFERS 用於從緩沖池中清空所有的干凈頁。

在性能測試時,使用DBCC DROPCLEANBUFFERS從SQLSERVER的數據緩存池中清除所有的clean緩存數據,需要注意的是該命令只移走干凈的緩存,不移走臟緩存。因此,在執行這個命令前,應該先執行CheckPoint,將所有臟頁寫入磁盤,這樣在運行DBCC RROPCLEANBUFFERS 時,可以保證所有的數據緩存被清理,而不是其中的一部分。

2,清空計划緩存

計划緩存(Plan Cache)用於緩存查詢語句的執行計划,每一條查詢語句在執行之后,其查詢計划都會緩存Plan Cache中。在產品環境中,不要輕易清理掉Plan Cache。如果檢測到某個Plan Cache產生參數嗅探問題,導致性能十分低下,推薦修改查詢語句,重新編譯存儲過程,以單獨刷新該SP的計划緩存。

DBCC FREEPROCCACHE [ ( { plan_handle | sql_handle} ) ]

計划緩存,之前叫做過程緩存(procedure cache),執行DBCC FREEPROCCACHE 命令,釋放所有的計划緩存,這會導致存儲過程,AdHoc查詢等必須重新編譯,產生新的計划緩存。

 

附:冷緩存,熱緩存,臟緩存和干凈緩存名詞解釋:

  • 凈緩存頁(Clean Buffer) 是指內存中未被修改的數據頁,DBCC DROPCLEANBUFFERS 用於從緩沖池(Buffer Pool)移除干凈頁,釋放Buffer。
  • 臟緩存頁(Dirty Buffer)是指數據頁在內存中被修改,但是還沒有寫入到硬盤中,導致硬盤中的數據不同於內存,通常情況下,臟頁通過CHECKPOINT進程來自動同步,CHECKPOINT 將臟頁數據寫入到硬盤中,使內存和硬盤文件中的數據保持一致,能夠減少數據還原的時間。
  • 冷緩存頁(Cold Buffer)是指,在數據處理階段,最近沒有被使用的緩存頁。
  • 熱緩存頁(Hot Buffer)是指,在數據處理階段,最近經常被使用的緩存頁。

參考文檔:

sys.dm_os_buffer_descriptors (Transact-SQL)

What is a COLD, DIRTY or CLEAN Buffer


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM