来週必要になりそうなので操作メモ。 Profilerで実行プランを採取する設定、トレースファイル出力する形で採取開始。 所定のSQLを実行 トレースを停止する。 トレースファイルをダブルクリックしてSQL Profilerを起動し編集する。 トレースファイル設定時に5MByteでロールオーパする設定をしていたので、以下ダイアログがロールオーバー分表示される。全部[はい]を選択。 プロパティで表示する行、列を絞り込む このダイアログで、タブ切り替えをして この画面で、必要なイベント(行)、項目(列)を絞り込む 次に列フィルタで実行プランに特定のキーワードが入っているもののみに絞り込む。今回は「7067」のキーワードにしてみる。 またロールオーパファイルの指定があるので、すべて「はい」を選択。 絞り込まれたデータが意図通りか1つ内容確認をしてみる。 よければ、全行を選択しコピー(Ctrl+C)する。 excelに貼り付ける。 絞り込みの結果、ShowPlanイベントの場合、B列がtextDataとしていたので、内容はShowplanXMLとなっている。 このExcelを渡して解析してもらえばいいかな。xlsxなら、zip圧縮ファイルなのでテキストデータは効率よく圧縮してくれるはず。 この段階で、実行プランのビジュアル化は不要だと思うけど、必要な場合は、B列の1セルの内容をテキストエディタに貼り付けて、 拡張子を.sqlplan、文字コードをBOMつきUTF-8で保存すると、 その.sqlplanファイルをダブルクリックすれば、SSMSが立ち上がり実行プランをグラフィックで表示できる。 追記:上でProfilerの結果をExcelに貼り付けたが、実行プランはXMLなので実行プランでは文字数が33000文字を超えることはざらにある。 Excelの1つセルに入れられる文字の最大数は32,767文字なので、はみ出した文字は次の行の先頭セルに入れられてしまう。 これでは、1列目はイベント種類ということが言えなくなり、実行プランファイルを作るには、2つのセルの内容を連結して作らなければならない。 うまくいかないものですね。 Profilerで実行プランのイベントを抽出する設定をした場合に追加される以下の[イベント抽出の設定]タブでXMLプランを個別にファイルに出力した方がよさそう。 ただ、何十個の実行プランを1ファイルに保存すると、この.sqlplanファイルを開いたときにSSMSが固まるので、実行プランごとにファイルに分ける設定ができる。 この設定をすると(トレースファイルのロールオーバーのように)ファイル名の拡張子の前に_連番が付加される。 (下のダイアログでは、aa.sqlplan,aa_1.SQLPlan,aa_2.SQLPlan,...,aa_10.SQLPlan,...となる)
PC備忘録
コンピュータを中心ですが、日常のこともいっぱい取り混ぜていきます。 なお自分のメモみたいなものなので、文句は受け付けないですよ~。自己責任でお願いします。
2025年12月6日土曜日
Profilerから特定のSQL実行プランの調査方法
2025年11月29日土曜日
同じSELECT文の構造同士でデッドロックエラーが出る現象
前回の記事で、SELECTでインデックスの絞り込みが甘いせいでデッドロックが発生した記事を書いたけど、 その現象の一例について記事化しておく。 pubsデータベースのsalesテーブルを使って解説。 pubsデータベースのトランザクション分離レベルはREAD_COMMITTED。 salesテーブルは、主キーが、stor_id,ord_num,title_idとなっているクラスター化インデックステーブル。 salesテーブルにstor_id='7067'のデータは4件あり、このうち2件に対して、rowlockとupdlockのヒントをつけてselectをする。 (rowlockはロックエスカレーションを起こさないため、updlockは、この後の更新で変換デッドロックを起こさないためにつけている。) 2つのトランザクションから同じ構造のselect文を2回発行する。(トランザクションをTrAとTrBと書く。) TrAとTrBで発行するのはそれぞれ以下のSQL。 select * from sales with(rowlock,updlock) where stor_id='7067' and title_id='TC4203' と、 select * from sales with(rowlock,updlock) where stor_id = '7067' and title_id='TC3218' まず、TrAでselectを実行する。(下図の左の選択部分を実行した結果) 実行できて、1行結果が返ってくる。 次に、TrBでselectを実行する。(下図の右の選択部分を実行した結果) 検索実行待ちとなる。 次にTrAで、2回目のselectを実行する。(下図の左の選択部分を実行した結果) TrAは結果が返ってきて、TrBは待ちとなっていたselectがデッドロックエラーとなる。 では解説。 まず、TrAで1つ目のSELECTをした結果1行返ってきた時点で、このトランザクションのこの行に更新ロックがかかる。 トランザクション分離レベルが何であっても更新ロックの生存期間はトランザクション終了までである。 次に、TrBでSELECTをした結果、(店番に対してのみの)クラスタ化インデックスキーシークを行う。 このときに、店番で見つけたレコードに対して、更新ロックをかけ中身を見ようとする。 (ロックの役割上ロックをかけられなればそのレコードの中身を見れない。) TrAで取得していない行をロックしつつ、 TrAで取得した行をロックをしようとしたときに、更新ロック同士は互換性がないため、 TrAのロックの開放待ちとなり、中身が見れない状態となる。 この行の内容が検索条件にヒットするのかヒットしないのかわからない状態のため、SELCT自体のTrBも実行中(ロック解除待ち)となる。 次に、TrAの2つ目のSELECTをした結果、TrBでロックされている行をロックすることができず、その行の中身が見られない。(ロック解除まち)となる。 結果、TrAとTrBが互いのロック解除待ちとなりデッドロックが成立する。ここで、TrBが検索結果をまだ返していないのにもかかわらずTrAとのデッドロックの原因となることから、 たとえupdatelockつきselectが1回のみのトランザクション同士でも、そのトランザクション同士が同時に起動した場合にデッドロックが起こりうることになる。どんだけやっても1つのselect文だけでは2つのトランザクション間でデッドロックが発生しなかった。 この辺のロックの詳細仕様はSQL Server内部の仕様となり不明。 (例えば、ロックする行の順番など仕様として決めてしまうとマルチプロセスで処理をする場合の足かせになってしまうので、仕様を定めていないと思います。) 今回の実験ではデッドロックとなりましたが、実行順序を変えた場合以下のようになり、ロックは発生するがデッドロックは起きませんでした。 TrBで1つ目のSELECT→1行返ってくる。 TrAで1つ目のSELECT→実行中となる。 TrBで2つ目のSELECT→1行返ってくる。 TrBでCOMMIT→TrAで1行返ってくる。(TrBのロック待ちが解除された) TrAで2つ目のSELECT→1行返ってくる。 TrAでCOMMIT→正常完了。 で、インデックスで対象の行が絞り込まれ、絞り込まれた先には他のトランザクションのロックがかかっていない場合は、このデッドロックの現象を回避できる。 というわけで、、前回の記事のように、 salesテーブルにstor_id,title_idを持つインデックスixを作り、 TrAとTrBから同じ構造のselect文を2回発行する。 TrAとTrBで発行するのはそれぞれ以下のSQL。 select * from sales with(rowlock,updlock,index(ix)) where stor_id='7067' and title_id='TC4203' と、 select * from sales with(rowlock,updlock,index(ix)) where stor_id = '7067' and title_id='TC3218' まず、TrAでselectを実行する。(下図の左の選択部分を実行した結果) 実行できて、1行結果が返ってくる。 次に、TrBでselectを実行する。(下図の左の選択部分を実行した結果) 実行できて、1行結果が返ってくる。 次にTrAで、2回目のselectを実行する。(下図の左の選択部分を実行した結果) 実行できて、1行結果が返ってくる。 次にTrBで、2回目のselectを実行する。(下図の左の選択部分を実行した結果) 実行できて、1行結果が返ってくる。
2025年11月24日月曜日
SQL Server Profilerの使用は非推奨?
SQL Serverでupdlock,rowlockヒント付きの同じselect文同士で、デッドロックが発生した。 デッドロックエラー時の実行プランをみたところ、4列からなる主キーのうち、シークキーとしているのが1列のみ。 理由は、select文のwhere句に指定があるのは、主キーの第1,3,4番目の列で、主キーの第2番目の列を指定していないため。 別に用意されていた(where句の検索条件で十分絞り込まれる)2次インデックスは使われていなかった。 対応として、(たとえSQL Serverが計算したコストが高くなっても)2次インデックス使うようにするヒントを追加する対応をする。 で、効果確認で、実行後にプランハンドルを検索し実行プランを抽出しようとしたが、 検索に手間取りプランハンドル特定時には実行プランが流れて(消えて)しまっていた。 なので、SQL Profilerを仕込んで、SQLを実行することにしようとしたが、netを見ると、profilerの使用は非推奨となっており、 拡張イベントを使う方法が推奨とのこと。 というわけで、SQL Profilerと拡張イベントでの実行プランの採取方法の備忘録。 まずは、profilerの方法 Microsoft SQL Server Tools→SQL Server Profiler を起動し、新しいトレースを押し、 サーバへの接続ダイアログで、該当サーバに接続し、 トレースのプロパティダイアログで、[全般]タブでは、使用するテンプレートでStandard(default)のまま、 [イベントの選択]タブで、すべてのイベントを表示するにチェックボックスをONにした後、 Performance→Showplan XML Statistics Profileの行にチェックを入れて実行ボタンを押下。 調査するSQL文の実行が終わった後に、赤い四角アイコン(選択したトレースの停止)を押して、トレース採取完了。 EventClassがShowplan XML Statistics Profileの行を選択すると、SQLPlanが図示される。 次に拡張イベントの方法 SQL Server Management Studioでサーバへの接続ダイアログで、該当サーバに接続し、 オブジェクトエクスプローラーウィンドウでSQL Server→管理→拡張イベント→セッションを右クリックし新しいセッションウィザードを選択。 ウィザード(ステップ式ダイアログ入力)で [セッションのプロパティの設定]ステップで、セッション名を指定し、 [テンプレートの選択]ステップで、テンプレートにStandardを選択し、 [キャプチャするイベントの選択]ステップで、query_post_execution_showplanイベントを追加し、 [グローバルフィールドのキャプチャ]ステップと、 [セッションイベントフィルターの設定]ステップはデフォルトのまま [セッションデータストレージの指定]ステップで、SQLサーバー上のファイル名を指定して(SQL Serverへのログインユーザの次第では書き込み権限がないかも。)完了ボタンを押下。 成功のダイアログを閉じる。 オブジェクトエクスプローラーウィンドウでSQL Server→管理→拡張イベント→セッションを展開すると、ウィザードステップ1でしていしたセッション名が赤四角(停止中)で表示される。 右クリック→[セッションの開始]すると、赤い四角から、緑の右向き三角となり採取中となる。 最後に、今回の対応での実行プランの見方をpubsデータベースsalesテーブルを使って解説。 salesテーブルは、主キーが、stor_id,ord_num,title_idとなっているクラスター化インデックスキーとなっている。 salesテーブルにstor_id,title_idを持つインデックスixを作ってみる。 SQL Server Management Studioで[実際の実行プランを含める(Ctrl + M)]をした後、 select * from sales where stor_id='6380' and title_id='BU1032' を実行し、実行プランを見ると、SELECTはクラスター化インデックスシークを行っていることがわかる。 クラスター化インデックスシークをポイントすると、シークキーにstor_idのみ使われていることがわかり、 SELECTをポイントすると、サブツリーの推定コストが、0.0032842とわかる。 次に、select * from sales with(ixndex(ix)) where stor_id='6380' and title_id='BU1032' を実行し、実行プランを見ると、SELECTは[sales].[ix]のインデックスシークと、[sales].UPKCL_salesのキー参照をNested Loops(入れ子ループ)でインナージョインしていることがわかる。 インデックスシークでは、シークキーに、stor_idと、title_idが使われており、 SELECTでは、サブツリーの推定コストが、0.0065704とわかる。 つまり、with(index(ix))のヒントにより、SQL Serverは、推定コストが0.0032842ではなく、それより大きい0.0065704の実行プランを採用していることがわかる。 これで、デッドロックの解消ができればいいんだけれど。
2025年10月11日土曜日
ローマ字入力からカナ入力への転向
これから生成AIの時代、なんとしても入力速度を上げたくて30年つづけていたローマ字入力からかな入力に転向中。 転向から1年半が経過したが、まだローマ字入力だった時よりも入力が遅い。 ただ、もう無意識の運指がかな入力になっているので、もうローマ字入力には戻れない状態となっている。 転向してみてわかった以下のデメリットがある。もしローマ字入力からカナ入力にしたいと思ったら以下のことを受け入れる覚悟がいることを知ってほしい。 (こんな苦労があるとわかっていたら私は転向はしなかったと思う。) ・過渡期中は、入力スピードが極端に遅くなり、ローマ字入力に戻れないため、ストレスががかる。 ・人のPCやリモートデスクトップしたPCでの日本語入力は壊滅的。(他人のPCをカナ入力にするわけにもいかないし。) ・キー入力に迷いやミスが起こる。(ローマ字入力のキー入力の種類は25個なのに対して、カナ入力の1文字はシフトキーインを別々に考えると62個ある) ・全角の記号や数字を嫌うようになる(数字や記号入力のために入力をカナからローマ字の切り替えと戻しをする必要がある) という愚痴は置いておいて、Windowsのカナ入力設定 1.カナ入力設定 2.キーによるカナ入力とローマ字入力の切り替え ローマ字入力がしたいわけではなく、全角数字(1など)や全角記号(?や①や@や:など)を入力するため 3.以前のバージョンのMicrosoft IMEを使う Excelマクロする場合に設定。ExcelVBA開発環境で標準のIMEを使うと日本語入力中にShiftを押すと無変換で確定してしまうため 「ぁぃぅぇぉっゃゅょを、。・」を入力するときに支障があるため 1.2.3.ともに同じ設定画面で以下を設定。 記号の入力が半角なら[半角/全角]キーか[capslock]でIMEのoffを切り替えてから入力する。 記号の入力が全角なら[alt]+[カタカナひらがなローマ字]で入力モードをローマ字に切り替えてから入力する。 その他の方法では、いったんカナ入力で入力した後、変換候補をF9(全角英数)もしくはF10(半角英数)で切り替える方法もある。 ファンクションキー入力では入力モードの切替戻しが不要なので速いはずなんだけど、キーボードを見ない入力だと押し間違いが酷い。
登録:
コメント (Atom)


















































