SQL Server数据库巡检命令清单:会话、索引碎片与死锁排查实战

前阵子我集中对一套业务系统做了几轮数据库巡检,翻笔记本时发现当天用到的SQL Server命令其实可以组成一份相当完整的排查清单。从会话状态到索引碎片,从等待统计到死锁现场,基本覆盖了日常运维里最常遇到的那几类问题。这篇就把当天真正动手敲过、并且验证有效的命令逐条写出来,每条附带我当时的判断逻辑和踩过的坑,想直接拿去用的朋友照着复制即可。

1. 这批命令的整理背景:慢查询和阻塞告警逼出来的清单

那段时间生产环境频繁报出两类问题:一是某些页面接口响应突然变慢,二是早上九点半左右偶发超时告警。我最初也是靠经验逐个排查,执行计划、索引、锁挨个看,效率不高。后面实在觉得不行,干脆把高频用到的排查命令统一整理成一份脚本文件,命名成“巡检脚本.sql”,塞进常用工具目录里。之后只要系统再报警,我第一件事就是跑一遍这套命令,从全局指标到细节语句一条龙过完,基本十分钟内能定位到方向。

这套命令挑选的标准很简单:一条命令对应一类常见问题。比如连接数异常对应会话视图,慢查询对应语句统计,锁等待对应阻塞查询,空间紧张对应数据库文件大小。每一条都要能直接给出可判断的指标,而不是给一堆原始数据让读者自己猜。适用版本方面,我本地的环境是SQL Server 2019和2022,下面写的系统视图和命令在2016及以上版本都可以跑,老版本2008 R2部分视图字段略有差异,使用时要留意。

另外提一句,我整理的这些命令多数是只读查询,不会修改数据,跑起来相对安全。但涉及DBCC、重建索引和备份那几条,必须在维护窗口或低峰期操作,千万别在业务高峰期直接对生产库执行重建索引,这个后面细说。

2. 数据库健康巡检命令:先看整体状态再追细节

很多朋友排查问题时习惯直接揪着某条慢SQL去优化,我的习惯是反过来——先看一眼服务器和数据库整体状态。因为很多时候单个语句慢只是表象,背后是连接数堆积或日志文件膨胀在拖累全局。开场先跑这几条,相当于给数据库做个体检。

2.1 会话与连接状态:快速判断当前有没有“堵车”

判断数据库“堵不堵”,第一条命令是查当前会话和连接数:

SELECT session_id, status, login_name, host_name, program_name, login_time, last_request_start_time, last_request_end_time FROM sys.dm_exec_sessions WHERE is_user_process = 1 ORDER BY last_request_start_time;

这条命令会把所有用户会话列出来,我主要看两件事:一是有没有大量会话长时间处于running或sleeping状态,二是last_request_start_time和last_request_end_time间隔是不是异常大。如果某个会话的开始时间和结束时间差了好几秒甚至几十秒,大概率这个会话对应的请求出现了阻塞或者执行计划有问题。

再配合查询具体请求状态:

SELECT session_id, start_time, status, command, wait_type, wait_time, blocking_session_id FROM sys.dm_exec_requests WHERE session_id > 50;

wait_type是重点,常见的有LCK_M_S(共享锁等待)、LCK_M_X(排他锁等待)、PAGEIOLATCH_SH(磁盘IO等待)、CXPACKET(并行查询等待)。看到wait_type是锁等待时,就得继续查阻塞链,这个我放到后面专门讲。看到PAGEIOLATCH_SH这类IO等待占大头,就要考虑是不是内存不足或磁盘性能瓶颈,而不是盲目加索引。

2.2 数据库文件和日志状态:预防磁盘被写满

巡检时必须看的还有数据库文件大小和剩余空间。生产环境最常见的事故之一就是日志文件疯狂增长把磁盘塞满,导致数据库进入只读状态。我用的语句是:

SELECT db.name AS database_name, mf.type_desc AS file_type, mf.name AS logical_name, mf.physical_name, mf.size * 8 / 1024 AS size_mb, mf.growth * 8 / 1024 AS growth_mb, mf.is_percent_growth, CAST(mf.size * 8.0 / 1024 / 1024 AS DECIMAL(10,2)) AS size_gb, CAST(FILEPROPERTY(mf.name, 'SpaceUsed') * 8.0 / 1024 / 1024 AS DECIMAL(10,2)) AS used_gb FROM sys.databases db JOIN sys.master_files mf ON db.database_id = mf.database_id WHERE db.database_id > 4 ORDER BY size_gb DESC;

这里算出每个文件的逻辑名、物理路径、总大小、已用空间,一眼看到剩余空间。有个容易忽略的点:growth字段如果是按百分比增长,大型数据库扩容时可能瞬间分配很大空间,引起IO抖动。建议核心业务库的数据文件和日志文件都改成固定大小增长,比如每次增长2GB或4GB,避免频繁自动扩张带来的性能波动。

2.3 活动事务检查:清理日志失败的常见原因

还有一条我经常用的DBCC命令:

DBCC OPENTRAN('数据库名');

这条的作用是查看当前数据库中最早的活动事务。如果日志备份一直失败或者日志文件无法收缩,多半是有长事务没有提交。跑完这条如果看到类似“最早的活动事务”信息,就能定位到具体的事务开始时间和SPID。记得有一次某张业务表被一条未提交的UPDATE锁住,外部程序一直在等锁,日志也越涨越大,就是靠DBCC OPENTRAN找到事务源头,然后和业务侧确认后手动回滚才解除的。

3. 性能排查命令:从等待统计中揪出真正的瓶颈

数据库变慢,往往不是单一语句的问题,而是某个环节成为了瓶颈。我比较依赖等待统计来定位,因为等待统计是SQL Server自己记录的“卡在哪”的数据,比我们猜要准确得多。有个很形象的类比:医院急诊室护士会记录每个病人的等待原因和等待时间,等待统计就是数据库的“候诊记录表”。

3.1 等待类型汇总:先看整体等待分布

SELECT wait_type, wait_time_ms / 1000.0 AS wait_time_sec, waiting_tasks_count, signal_wait_time_ms / 1000.0 AS signal_wait_sec, (wait_time_ms - signal_wait_time_ms) / 1000.0 AS resource_wait_sec, wait_time_ms * 100.0 / SUM(wait_time_ms) OVER() AS wait_percent FROM sys.dm_os_wait_stats WHERE wait_type NOT IN ( 'BROKER_EVENTHANDLER', 'BROKER_RECEIVE_WAITFOR', 'BROKER_TASK_STOP', 'BROKER_TO_FLUSH', 'BROKER_TRANSMITTER', 'CHECKPOINT_QUEUE', 'CHKPT', 'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'CLR_SEMAPHORE', 'DBMIRROR_DBM_EVENT', 'DBMIRROR_EVENTS_QUEUE', 'DBMIRROR_WORKER_QUEUE', 'DBMIRRORING_CMD', 'DIRTY_PAGE_SCAN', 'DISPATCHER_QUEUE_SEMAPHORE', 'EXECSYNC', 'FSAGENT', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'FT_IFTSHC_MUTEX', 'HADR_CLUSAPI_CALL', 'HADR_NOTIFICATION_DEQUEUE', 'HADR_TIMER_TASK', 'HADR_WORK_QUEUE', 'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP', 'LOGMGR_QUEUE', 'MEMORY_ALLOCATION_EXT', 'ON_TASK_WAIT', 'PREEMPTIVE_OS_LIBRARYOPS', 'PREEMPTIVE_OS_COUNTOPS', 'PREEMPTIVE_OS_CRYPTOPS', 'PREEMPTIVE_OS_QUERYMDN', 'PREEMPTIVE_OS_TXITPROC', 'REQUEST_FOR_DEADLOCK_SEARCH', 'RESOURCE_QUEUE', 'SLEEP_BPOOL', 'SLEEP_BUFFERPOOL', 'SLEEP_DBSTARTUP', 'SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY', 'SLEEP_MASTERMDREADY', 'SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP', 'SLEEP_SYSTEMTASK', 'SLEEP_TASK', 'SLEEP_TEMPDBSTARTUP', 'SNI_HTTP_ACCEPT', 'SP_SERVER_DIAGNOSTICS_SLEEP', 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH', 'SQLTRACE_WAIT_ENTRIES', 'WAIT_FOR_RESULTS', 'WAITFOR', 'WAITFOR_TASKSHUTDOWN', 'WHILE_TEMP', 'XE_BUFFERMGR_ALLOC', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'XE_MODULEMGR_CALLNOTIFICATION', 'XE_LOGGER_TICK', 'XE_TIMER_EVENT' ) AND waiting_tasks_count > 0 ORDER BY wait_time_ms DESC;

这里我排除了大量自循环等待类型——比如SLEEP_TASK、WAITFOR这类属于空闲等待,如果不过滤掉,它们会占掉大部分统计,容易误导判断。看结果时我主要关注三块:排名靠前的是PAGEIOLATCH_*说明磁盘IO压力大,LCK_*说明存在锁阻塞,CXPACKET说明并行查询等待较多。每种等待的应对思路完全不同,不能盲目加索引或加内存。

3.2 最耗时查询排行:直接找出“罪魁祸首”

等待统计确认了方向后,下一步就是把最慢的语句找出来。我用的是执行计划缓存中的统计信息:

SELECT TOP 10 qs.total_elapsed_time / qs.execution_count AS avg_elapsed_ms, qs.total_worker_time / qs.execution_count AS avg_cpu_ms, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.execution_count, qs.total_elapsed_time, qs.total_logical_reads, qs.creation_time, SUBSTRING(st.text, (qs.statement_start_offset / 2) + 1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1) AS statement_text, qs.plan_handle FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st ORDER BY avg_elapsed_ms DESC;

这条能列出平均耗时最长、平均CPU最多、逻辑读取最多的语句。我通常按平均逻辑读取排序再看耗时,因为逻辑读太高的语句往往意味着全表扫描或索引缺失。得到plan_handle之后,还可以进一步查看该语句的完整执行计划:

SELECT query_plan FROM sys.dm_exec_query_plan(0x06000600...); -- 填入实际的 plan_handle

注意,query_plan返回的是XML格式,在SSMS中点击即可看到图形化执行计划。这里有个隐藏坑:执行计划缓存是动态的,实例重启或内存压力大时,旧的统计会被清掉。所以如果系统已经重启过或者跑了很长时间,sys.dm_exec_query_stats里只能看到缓存中的语句。想完整记录历史语句,还是得提前开扩展事件或第三方监控,不能只靠这一个视图。

3.3 缺失索引提示:参考,但不要无脑加

SQL Server提供一个很实用的动态管理视图可以直接告诉你“它觉得哪里缺索引”:

SELECT migs.avg_total_user_cost * migs.avg_user_impact AS improvement_score, migs.avg_user_impact, migs.avg_total_user_cost, mid.database_id, mid.object_id, mid.statement AS table_name, mid.equality_columns, mid.inequality_columns, mid.included_columns, 'CREATE INDEX [IX_' + OBJECT_NAME(mid.object_id, mid.database_id) + '_' + REPLACE(REPLACE(REPLACE(ISNULL(mid.equality_columns, ''), ', ', '_'), '[', ''), ']', '') + CASE WHEN mid.inequality_columns IS NOT NULL THEN '_' + REPLACE(REPLACE(REPLACE(mid.inequality_columns, ', ', '_'), '[', ''), ']', '') ELSE '' END + '] ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns, '') + CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END + ISNULL(mid.inequality_columns, '') + ')' + CASE WHEN mid.included_columns IS NOT NULL THEN ' INCLUDE (' + mid.included_columns + ')' ELSE '' END + ';' AS create_index_statement FROM sys.dm_db_missing_index_details mid JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mid.index_group_handle WHERE mid.database_id = DB_ID() ORDER BY improvement_score DESC;

这个视图会直接拼出建议的CREATE INDEX语句,看起来非常方便,但我要提醒一句:缺失索引建议只是基于成本评估的“建议”,不是“必须执行”。我在实际中见过不少案例,按提示加索引后单条查询快了不少,但整个表的写入变慢,甚至因为索引过多导致日志增长和存储膨胀。

我的习惯是:先人工判断这个缺失索引对应的查询是否高频,涉及的列是否在WHERE和JOIN条件中出现,选择性是否够好,再加一个INCLUDE的列是否能让查询覆盖。一条查询如果只跑一次,就算预测收益99%也不值得加索引。

4. 索引维护命令:碎片检测与重建/重组的完整决策链

索引维护是SQL Server日常运维里绕不开的话题,但也是被误解较多的。很多人只知道定期重建索引,却不问为什么重建、碎片到什么程度才需要重建。这里把完整的命令决策链写出来。

4.1 碎片详情查询:重建之前先看看数据

执行如下语句查看某个数据库所有索引的碎片情况:

SELECT OBJECT_NAME(ps.object_id) AS table_name, i.name AS index_name, ps.index_type_desc, ps.page_count, ps.avg_fragmentation_in_percent, ps.avg_page_space_used_in_percent, ps.record_count, ps.ghost_record_count FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') ps JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id WHERE ps.avg_fragmentation_in_percent > 5 AND ps.page_count > 100 AND ps.index_type_desc <> 'HEAP' ORDER BY ps.avg_fragmentation_in_percent DESC;

重点看两列:avg_fragmentation_in_percent代表碎片率,avg_page_space_used_in_percent代表页面填充率。碎片率只是表面指标,页面填充率过低说明页内空间浪费严重,即使碎片率不高也需要关注。

这里我踩过一个大坑:SAMPLED模式只采样部分页面,扫描大表很快,但结果可能不够准确。小表或者碎片极不均匀的表,用SAMPLED可能漏掉某些碎片区。更准确的是DETAILED模式,但DETAILED会扫描全部页面,大表耗时很长,必须在维护窗口执行。所以我的做法是:日常巡检用SAMPLED快速看,维护日再针对重点大表用DETAILED做最终确认。

4.2 重建与重组的选择:按碎片率分档处理

查到碎片后,按以下规则处理比较稳妥:

  • 碎片率低于5%:不需要处理,哪怕索引会继续膨胀。
  • 碎片率在5%到30%之间:执行重组ALTER INDEX ... REORGANIZE,这个是逻辑整理,不重建物理结构,不会产生大量日志。
  • 碎片率大于30%:执行重建ALTER INDEX ... REBUILD,物理上重新分配页,产生日志较多。

对应命令分别是:

-- 重组单个索引 ALTER INDEX [IX_表名_列名] ON [dbo].[表名] REORGANIZE; -- 重建单个索引 ALTER INDEX [IX_表名_列名] ON [dbo].[表名] REBUILD; -- 重建表上所有索引 ALTER INDEX ALL ON [dbo].[表名] REBUILD WITH (FILLFACTOR = 90, ONLINE = ON);

如果生产环境是企业版,重建索引时可以带ONLINE = ON,这样表在重建期间仍然可读写,业务影响小很多;标准版不支持在线重建,通常只能选择在维护窗口操作。另外,重建大表索引前一定要预估好日志空间。曾经有人在几小时内跑完重建,结果日志文件把磁盘塞爆,最后数据库进入只读。更稳妥的做法是对核心大表,在重建前先备份数据库,或者把数据库恢复模式临时改成简单模式(仅限于非关键库,并且要评估数据安全)。

4.3 填充因子与其他参数:别让索引维护白忙活

FILLFACTOR是索引页的填充百分比,它直接影响后续碎片产生的速度。默认值是0,等价于100%,即索引页尽可能填满。对于频繁插入的表,建议设置成90左右,预留一部分空间给后续插入,避免页分裂导致碎片快速增加;对于基本只读的表,可以保持默认,这样索引更紧凑,查询IO更少。

还有一个点容易被忽略——重建索引之后,对应的统计信息也会自动更新。所以通常不需要再单独执行UPDATE STATISTICS。但如果某个查询的执行计划一直不稳定,另当别论。

5. 阻塞与死锁排查命令:还原现场而不是只看到结果

数据库阻塞和死锁是运维中最头疼的问题之一。难点在于死锁发生那一刻就结束了,等你去查的时候现场已经没了。所以阻塞查询要“实时”跑,死锁追踪要“提前开”。以下是我排查这类问题时实际用到的命令组合。

5.1 实时阻塞链查询:从等待者找到源头

sp_who2是老一代DBA常用命令,胜在简单:

EXEC sp_who2;

但sp_who2返回的信息不够结构化,也不方便过滤。我现在更常用这样一条查询,能直接看到阻塞者和被阻塞者的关系:

SELECT r.session_id AS blocked_session_id, r.blocking_session_id AS blocking_session_id, r.wait_type, r.wait_time / 1000.0 AS wait_time_sec, r.status, t.text AS blocked_sql_text, b.text AS blocking_sql_text, es.host_name, es.login_name, es.program_name FROM sys.dm_exec_requests r LEFT JOIN sys.dm_exec_connections rc ON r.session_id = rc.session_id LEFT JOIN sys.dm_exec_connections bc ON r.blocking_session_id = bc.session_id CROSS APPLY sys.dm_exec_sql_text(rc.most_recent_sql_handle) t CROSS APPLY sys.dm_exec_sql_text(bc.most_recent_sql_handle) b JOIN sys.dm_exec_sessions es ON r.session_id = es.session_id WHERE r.blocking_session_id <> 0 AND r.blocking_session_id IS NOT NULL;

这条查询把阻塞者和被阻塞者两边的SQL文本都列出来,一眼就能看出是哪条语句锁住了什么资源。有一次排查超时告警,跑完后发现阻塞源头是一个长事务里的UPDATE语句,一直在更新某大表的同一批记录。往下一查,发现更新条件字段上没有索引,每次更新都在全表扫描,持有大量锁不释放,后面所有涉及该表的操作全被挡在门外。后续在该字段上补了索引,再配合业务侧缩短事务时间,问题才彻底解决。

5.2 历史死锁追踪:提前开启跟踪标记

死锁不像阻塞可以现场看到,更多时候需要我们翻历史记录。SQL Server默认会记录死锁相关信息,但信息比较简略。我建议在实例层面开启跟踪标记1222和1204,这样死锁发生时会把完整的资源和语句写到错误日志里:

DBCC TRACEON(1222, -1); DBCC TRACEON(1204, -1);

开启后,查看错误日志中的死锁信息:

EXEC sp_readerrorlog 0, 1, 'deadlock';

日志里会显示类似“deadlock victim”的字样,以及涉及的资源、进程ID和SQL语句。如果还嫌不够直观,可以开扩展事件来捕获xml_deadlock_report事件,保存死锁图形文件,用SSMS打开直接看环形依赖关系。我个人在生产环境更推荐扩展事件方式,因为它能保存完整的死锁图XML,排查效率比看文本日志高很多。

5.3 锁粒度和隔离级别:理解比命令本身更重要

排查锁问题到深处,必须理解隔离级别的影响。很多时候不是语句写错了,而是隔离级别设置不合理导致锁持有时间过长。比如默认的READ COMMITTED下,读操作只加共享锁,读完即释放;但如果改成REPEATABLE READ或SERIALIZABLE,读锁会一直持有到事务结束,极易造成阻塞放大。

-- 查看当前会话隔离级别 SELECT CASE transaction_isolation_level WHEN 0 THEN 'Unspecified' WHEN 1 THEN 'READ UNCOMMITTED' WHEN 2 THEN 'READ COMMITTED' WHEN 3 THEN 'REPEATABLE READ' WHEN 4 THEN 'SERIALIZABLE' WHEN 5 THEN 'SNAPSHOT' ELSE 'UNKNOWN' END AS isolation_level FROM sys.dm_exec_sessions WHERE session_id = @@SPID;

针对某些报表类查询,如果允许脏读,可以按语句级别加WITH (NOLOCK)提示,把读锁彻底去掉,但这要业务侧接受可能出现的中途被修改的数据。对核心交易逻辑,不建议用NOLOCK来绕过锁问题,正确做法是优化事务粒度、减少锁持有时间,或者开启READ COMMITTED SNAPSHOT(RCSI)让读操作走行版本快照。

6. 备份与完整性校验命令:出事后才做的操作其实最需要提前演练

备份算是SQL Server命令里最基础也最不能出错的部分。我发现很多团队的备份脚本是“能跑就行”,但从没做过恢复演练,真到出问题时才发现备份文件损坏或恢复链断裂。这里分享的是我实际使用的备份与校验命令组合。

6.1 全量备份与差异备份:参数不是随便填的

全量备份最常用的一条:

BACKUP DATABASE [数据库名] TO DISK = N'D:\Backup\数据库名_20260120.bak' WITH INIT, NAME = N'数据库名-全量备份', COMPRESSION, CHECKSUM, STATS = 10;

几个关键参数说明一下。INIT表示覆盖同名文件,如果不用INIT而且目标文件已存在,备份会追加到文件里,容易造成文件字节数增长且不可读。COMPRESSION启用备份压缩,能显著减小备份体积,但也更消耗CPU资源——如果服务器CPU本身已经长期高位,可以改成MAXTRANSFERSIZE调优而不是启用压缩。CHECKSUM在校验备份时非常关键,它会在备份过程中计算校验和,RESTORE VERIFYONLY时就能检查数据页是否有损坏。

差异备份的命令类似:

BACKUP DATABASE [数据库名] TO DISK = N'D:\Backup\数据库名_20260120_diff.bak' WITH DIFFERENTIAL, INIT, NAME = N'数据库名-差异备份', CHECKSUM;

需要强调差异备份是基于上一次全量备份的增量。如果全量备份被覆盖或删除,中间所有差异备份都失效。所以备份文件的保留策略必须严格,比如保留最近7天每日全量,再加上当天所有差异备份,不要手动去删中间文件。

6.2 事务日志备份:恢复时间点的关键

对恢复模式为FULL的数据库,必须定期备份事务日志:

BACKUP LOG [数据库名] TO DISK = N'D:\Backup\数据库名_20260120_log.trn' WITH INIT, CHECKSUM;

日志备份做得好,才能把数据库恢复到任意时间点。现实中我看到不少情况是全量备份和差异备份都有,但日志备份频率过低,甚至一天才一次。如果数据库在下午两点宕机,日志备份最后一次是早上六点,中间的数据就只能丢。核心交易库建议日志备份间隔控制在15分钟以内,备份文件不大,成本主要是IO开销和时间精度。

6.3 恢复校验:备份的真正考验是能不能恢复

备份完不等于万事大吉,我用一条命令验证备份文件完整性:

RESTORE VERIFYONLY FROM DISK = N'D:\Backup\数据库名_20260120.bak' WITH CHECKSUM;

这条只校验备份集完整性,不实际恢复数据,速度快,可作为日常监控任务的一部分。但需要说明,VERIFYONLY不能完全代替真正的恢复演练。我自己的做法是每季度至少做一次完整的RESTORE DATABASE到隔离环境的演练,确认备份文件能正常恢复并且应用日志链后能得到期望时间点。偶尔遇到备份文件在校验时报CHECKSUM错误的情况,就是因为备份过程中磁盘IO异常或文件受损,这种时候必须尽快定位物理磁盘问题,重新做备份,不能继续往这个文件上追加任何数据。

6.4 数据库完整性检查:DBCC CHECKDB的正确打开方式

除了备份,我还把DBCC CHECKDB纳入月度维护计划。它检查数据库逻辑和物理完整性,能发现页损坏、分配错误、逻辑一致性等问题:

DBCC CHECKDB([数据库名]) WITH NO_INFOMSGS, ALL_ERRORMSGS, PHYSICAL_ONLY;

大多数情况下,生产库我建议用PHYSICAL_ONLY模式,因为它只检查物理结构,不检查所有逻辑约束,耗时比完整检查短很多。维护窗口充足时,再跑不带PHYSICAL_ONLY的完整检查。还有一个实际建议:CHECKDB也是CPU和IO密集操作,尽量不要和索引重建、全量备份安排在同一时间段执行,免得互相抢资源。

7. 把命令整理成自己的巡检清单:多跑几遍你也有手感

最后分享一个我自己坚持的习惯。上面这些命令不是每次都需要全部跑一遍,我的做法是分为日常巡检、周度巡检、月度维护三档,对应不同频率和深度。

日常巡检(每天早上一遍):跑会话状态查询、数据库文件大小、DBCC OPENTRAN、备份是否成功。十分钟内完成,脚本里加好时间戳和状态高亮,异常时直接标红。周度巡检:加跑等待类型汇总、碎片率抽样、最耗时查询排行,用于发现性能退化趋势。月度维护:跑CHECKDB、索引重组或重建、季度恢复演练、完整死锁日志分析。

把这些命令组合到一起后,你会发现数据库问题大多是渐变的——日志文件在悄悄变大,某条语句的逻辑读在逐步上升,等待类型占比在慢慢偏移。只看某一天的快照很难发现问题,持续按固定频率采集并对比才能抓住趋势。

关于这些命令本身,我再补充一个建议:新建一个专门的查询脚本文件,把每条命令都写好注释和排序条件,别到用的时候临时打开SSMS手敲。人总是会记忆模糊的,尤其凌晨被叫起来处理故障时,一份固化的巡检脚本比什么都可靠。我几次凌晨排查问题能十分钟定位,靠的就是这份提前写好的脚本,以及平时养成的固定执行节奏。

相关新闻

MySQL 8.0性能调优实战:从配置、索引到SQL与监控

MySQL 8.0性能调优实战:从配置、索引到SQL与监控

MySQL 8.0 基础优化实战指南&#xff1a;从配置到性能调优很多人以为装好MySQL 8.0、把数据导进去&#xff0c;数据库就算"能用"了。直到某天业务慢得像蜗牛爬&#xff0c;开发跑过来喊了一句"数据库卡了"&#xff0c;你才发现默认配置和真实负载之间隔着一…

2026/10/19 21:52:07 阅读更多 →
开源CLI代码审查工具:Git原生集成与LLM增强的静态分析实践

开源CLI代码审查工具:Git原生集成与LLM增强的静态分析实践

1. 项目概述&#xff1a;一个真正能落地的开源代码审查 CLI 工具 “open-code-review”这个名字乍一听像某个 GitHub 上刚起步的玩具项目&#xff0c;但如果你在团队里干过三年以上后端开发&#xff0c;或者带过两个以上跨地域协作的前端小组&#xff0c;你大概率会立刻坐直身…

2026/10/19 21:52:07 阅读更多 →
开源代码审查工具open-code-review:规则引擎与CI落地实践

开源代码审查工具open-code-review:规则引擎与CI落地实践

1. 为什么我要自己做一套开源代码审查工具1.1 代码审查是刚需&#xff0c;但痛点比想象中多先说个背景。我所在的团队前后端加起来不到二十人&#xff0c;代码量随着业务增长涨得飞快。代码审查这件事&#xff0c;我们一直靠人工交叉Review加几个Lint工具硬扛。刚开始还好&…

2026/10/19 21:51:06 阅读更多 →

最新新闻

DeepSeek降AI指令5步流程:从AI味到自然学术写作

DeepSeek降AI指令5步流程:从AI味到自然学术写作

最近太多人在问“DeepSeek降AI指令怎么写”&#xff0c;尤其是论文写了一半&#xff0c;拿AI润色完&#xff0c;自己一读都觉得“太工整了”&#xff0c;然后被各种AI痕迹检测提示搞得心里发毛。先说结论&#xff1a;所谓“降AI”&#xff0c;并不是玄学&#xff0c;本质是让大…

2026/10/20 3:26:04 阅读更多 →
dnastar安装全攻略:许可证配置与常见报错排查

dnastar安装全攻略:许可证配置与常见报错排查

简介&#xff1a;DNASTAR是生物信息学领域广泛使用的序列分析与分子生物学工具套件&#xff0c;这份完整安装包面向从事基因测序、基因组组装及蛋白质结构预测的科研人员。压缩包内共2个文件&#xff0c;包括Windows系统下的Lasergene 7.1安装程序和一个HTML说明文档&#xff0…

2026/10/20 3:26:04 阅读更多 →
免费导出原神抽卡记录:4 步把最近 6 个月的完整祈愿存档拉到本地

免费导出原神抽卡记录:4 步把最近 6 个月的完整祈愿存档拉到本地

免费导出原神抽卡记录&#xff1a;4 步把最近 6 个月的完整祈愿存档拉到本地 【免费下载链接】genshin-wish-export Easily export the Genshin Impact wish record. 项目地址: https://gitcode.com/GitHub_Trending/ge/genshin-wish-export 凌晨的群里&#xff0c;朋友…

2026/10/20 3:26:04 阅读更多 →
基于Python的电影数据可视化分析平台设计与实现全流程解析

基于Python的电影数据可视化分析平台设计与实现全流程解析

每年到了做毕业设计的季节&#xff0c;电影数据可视化这个题目都会成批出现。它的热门是有道理的&#xff1a;题材有趣、图表效果醒目、技术栈成熟&#xff0c;随便截两张图放PPT里都很好看。但也是因为做的人实在太多&#xff0c;如果只是从某个网站下载一套现成数据&#xff…

2026/10/20 3:26:04 阅读更多 →
MATLAB进化计算实战:PSO、DE与GA求解NP难题

MATLAB进化计算实战:PSO、DE与GA求解NP难题

简介&#xff1a;面向石化生产计划与作业车间调度&#xff08;JSSP&#xff09;等 NP 难题的 MATLAB 进化计算资源包&#xff0c;以系统开源方式提供&#xff0c;适合从事优化算法研究或工程应用的工程师、研究生。内容覆盖粒子群、人工蜂群&#xff08;ABC&#xff09;与教学学…

2026/10/20 3:26:04 阅读更多 →
从零到一构建Web应用:前端工程化与REST API联调实战

从零到一构建Web应用:前端工程化与REST API联调实战

&#xff08;二&#xff09;这个编号&#xff0c;其实是整个系列里最难写的一篇。&#xff08;一&#xff09;通常熬过了需求梳理和技术选型&#xff0c;读者们热情还在&#xff0c;但你到了第二篇&#xff0c;面对的是一大堆还没落地的代码骨架、各种边角约定&#xff0c;以及…

2026/10/20 3:25:04 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →