使用WinDbg調試SQL Server查詢

jopen 9年前發布 | 9K 次閱讀 SQL Server 數據庫服務器

上一篇文章我給你介紹了WinDbg的入門,還有你如何能附加到SQL Server。今天的文章,我們繼續往前一步,我會向你展示使用WinDbg調試SQL Server查詢需要的步驟。聽起來很有意思?我們開始吧!

假設在你面前有個簡單的查詢,你想在WinDbg里調試那個特定的查詢。聽起來很簡單,但一旦你開始考慮這個問題,就會碰到很多問題:

  • 在我特定執行的查詢上,我如何標識出正確的工作者線程?
  • 在sqlservr.exe里,我應該在哪里設置斷點?
  • </ul>

    我們來具體講解下這2個問題。

    標識出正確的工作者線程

    當你在SQL Server里執行一個查詢,默認情況下你是不知道查詢是在哪個線程上運行的。幸運的是SQL Server在DMV sys.dm_os_threads里提供os_thread_id列來告訴我們。OS線程ID就是用來執行指定查詢的。不幸的是你需要從sys.dm_exec_requests直到sys.dm_os_threads連接多個表才可以得到需要的信息。我們來看下面的查詢:

    SELECT R.Session_Id, Th.os_thread_id FROM sys.dm_exec_requests R 
    JOIN sys.dm_exec_sessions S ON R.session_id = S.session_id 
    JOIN sys.dm_os_tasks T ON R.task_address = T.task_address 
    JOIN sys.dm_os_workers W ON T.worker_address = W.worker_address 
    JOIN sys.dm_os_threads Th ON W.thread_address = Th.thread_address 
    WHERE S.is_user_process = 1
    GO

    在WinDbg里用CTRL+BREAK中斷sqlservr.exe。為了切換到sys.dm_os_thread提供的系統線程ID,你可以用下列WinDbg命令:

    ~~[tid]s

    </blockquote>

    占位符tid的值就是實際的系統線程ID——16進制值。因此你需要來自sys.dm_os_thread的os_thread_id列值轉為16進制值,用剛才提到的命令。當你的系統線程ID是4910時,你應該用下列WinDbg命令切換到正確的線程:

    ~~[132E]s

    </blockquote>

    當你的查詢運行時,對于你的產尋,sys.dm_os_thread只顯示系統線程ID。因此就有下一個問題:對于一個執行的查詢,我如何獲得“正確的”系統線程ID。我這里用一個小技巧:首先我運行一個簡單的WAITFOR DELAY命令(例如1分鐘),然后再運行實際的查詢。如果你用這個方法,你需要保證在1個批處理里提交2個T-SQL查詢。不然的話,SQL OS調度會放置WAITFOR語句和實際的查詢在2個不同的線程!我們來看實際的代碼:

    WAITFOR DELAY '00:01:00'

    SELECT soh., d. FROM Sales.SalesOrderHeader soh INNER JOIN Sales.SalesOrderDetail d ON soh.SalesOrderID = d.SalesOrderID WHERE soh.SalesOrderID = 71832 AND d.SalesOrderDetailID = 111793 GO</pre>

     在等待期間,你需要進行下列操作:

    1. sys.dm_os_thread為你等待的查詢獲得在不同會話里系統線程ID
    2. 轉化系統線程ID為16進制值
    3. 用CTRL+BREAK中斷sqlservr.exe
    4. ~~[tid]命令切換到正確的系統線程ID
    5. 在指定線程上設置斷點
    6. 繼續sqlservr.exe的運行
    7. 等待直到觸發斷點
    8. </ol>

      你要在用WAITFOR DELAY語句引起的延遲時間內完成所有這些操作。如果超過這個時間,這個方法就不可靠了。因此在剛開始的時候,你可以用WAITFOR DELAY設置長一點的延遲時間,直到用這個方法你已經有經驗了。

      在sqlservr.exe里設置“好的”斷點

      現在你已經從sys.dm_os_thread獲 得了系統線程ID,而且你用WinDbg掛起了sqlservr.exe的執行。下一步你要在sqlservr.exe里設置斷點,這樣的話你可以在你的 查詢里調試并單步執行通過。但什么是好的斷點呢?這個看情況:)執行計劃里的每個運算符都是用獨立的C++類實現的,它里面包含不同的函數。其中一個熟知 的函數是GetRow,它返回一行到執行里上迭代器。我的方法如下:在執行計劃里,嘗試在最左的一個迭代器里設置斷點。從我的經驗里發現,每個 SELECT查詢開始于sqlmin!CQueryScan::GetRow的函數調用。

      剛開始在指定類和函數上設置斷點應該非常有用。當然你需要花很長時間(當單步執行通過代碼時),指導你碰到SQL Server有意思的部分,像B樹管理器,或者閂鎖/旋轉鎖的實現。但初次試驗時,建議你在特定函數設置斷點就可以了。你要確保斷點設置在正確的線程上,因為你只想調試你特定查詢,沒別的!用bm命令在指定線程和符號名上設置斷點:

      ~tid bm sqlmin!CQueryScan::GetRow

      </blockquote>

      但你還要意識到你不必提供系統線程ID。bm命令期望一個從零開始數字線程號。當你用~~[132E]s切換到正確的系統線程時,你會在WinDbg左下角看到線程號:

      使用WinDbg調試SQL Server查詢 

      當WinDbg提示像47的線程號,你可以用下列命令在正確的線程上,在sqlmin!CQueryScan::GetRow函數設置斷點:

      ~47 bm sqlmin!CQueryScan::GetRow

      </blockquote>

      設置斷點后,你可以用F5繼續sqlservr.exe的運行。幾秒后(取決于在WAITFOR語句上設置的延遲)WinDbg應該會在特定的斷點中斷:

      使用WinDbg調試SQL Server查詢 

      現在好戲才開始:你可以用k命令探索當前的調用堆棧,你可以對匯編代碼單步執行通過,看看其他函數是如何調用的。夢想有多遠,你的選擇就有多遠(Your choices are endless, and only limited by your imagination.)。

      小結

      希望這篇文章已經給你以下內容深入的介紹:

      sqlservr.exe里對于指定的查詢進行調試時,如何成功的設置斷點。

      來自:http://www.cnblogs.com/woodytu/p/4665427.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!