SQL Server 轉儲的介紹

edwardking888發表於2010-12-22

轉載之:http://blog.csdn.net/obuntu/archive/2010/10/24/5962378.aspx

轉儲的英文單詞是dump,但這邊我們說的dump不是SQL Server本身的DUMP備份命令,而是指通過sqldumper.exe中的dump。那什麼是dump呢,dump指的是將某種內容轉換為另外一種更具可讀性的方式。在ORACLE中,有專門的dump命令可以dump出資料檔案等的內容,其trace也相當於另外一種dump。通過dump,我們便可以瞭解整個系統的執行原理。SQL Server這方面的資料很少,當然,這也符合了微軟不開源的策略。不過這幾年來,關於這方面的資料比較多了,通過google可以獲得相關的內容。

最早對此感興趣的是碰到了很多人經常問的.mdmp檔案,mdmp的叫mini dmp,也可以叫memory dmp,這是由於SQL Server 在執行過程中,遇到了一些bug或者錯誤而進行轉儲以便記錄出錯資訊的檔案。一般對這類檔案的處理,都是建議打包後提交給微軟分析的。在無法獲得微軟幫助的情況,就需要自己對此類檔案進行分析了,然後找出問題原因,從而進行解決。

一、SQLDumper.exe介紹

前面介紹了SQL Server 會在執行時自動產生一些dump檔案,我們也可以手工產生dump檔案,產生dump檔案的方式,就是通過Sqldumper來進行的。

自 SQL Server 2000 Service Pack 3 (SP3) 起,Microsoft SQL Server 2000 中開始附帶 Sqldumper.exe。Sqldumper.exe 可根據任一 Microsoft Windows 應用程式的需要生成轉儲檔案。Sqldumper.exe不僅可以轉儲SQL Server,還可以轉儲其他的windows application。


我使用的環境是SQL Server 2008,因此SQLDumper位於C:\Program Files\Microsoft SQL Server\100\Shared下,我們可以執行SQLDumper /? 檢視其使用方法

C:\Program Files\Microsoft SQL Server\100\Shared>SqlDumper.exe /?

Usage: SqlDumper.exe [ProcessID [ThreadId [Flags[:MiniDumpFlags] [SqlInfoPtr [DumpDir [ExceptionRecordPtr [ContextPtr [E

xtraFile]]]]]]]] [-I] [-S][-remoteservers:[print|dump|freeze|resume|remote:guid\dumporigin\si

gnature\localId\port\operationType]]

  Flags:

    dbgbreak            = 0x0001

    nominidump          = 0x0002

    validate_image      = 0x0004

    referenced_memory   = 0x0008

    all_memory          = 0x0010

    dump_all_threads    = 0x0020

    match_file_name     = 0x0040

    no_longer_used_flag = 0x0080

    verbose             = 0x0100

    wait_at_exit        = 0x0200

    send_to_watson      = 0x0400

    defaultflags        = 0x0800

    maximumdump         = 0x1000

    mini_and_maxdump    = 0x2000

    force_send_to_watson= 0x4000

    full_filtered_dump  = 0x8000

  MiniDumpFlags:

    Normal                           = 0x0000

    WithDataSegs                     = 0x0001

    WithFullMemory                   = 0x0002

    WithHandleData                   = 0x0004

    FilterMemory                     = 0x0008

    ScanMemory                       = 0x0010

    WithUnloadedModules              = 0x0020

    WithIndirectlyReferencedMemory   = 0x0040

    FilterModulePaths                = 0x0080

    WithProcessThreadData            = 0x0100

    WithPrivateReadWriteMemory       = 0x0200

    WithoutOptionalData              = 0x0400

    WithFullMemoryInfo               = 0x0800

    WithThreadInfo                   = 0x1000

從上面的命令可以看出,要想對某一application進行dump,需要先找出其pid(processes id),然後加上一些Flags的控制標識來控制dump內容。

比如,我現在想對我的SQL Server 進行dump,先找到SQL Server 的pid 為900,想dump所有的記憶體資訊,那就可以用下面的命令來進行:

C:\Program Files\Microsoft SQL Server\100\Shared>SqlDumper.exe 900 0x0010

其中,0x0010 表示all_memory,這樣,在C:\Program Files\Microsoft SQL Server\100\Shared目錄下會產生SQLDmprnnn.mdmp的檔案,這就是轉儲檔案(.mdmp)。

以下是幾個比較常見的dump 標識:

0x0120 - Minidump,這個flag只dump 一個Process的堆疊和載入模組的資訊,這是最小的轉儲方式,也是SQL Server自動產生dump檔案的方式。

0x01100 – Full Dump,這個flag會轉儲Process的整塊資訊,如果在64位的系統上,其檔案大小還是很大的。

0x8100 – Filtered Dump,這個flag用來轉儲用於於其他伺服器用途(包括過程快取)的記憶體資訊

二、SQL Server方式的DUMP

使用Sqldumper只是手工產生dump檔案的一種方式,當然,產生dmp檔案的方式還是很多的,SQL Server內部也提供了這樣的工具。主要是DBCC STACKDUMP 和dbcc dumptrigger 這兩個命令。當然還可以通過TraceFlag來控制是否產生dmp檔案或者遇到什麼錯誤時才產生檔案。比如,我們想產生一個Full Dump,必須開啟Trace Flag 2544 和 2546

dbcc traceon(2544, -1)

go

dbcc traceon(2546, -1)

go

dbcc stackdump

如果想讓SQL Server 只針對 某個錯誤而產生轉儲檔案,可以使用dbcc dumptrigger,下面是一個例子

dbcc traceon(2544, -1)

go

dbcc traceon(2546, -1)

go

--設定dump的觸發器為錯誤802

dbcc dumptrigger('set', 802)

go

-- 檢視當期的dump觸發器內容

dbcc traceon(3604, -1)

go

dbcc dumptrigger('display')

go

dbcc traceoff(3604, -1)

go

-- 關閉當期的dump觸發器。

dbcc dumptrigger('clear', 802)

go

網上有聽說可以使用2542 Trace Flag 來控制mdmp檔案的產生,至今仍沒有去試,有試過的朋友可以分享下。啟用2542 TF,在啟動引數後面新增 -T2542就是了。

三、分析mdmp檔案的一些知識說明

以上只是介紹了mdmp的產生,以及如何自己手工產生mdmp檔案,但如何對mdmp檔案進行分析才是重點。曾經對其進行了一些分析,但道行有限,能獲得的資訊不多。把如何分析mdmp檔案的過程分享出來,希望更厲害的人能從中找到一些SQL Server的執行原理。

說到分析mdmp檔案,也順便提一個人,就是The guru guide系列的作者Ken Henderson(http://www.amazon.com/Ken-Henderson/e/B001IOFJL2/ref=ntt_athr_dp_pel_pop_1),他的《The Guru's Guide to Transact-SQL》 就是中文版的《T-SQL權威指南》,其他的兩本貌似沒有找到中文版。他在Guru Guide 裡面常常會介紹一些微軟未公開的內容(undocument commands),而《The Guru's Guide to SQL Server Architecture and Internals》

就是介紹了SQL Server 2000的體系結構很是深入的一本書。可惜其在2008年已經英年早逝了(http://sqlblog.com/blogs/kalen_delaney/archive/2008/01/29/ken-henderson.aspx),不然肯定能夠提供更多關於SQL Server Interal的內容了。

1,除錯

由於SQL Server 也是在windows平臺是執行的一款程式,有問題時,把它當成一款普通的windows程式來進行除錯就行了。在windows上,有兩方面的除錯,一個是核心模式除錯,一個是使用者模式除錯。核心除錯是針對Windows作業系統進行除錯的,反應windows OS內部和硬體裝置的執行。使用者模式的除錯就是對應用程式進行除錯,因為應用程式就是執行在使用者模式上的。二者的除錯是不同的,這邊就不做過多的介紹,有疑問,就google吧。

除錯還有另外一個區別:是在程式執行時對其除錯(live-debugging),還是讀取mdmp分析除錯(post-mortem debugging)。這二者也是不一樣的。在live-debugging時會使程式掛起,然後設定bp(break point),觀察程式的執行行為。這邊主要介紹post-mortem debugging.

2,windbg

要除錯,當然得有工具,這個工具就是windbg,其主頁為:

http://www.microsoft.com/whdc/devtools/debugging/default.mspx

安裝完成後,除了有圖形介面的除錯工具(windbg),還會有命令列的除錯工具(kd,cdb,ntsd)。

在除錯過程中,我們最常見的是分析執行緒(thread)的堆疊(stack)的跟蹤資訊。因為在windows平臺上,application是以process來執行的,而一個process又包含了thread,thread才是真正在執行一些函式功能。我們可以通過select spid,kpid from sysprocesses 來看執行SQL Server的執行緒資訊,也可以通ProcessExplorer

spidd   kpid

------ ------

51     0

52     644

在ProcessExplorer裡的檢視如下,可以看到ProcessExplorer就提供了檢視堆疊的功能,但仍有一些像 10 sqlservr.exe+0x58d36b的內容無法檢視。

 

從上圖可以看到,stack的內容格式一般如下:

MyModule!Func03

MyModule!Func01

應該從底往上看,Func03是在Func01之後執行的。通過windbg,還會看到一些指標資訊(Child-sp:Stack Pointer)和地址資訊(RetAddr:Return Address)

Child-SP          RetAddr           Call Site

00000000`09cbe9e8 00000000`777b2f60 ntdll!NtSignalAndWaitForSingleObject+0xa

00000000`09cbe9f0 00000000`00bdc99e kernel32!SignalObjectAndWait+0x110

00000000`09cbeaa0 00000000`00bc4575 sqlservr+0x1c99e

00000000`09cbed40 00000000`00bc3ea8 sqlservr+0x4575

00000000`09cbed80 00000000`00bdcfad sqlservr+0x3ea8

00000000`09cbf370 00000000`01139d9c sqlservr+0x1cfad

00000000`09cbf430 00000000`032b34c7 sqlservr+0x579d9c

00000000`09cbf650 00000000`00bd2abb sqlservr!TlsGetValueForMsxmlSQL+0x4706d7

00000000`09cbf6c0 00000000`00bd0fda sqlservr+0x12abb

00000000`09cbf7e0 00000000`00bd2665 sqlservr+0x10fda

00000000`09cbf870 00000000`0117abb0 sqlservr+0x12665

00000000`09cbf8e0 00000000`0117c4b0 sqlservr+0x5babb0

00000000`09cbf9a0 00000000`0117a060 sqlservr+0x5bc4b0

00000000`09cbf9d0 00000000`0117a9ef sqlservr+0x5ba060

00000000`09cbfa60 00000000`734937d7 sqlservr+0x5ba9ef

00000000`09cbfaf0 00000000`73493894 MSVCR80!endthreadex+0x47

00000000`09cbfb20 00000000`7775f56d MSVCR80!endthreadex+0x104

00000000`09cbfb50 00000000`77893281 kernel32!BaseThreadInitThunk+0xd

00000000`09cbfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x21

要看sqlservr+0x579d9c 這樣的內容,需要使用symbol來map這些16進位制的值所表示的函式。使用symbol後,會得到下面的形式的內容。

Child-SP          RetAddr           Call Site

00000000`09cbe9e8 00000000`777b2f60 ntdll!NtSignalAndWaitForSingleObject+0xa

00000000`09cbe9f0 00000000`00bdc99e kernel32!SignalObjectAndWait+0x110

00000000`09cbeaa0 00000000`00bc4575 sqlservr!SOS_Scheduler::SwitchContext+0x84e

00000000`09cbed40 00000000`00bc3ea8 sqlservr!SOS_Scheduler::SuspendNonPreemptive+0xc5

00000000`09cbed80 00000000`00bdcfad sqlservr!EventInternal >::Wait+0x428

00000000`09cbf370 00000000`01139d9c sqlservr!ResQueueBase::Dequeue+0x19d

00000000`09cbf430 00000000`032b34c7 sqlservr!CheckpointLoop+0x1aa

00000000`09cbf650 00000000`00bd2abb sqlservr!ckptproc+0x47

00000000`09cbf6c0 00000000`00bd0fda sqlservr!SOS_Task::Param::Execute+0x11b

00000000`09cbf7e0 00000000`00bd2665 sqlservr!SOS_Scheduler::RunTask+0xca

00000000`09cbf870 00000000`0117abb0 sqlservr!SOS_Scheduler::ProcessTasks+0x95

00000000`09cbf8e0 00000000`0117c4b0 sqlservr!SchedulerManager::WorkerEntryPoint+0x110

00000000`09cbf9a0 00000000`0117a060 sqlservr!SystemThread::RunWorker+0x60

00000000`09cbf9d0 00000000`0117a9ef sqlservr!SystemThreadDispatcher::ProcessWorker+0x12c

00000000`09cbfa60 00000000`734937d7 sqlservr!SchedulerManager::ThreadEntryPoint+0x12f

00000000`09cbfaf0 00000000`73493894 MSVCR80!endthreadex+0x47

00000000`09cbfb20 00000000`7775f56d MSVCR80!endthreadex+0x104

00000000`09cbfb50 00000000`77893281 kernel32!BaseThreadInitThunk+0xd

00000000`09cbfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x21

在這個例子中,我們發現有sqlservr!CheckpointLoop+0x1aa 這個函式呼叫的資訊,這就是我們說的checkpoint的實際執行資訊了。通過select * from sys.dm_exec_requests

可以看出是哪個spid在執行checkpoint。

從上面也可以看出Call Site的格式一般如下:

!

!::

四、使用windbg

在瞭解上述知識後,就可以使用windbg來進行分析了。

1,windbg環境的配置

到微軟的網站下載windbg後直接安裝,安裝完成後,需要配置symbols的path,開啟windbg,File --&gt Symbols File Path 在彈出的對話方塊輸入

srv*c:\symbols*http://msdl.microsoft.com/download/symbols

其中c:\symbols是本地硬碟的資料夾,在使用時,windbg會到http://msdl.microsoft.com/download/symbols下載相關的symbols,也可以自己手工下載相關作業系統的symbols。

配置好,可以使用.reload命令來強制下載某個symbols,如:

.reload /f sqlservr.exe

具體的命令可以參考windbg的幫助文件。

2,開啟mdmp檔案

開啟windbg,File --&gt Open Crash Dump,選擇mdump檔案,在彈出的對話方塊裡點選yes

3,分析mdmp

在下面的對話方塊輸入 ~ 會出現執行緒的資訊

0:000> ~

.  0  Id: 384.608 Suspend: 1 Teb: 7ffdd000 Unfrozen

   1  Id: 384.698 Suspend: 1 Teb: 7ffda000 Unfrozen

   2  Id: 384.6a8 Suspend: 1 Teb: 7ffd9000 Unfrozen

   3  Id: 384.6a4 Suspend: 1 Teb: 7ffd8000 Unfrozen

   4  Id: 384.6b0 Suspend: 1 Teb: 7ffd7000 Unfrozen

   5  Id: 384.6ac Suspend: 1 Teb: 7ffd6000 Unfrozen

   6  Id: 384.6c8 Suspend: 1 Teb: 7ffd5000 Unfrozen

   7  Id: 384.6dc Suspend: 1 Teb: 7ffd4000 Unfrozen

   8  Id: 384.6e0 Suspend: 1 Teb: 7ffd3000 Unfrozen

   9  Id: 384.108 Suspend: 1 Teb: 7ff9f000 Unfrozen

  10  Id: 384.6e8 Suspend: 1 Teb: 7ff9e000 Unfrozen

  11  Id: 384.6e4 Suspend: 1 Teb: 7ff9d000 Unfrozen

  12  Id: 384.604 Suspend: 1 Teb: 7ff9c000 Unfrozen

  13  Id: 384.714 Suspend: 1 Teb: 7ff9b000 Unfrozen

  14  Id: 384.718 Suspend: 1 Teb: 7ff9a000 Unfrozen

  15  Id: 384.71c Suspend: 1 Teb: 7ff99000 Unfrozen

  16  Id: 384.720 Suspend: 1 Teb: 7ff98000 Unfrozen

  17  Id: 384.728 Suspend: 1 Teb: 7ffdc000 Unfrozen

  18  Id: 384.730 Suspend: 1 Teb: 7ff97000 Unfrozen

  19  Id: 384.74c Suspend: 1 Teb: 7ff96000 Unfrozen

  20  Id: 384.784 Suspend: 1 Teb: 7ff95000 Unfrozen

  21  Id: 384.788 Suspend: 1 Teb: 7ff94000 Unfrozen

  22  Id: 384.1e0 Suspend: 1 Teb: 7ff93000 Unfrozen

  23  Id: 384.284 Suspend: 1 Teb: 7ff92000 Unfrozen

  24  Id: 384.280 Suspend: 1 Teb: 7ff91000 Unfrozen

  25  Id: 384.23c Suspend: 1 Teb: 7ff8f000 Unfrozen

  26  Id: 384.3d0 Suspend: 1 Teb: 7ff8e000 Unfrozen

  27  Id: 384.3d4 Suspend: 1 Teb: 7ff8d000 Unfrozen

  28  Id: 384.3d8 Suspend: 1 Teb: 7ff8c000 Unfrozen

  29  Id: 384.204 Suspend: 1 Teb: 7ff8b000 Unfrozen

  30  Id: 384.43c Suspend: 1 Teb: 7ff8a000 Unfrozen

  31  Id: 384.450 Suspend: 1 Teb: 7ff89000 Unfrozen

  32  Id: 384.454 Suspend: 1 Teb: 7ff88000 Unfrozen

  33  Id: 384.458 Suspend: 1 Teb: 7ff87000 Unfrozen

  34  Id: 384.45c Suspend: 1 Teb: 7ff86000 Unfrozen

  35  Id: 384.464 Suspend: 1 Teb: 7ff84000 Unfrozen

  36  Id: 384.44c Suspend: 1 Teb: 7ff83000 Unfrozen

  37  Id: 384.1e8 Suspend: 1 Teb: 7ffdb000 Unfrozen

  38  Id: 384.1cc Suspend: 1 Teb: 7ff82000 Unfrozen

  39  Id: 384.1684 Suspend: 1 Teb: 7ff80000 Unfrozen

  40  Id: 384.c38 Suspend: 1 Teb: 7ff90000 Unfrozen

  41  Id: 384.1048 Suspend: 1 Teb: 7ff85000 Unfrozen

  42  Id: 384.140c Suspend: 1 Teb: 7ff7f000 Unfrozen

  43  Id: 384.a18 Suspend: 1 Teb: 7ff81000 Unfrozen

在我的這個例子中,我的spid在迴圈執行一個select命令,從sysprocesses中,可以看到spid對應的kpid是488

spid kpid

51 0

52 488

488轉化為16進位制剛好為1e8 ,對應的序號是37。

那我們如果想看執行緒37的內容,可以先使用 ~37s命令切換到執行緒37的上下文中

0:000> ~37s

eax=00000000 ebx=3f20f344 ecx=1f8dcf08 edx=00000001 esi=000009b5 edi=00000000

eip=7c92e514 esp=3f20f238 ebp=3f20f29c iopl=0         nv up ei ng nz ac pe cy

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000297

ntdll!KiFastSystemCallRet:

7c92e514 c3              ret

看起來是CPU的暫存器資訊,可惜這些還看不太懂,不然可以更深入了。

接著用k命令,檢視具體的函式呼叫資訊

0:037> k

ChildEBP RetAddr 

3f20f234 7c92df5a ntdll!KiFastSystemCallRet

3f20f238 7c8025db ntdll!ZwWaitForSingleObject+0xc

3f20f29c 7c802542 kernel32!WaitForSingleObjectEx+0xa8

3f20f2b0 011e7ced kernel32!WaitForSingleObject+0x12

3f20f324 011e7ddb sqlservr!Np::StatusWriteNoComplPort+0x9f

3f20f354 011e7ea2 sqlservr!SNIStatusWriteNoComplPort+0x82

3f20f374 012a8ae0 sqlservr!TDSSNIClient::WriteStatus+0x6a

3f20f4a0 0153d30c sqlservr!write_data+0x1a6

3f20f4d0 0117492e sqlservr!flush_buffer+0xdf

3f20f6a0 015490b6 sqlservr!CKatmaiTds::SendRowImpl+0x2faf

3f20f6ac 01532f0d sqlservr!CValOdsRow::SetDataX+0x29

3f20f6bc 01532d8b sqlservr!SetMultData+0x1e

3f20f734 0154962f sqlservr!CEs::GeneralEval4+0xd0

3f20f740 01547825 sqlservr!CEs::Eval+0x13

3f20f7f8 015499af sqlservr!CXStmtQuery::ErsqExecuteQuery+0x409

3f20f85c 015401c3 sqlservr!CXStmtSelect::XretExecute+0x268

3f20f8f8 01540cc0 sqlservr!CMsqlExecContext::ExecuteStmts<1,1>+0x28d

3f20f9e0 01540686 sqlservr!CMsqlExecContext::FExecute+0x70e

3f20fa84 0153cf8c sqlservr!CSQLSource::Execute+0x598

3f20fc08 01539f79 sqlservr!process_request+0x2f0

從下面的內容,可以看出幾點(個人觀點:) )

3f20f354 011e7ea2 sqlservr!SNIStatusWriteNoComplPort+0x82

3f20f374 012a8ae0 sqlservr!TDSSNIClient::WriteStatus+0x6a

3f20f4a0 0153d30c sqlservr!write_data+0x1a6

3f20f4d0 0117492e sqlservr!flush_buffer+0xdf

3f20f7f8 015499af sqlservr!CXStmtQuery::ErsqExecuteQuery+0x409

3f20f85c 015401c3 sqlservr!CXStmtSelect::XretExecute+0x268

3f20f8f8 01540cc0 sqlservr!CMsqlExecContext::ExecuteStmts<1,1>+0x28d

3f20f9e0 01540686 sqlservr!CMsqlExecContext::FExecute+0x70e

從底往上看,可以看到這是一個select動作,進行select時,先對記憶體的一些快取進行清除(flush_buffer),接著便是寫入資料(write_data),然後再傳送寫狀態(TDSSNIClient::WriteStatus),由於一直迴圈所以會有寫未完成的提示(SNIStatusWriteNoComplPort)。這也基本符合一個select的動作。

如果遇到錯誤時,在函式呼叫中一般會丟擲raiseerror等內容,類似如下:

00000000`220ce2d0 00000000`013a3d41 sqlservr!ex_raise2+0xcdd8bf

00000000`220ce630 00000000`02deb8ce sqlservr!ex_raise+0x51

這時,基本可以判斷出現問題的原因了。

3,其他

windbg的功能是很強大的,是通往sql server內部一個強大工具。要想了解的話,估計得好好研究下,有興趣的可以自行深入。

 

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8183550/viewspace-682357/,如需轉載,請註明出處,否則將追究法律責任。

相關文章