SE の雑記

SQL Server の情報をメインに Microsoft 製品の勉強内容を日々投稿

MAXDOP は「タスクの並列度」であることを確認してみる

without comments

先日投稿した SQL Server のスレッドおよびタスクのアーキテクチャ では、ベースとした、スレッドおよびタスクのアーキテクチャ ガイド の次の記載を引用しました。

最新の SQL Server / SQL Database であれば、これを確認するのはそれほど難しくありませんので書いておこうかと。

使用するのは以前の投稿に引き続きこちらのスクリプトです。

[System.Data.SqlClient.SqlConnection]::ClearAllPools()
$con = New-Object System.Data.SqlClient.SqlConnection("Server=localhost;Integrated Security=SSPI;database=tpch;Application Name=SQLTaskThread")
$con.Open()
Write-Host "Connection Open Compilete"
Read-Host
$sql = @"
/* TPC_H Query 2 - Minimum Cost Supplier */
SELECT TOP 100 S_ACCTBAL, S_NAME, N_NAME, P_PARTKEY, P_MFGR, S_ADDRESS, S_PHONE, S_COMMENT
FROM PART, SUPPLIER, PARTSUPP, NATION, REGION
WHERE P_PARTKEY = PS_PARTKEY AND S_SUPPKEY = PS_SUPPKEY AND P_SIZE = 15 AND
P_TYPE LIKE '%%BRASS' AND S_NATIONKEY = N_NATIONKEY AND N_REGIONKEY = R_REGIONKEY AND
R_NAME = 'EUROPE' AND
PS_SUPPLYCOST = (SELECT MIN(PS_SUPPLYCOST) FROM PARTSUPP, SUPPLIER, NATION, REGION
 WHERE P_PARTKEY = PS_PARTKEY AND S_SUPPKEY = PS_SUPPKEY
 AND S_NATIONKEY = N_NATIONKEY AND N_REGIONKEY = R_REGIONKEY AND R_NAME = 'EUROPE')
ORDER BY S_ACCTBAL DESC, N_NAME, S_NAME, P_PARTKEY
OPTION (MAXDOP 4)
"@
$cmd = $con.CreateCommand()
$cmd.CommandText = $sql
$cmd.CommandTimeout = 0
Write-Host "Start Execute NonQuery."
while($true){
    $cmd.ExecuteNonQuery() | Out-Null
}
$con.Close()
$con.Dispose()

 
このスクリプトを実行している最中に「実行中のクエリの情報」を確認してみます。

並列処理の最大限度 (MAXDOP) の制限は、要求ごとではなく、タスクごとに設定されます。 つまり、並列クエリの実行の間に、1 つの要求で複数のタスクを生成し、各タスクで MAXDOP の制限まで複数のワーカーを使用できます。 MAXDOP の詳細については、「max degree of parallelism サーバー構成オプションの構成」を参照してください。

DECLARE @session_id int
select @session_id = session_id from sys.dm_exec_sessions where program_name = 'SQLTaskThread'
select
	qp.session_id,
	qp.task_address,
	qp.physical_operator_name,
	qp.node_id,
	qp.thread_id,
	ot.task_state,
	qsx.query_plan
from
	sys.dm_exec_query_profiles as qp
	left join sys.dm_os_tasks as ot
		on ot.task_address = qp.task_address
	outer apply sys.dm_exec_query_statistics_xml(qp.session_id) as qsx
where
	qp.session_id = @session_id
	-- and ot.task_state = 'RUNNING'
order by
	node_id, thread_id

 
この取得方法は、SQL Server 2016 で追加された 軽量クエリ実行統計プロファイリング インフラストラクチャ という手法を用いたものです。

SQL Server 2019 / SQL Database であれば、デフォルトで使用することができます。
 
この仕組みが実装される前は、「実行中のクエリの情報取得」を負荷を低く実行するのは難しく、キャッシュの情報から追う必要があったのですが、軽量クエリ実行統計プロファイリング インフラストラクチャ (軽量なクエリプロファイリング) は「実行中のクエリの情報」を軽量に取得することができるものとなります。
スクリプトを実行している最中にクエリを実行して情報の取得を行うと、次のような結果を取得することができます。
image
それでは、この情報を読み解いていきましょう。
SQL Server のタスクですが、基本的な考え方としては、「実行プランのアイコン」を指すものと考えられるのではないでしょうか。
こちらがスクリプトから実行しているクエリの実行プランの一部ですが、各アイコンが 1 つのタスクであると考えることができます。

(関連する処理の一連の流れで、一つのタスクとなることもありますので、厳密には一つのアイコンが一つのタスクではなく、複数のアイコンが同一のタスクとなることもありますがイメージとしては、一つの処理 (アイコン) が一つのタスクのような形で考えてみるとわかりやすいかと)
image
これがわかると「並列処理の最大限度 (MAXDOP) の制限は、要求ごとではなく、タスクごとに設定されます。」の情報の確認方法もわかってきます。
image
赤枠が node_id = 2 のソート処理における使用されているスレッドの情報を示したものです。

このソート処理では「0~4」の 5 スレッドが使用されていることが確認できます。

それでは、実行プランの情報から詳細を確認してみましょう。
image
この画像が node_id = 2 の情報を示したものとなるのですが、thread_id = 1 ~ 4 の 4 スレッドで処理が行われたことが確認できますね。
このことから MAXDOP = 4 により、「一つのタスクが最大 4 スレッドで処理が行われた」ということが確認できます。
クエリの複雑性が増し、複数のテーブルからの情報の取得が必要となると、処理によっては複数テーブルの取得を並列で実行する必要が出てきます。
この時、各テーブルの情報取得はそれぞれが個別のタスクとして認識されることになりますので、各処理で MAXDOP を上限とした処理が行われることになります。
二つの処理 (タスク) が実行されており、MAXDOP が 4 なのであれば、 8 スレッドが並列して実行されている可能性があるということですね。
MAXDOP を設定することで、タスクの並列度を調整し、特定のクエリによって、過度に CPU を使用することを抑えることができます。
この時に、「MAXDOP はタスクについての並列度を制限している」ということを意識できていると、「MAXDOP を設定していも、特定のクエリによって CPU が過度に使用されているのはなぜか?」についての調査の一助となることがありますので「タスク」という単位での並列度の制御を行っているということは覚えておくと良いのではないでしょうか。

Written by Masayuki.Ozawa

3月 2nd, 2020 at 12:24 am

Posted in SQL Server

Tagged with

Leave a Reply