Setting Optimize for ad hoc workloads được sử dụng để làm gì ?

Mục đích : 

Mỗi lần thực thi một câu query, Database Engine sẽ tạo ra compiled plan, việc tạo compiled plan tốn rất nhiều chi phí nên compiled plan sau khi được tạo ra sẽ được lưu lại ở cach store để sử dụng cho tiếp theo, tuy nhiên hầu hết những câu query là ad hoc, chỉ được sử dụng một lần, vì thế việc lưu lại compiled plan dường như chỉ tốn thêm vùng nhớ chứ không phục vụ cho mục đích sử dụng lại ở lần chạy sau, vì vậy sql server đã thêm configuration option :  optimize for ad hoc workloads để khắc phục đều này. 

Khi option được bật sql server chỉ lưu lại một “stub” của query plan trong lần đầu tiên của mọi ad hoc query được compiled và chỉ sau lần compilation thứ 2 thì stub được thay thế bằng một full query plan

Ví dụ minh họa

Để giải thích rõ tác động khi bật option này chúng ta cùng đi qua một ví dụ sau

Trước khi thực hiện khảo sát, ta cần bật  setting : optimize for ad hoc workloads

để bật setting optimize for ad hoc workloads ta có thể dùng 1 trong 2 cách 

Cách 1 : sử dụng TSQ 

EXEC sp_configure 'optimize for ad hoc workloads', 1;
RECONFIGURE;

Cách 2 : bật trực tiếp bằng SQL Server Management Studio Management Studio 

Ví dụ:

CREATE TABLE Test (Id INT, V CHAR(10))
GO

INSERT INTO Test (Id, V)
VALUES (1, 'V1'), (2, 'V2'), (3, 'V3')
GO

Chúng ta cùng chạy câu query sau

DBCC FREEPROCCACHE
GO

Câu lệnh DBCC FREEPROCCACHE sẽ  xóa tất cả cách thành phần được lưu trong cache plan, lưu ý việc chạy câu lệnh này chỉ nhằm phục vụ cho mục đích test nên chỉ sử dụng nó ở môi trường test

SELECT * FROM Test
GO

SELECT p.*, st.text, q.query_plan
FROM sys.dm_exec_cached_plans p 
	CROSS APPLY sys.dm_exec_sql_text (p.plan_handle) AS st
	CROSS APPLY sys.dm_exec_query_plan(p.plan_handle) AS q
WHERE st.text LIKE '%Test%' AND st.text NOT LIKE '%dm_exec_cached_plans%'

Kết quả lần chạy đầu tiên

Hình 1

Trong lần chạy đầu tiên : cacheobjtype = Compiled Plan Stub và query_plan là NULL, đều này nói cho chúng ta biết Compiled Plan Stub đã được tạo ra và query_plan thực chất không được lưu lại ở lần chạy đầu tiên, mục đích của Compiled Plan Stub thực chất là giữ chỗ cho query plan ở những lần chạy tiếp theo, để giải thích rõ mục đích này chúng ta cùng khảo sát bằng câu query sau đây

SELECT name, type, buckets_count FROM sys.dm_os_memory_cache_hash_tables WHERE name  =  'SQL Plans'

Hình 2

SELECT *
FROM sys.dm_exec_plan_attributes (0x06000D003F038C0700C969DB8401000001000000000000000000000000000000000000000000000000000000)

0x06000D003F038C0790C269DB8401000001000000000000000000000000000000000000000000000000000000 là plan_hanle được lấy từ kết quả ở hình 1

Hình 3

Ad hoc query sau khi được thực thi thì compiled plan sẽ được lưu vào cache store có tên là CACHESTORE_SQLCP, sql server dùng hash table có tên là SQL Plans  để quản lý cache store này

,hash table này gồm nhiều buckets (40009 bucket theo kết quả trả ra ở hình 1), cached plan sẽ được quản lý bởi 1 trong số những bucket này, theo kết quả hình 1 trả ra thì ta thấy cached plan của câu query trên được quản lý bởi bucketId = 2710

Vì một bucket sẽ quản lý 0, 1 hoặc nhiều cached plan nên để tìm một cached plan cụ thể ở trong một bucket thì sql server sẽ dùng một bộ cache key tương ứng, ở hình 3 ta thấy với những attribute có Is_Cache_Key = 1 thì những attribute này được tính là 1 cache key trong bộ cache key, để biết đầy đủ thông tin ý nghĩa từng cache key bạn có thể xem ở link này : Cache Key

Nhìn vào kết quả hình 3  ta thấy với những attribute có Is_Cache_Key = 0 thì chỉ có attribute: sql_handle là có giá trị, những attribute khác mang giá trị mặc định là 0

Quay lại với mục đích của stub complied plan, nó dùng để giữ chỗ tức là khi tạo ra stub complied plan thì sql server sẽ tạo ra bộ cache key tương ứng để lần chạy thứ hai, compiled plan được lưu sẽ sử dụng bộ cache key này, đồng thời stub complied plan cũng sẽ chứa giá trị sql_handle, có tác dụng để xác định được query text ứng với plan này

Chạy tiếp câu query trên lần thứ 2 

SELECT * FROM Test
GO

SELECT p.*, st.text, q.query_plan
FROM sys.dm_exec_cached_plans p 
	CROSS APPLY sys.dm_exec_sql_text (p.plan_handle) AS st
	CROSS APPLY sys.dm_exec_query_plan(p.plan_handle) AS q
WHERE st.text LIKE '%Test%' AND st.text NOT LIKE '%dm_exec_cached_plans%'

Kết quả lần chạy thứ 2 

Hình 4

Từ kết quả lần chạy thứ hai ta thấy cacheobjtype = Compiled Plan và query_plan không còn là NULL nữa, bucketId = 2710 giống với lần chạy đầu

Chạy câu query sau để khảo sát cache key

SELECT *
FROM sys.dm_exec_plan_attributes (0x06000D003F038C0790C269DB8401000001000000000000000000000000000000000000000000000000000000)

Tất cả attribute có Is_Cache_Key = 1 và sql_handle đều giống như giá trị trả ra đối với Compiled Plan Stub, tuy nhiên có một số khác biệt ở attribute : free_exec_context = 1, misses_exec_context = 1 không giống như lần trước vì ở đây đã lưu thông tin tương ứng chứ không còn là giá trị mặc định nữa

So sánh kích thước plan được lưu ở 2 lần chạy

  • Lần chạy đầu tiên : size_in_bytes : 456 bytes
  • Lần chạy thứ hai : size_in_bytes : 49152 bytes

Vì lần chạy thứ 2 lưu full query plan nên kích thước của nó lớn hơn rất nhiều so với lần chạy đầu tiên

Tổng kết

  • Setting : Optimize for ad hoc workloads được sử dụng để giảm kích thước của cache plan được lưu đối với ad hoc query chỉ chạy một lần
  • Khi Optimize for ad hoc workloads  được bật, lần chạy đầu tiên câu query, Compiled Plan Stub sẽ được tạo ra với mục đích giữ chỗ cho các lần chạy sau đó, từ lần chạy thứ 2 trở đi Compiled Plan Stub sẽ được thay thế bằng full query plan

Tài liệu tham khảo : 

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *