最近一段时间服务器老是出现500、502或者503错误,由于数据量大了,访问用户增多,这类错误越来越影响正常业务。花了无数的时间排查问题,现做简单总结如下。如果你有新的建议,我会后续补上。 首先需要明确的是出现这类问题肯定是服务器错误,有可能是程序问题,也有可能是系统问题,要根据实际情况进行排查。在我遇到的情形中,我是按下列步骤进行排查的:

1. 首先打开任务管理器,查看内存占用

如果内存占用始终在95%上下,有可能是服务器上某个程序太耗内存导致内存不足。内存问题的排查情况比较多,可能是网站程序有内存泄露,也有可能是某个操作非常吃内存。
在我的服务器上,出现的问题是任务管理器里所有进程占用内存都不多,但是显示内存占用100%,在SQL SERVER中执行下面的SQL,确定是数据库缓存的问题。重启数据库,并为数据库设置最大占用内存可以解决这个问题。

select counter_name, ltrim(cntr_value*1.0/1024/1024)+'G' as memoryGB
from master.sys.dm_os_performance_counters
where counter_name like '%target%server%memory%'
    or counter_name like '%total%memory%'

-- Target Server Memory (KB) 服务器能够使用的动态内存总量。
-- Total Server Memory (KB) 从缓冲池提交的内存 (KB)。注意:这不是 SQL Server 使用的总内存。

2. 查看CPU占用

CPU占用100%同样可能会导致服务器出现50x错误,CPU问题的排查和内存问题一样,需要根据具体进程进行排查。在我的一次排查中,发现sqlservr.exe进程一直占用95%以上的CPU。进入SQL SERVER执行sp_who可以看到连接数很多,而且大多数状态都是suspended。使用下面的SQL语句可以查看当前正在执行的具体SQL。

SELECT
    [Spid] = session_Id,
    ecid,
    [Database] = DB_NAME(sp.dbid),
    [User] = nt_username,
    [Status] = er.status,
    [Wait] = wait_type,
    [Individual Query] = SUBSTRING(qt.text, er.statement_start_offset / 2,
        (CASE WHEN er.statement_end_offset = - 1
              THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
              ELSE er.statement_end_offset
         END - er.statement_start_offset) / 2),
    [Parent Query] = qt.text,
    Program = program_name,
    Hostname,
    nt_domain,
    start_time
FROM sys.dm_exec_requests er
INNER JOIN  sys.sysprocesses sp ON er.session_id = sp.spid
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS qt
WHERE session_Id > 50 /* Ignore system spids.*/ AND session_Id NOT IN (@@SPID)

结果显示正在执行很多相同的SQL,而且都是suspended状态。定位到具体的SQL语句,然后就是优化工作了,为该SQL添加索引后问题解决。

3. 检查IIS应用程序池是否正常运行

有时候IIS的应用程序池会异常退出,也会导致50x错误。应用程序池的退出很大一部分原因都是由于上面两个原因导致,要么是内存爆掉了,要么是CPU爆掉了,从而导致应用程序池崩溃这样的连锁反应。进入IIS->应用程序池,启动相应的程序池问题解决。

4. 检查数据库死锁

通过下面的SQL可以查看设置的死锁超时时间,如果LOCK_TIMEOUT值是-1,意味着如果遇到死锁则进程一直等待资源释放,这样就可能导致50x问题。可以将死锁的超时时间设置为3秒,则可以解决这类问题。具体的死锁原因还需要进一步分析。

SELECT @@LOCK_TIMEOUT
SET LOCK_TIMEOUT 3000

注意:LOCK_TIMEOUT值是程序级的设置(application-level setting),只对当前连接有效。在连接打开后,可以使用SET LOCK_TIMEOUT设置死锁超时时间。不能设置成全局的,如果想设置全局,可以使用CommandTimeout值代替。

参考链接:

1. 查询sqlserver 正在执行的sql语句的详细信息
2.SQL Server 性能调优(cpu)
3. 快速搞懂 SQL Server 的锁定和阻塞
4. SQL Server性能调优:资源管理之内存管理篇(上)

扫描二维码,在手机上阅读!