SQL Server 转储的介绍

 

 

转储的英文单词是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<InstanceName>] [-S<ServiceName>][-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<Spinlock<149,1,0> >::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的格式一般如下:

<module_name>!<function call>

<module_name>!<class_name>::<method/function call>

 

四、使用windbg

 

在了解上述知识后,就可以使用windbg来进行分析了。

 

1,windbg环境的配置

 

到微软的网站下载windbg后直接安装,安装完成后,需要配置symbols的path,打开windbg,File --> 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 --> 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内部一个强大工具。要想了解的话,估计得好好研究下<windows internal>,有兴趣的可以自行深入。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值