如何监控SQL Server数据库活动 定位软件操作触发的级联操作
不用依赖SQL Trace/Profiler这类已废弃的高开销工具,分两步就能查全所有关联动作,对生产库影响极小。
第一步:零开销静态元数据排查
先通过系统视图把目标表上直接绑定的触发类对象全部拉出来,不需要启动任何捕获任务,1秒出结果,能覆盖80%以上的常规级联场景。
执行下面的SQL,把变量替换成你实际的发票表所属架构、表名即可:
DECLARE @TargetSchema SYSNAME = 'dbo', @TargetTable SYSNAME = '替换为实际发票表名'; -- 查询表上启用的INSERT类型触发器 SELECT trig.name AS 触发器名称, OBJECT_DEFINITION(trig.object_id) AS 触发器完整定义 FROM sys.triggers trig INNER JOIN sys.tables t ON trig.parent_id = t.object_id INNER JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE s.name = @TargetSchema AND t.name = @TargetTable AND trig.is_disabled = 0 AND OBJECTPROPERTY(trig.object_id, 'ExecIsInsertTrigger') = 1; -- 查询所有关联的外键级联规则 SELECT fk.name AS 外键名称, CASE WHEN fk.parent_object_id = t.object_id THEN OBJECT_NAME(fk.referenced_object_id) ELSE OBJECT_NAME(fk.parent_object_id) END AS 关联表名, fk.delete_referential_action_desc AS 删除级联规则, fk.update_referential_action_desc AS 更新级联规则 FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id OR fk.referenced_object_id = t.object_id INNER JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE s.name = @TargetSchema AND t.name = @TargetTable;
注意:如果查询结果中出现CLR类型的触发器、存储过程,这部分是托管代码实现的逻辑,无法通过SQL直接查看定义,需要额外和第三方厂商确认,这类场景在发票保存业务中极少出现。
静态查询只能拿到第一层绑定的对象,如果触发器里嵌套调用存储过程、存储过程又操作其他表触发二级触发器,就需要用下面的轻量运行时捕获方案拿全链路动作。
第二步:低开销运行时全链路捕获
用SQL Server原生的扩展事件(Extended Events)做捕获,比SQL Trace开销低90%以上,不需要安装任何插件,SSMS里直接执行即可。
2.1 捕获手动插入触发的动作
如果是验证你自己手动插入数据的触发链路,直接执行下面的脚本创建会话,会话会只捕获你当前查询窗口的操作,完全不会抓取其他业务请求,零业务干扰:
-- 创建捕获会话,替换为实际的客户侧数据库名 CREATE EVENT SESSION [Catch_Invoice_Insert] ON SERVER ADD EVENT sqlserver.sql_statement_completed( ACTION(sqlserver.sql_text,sqlserver.object_name,sqlserver.username) WHERE sqlserver.database_name = N'替换为客户侧数据库名' AND sqlserver.session_id = @@SPID ), ADD EVENT sqlserver.sp_statement_completed( ACTION(sqlserver.sql_text,sqlserver.object_name,sqlserver.username) WHERE sqlserver.database_name = N'替换为客户侧数据库名' AND sqlserver.session_id = @@SPID ) ADD TARGET package0.event_file(SET filename=N'Catch_Invoice_Insert.xel',max_file_size=(5)) WITH (STARTUP_STATE=OFF); GO -- 启动会话 ALTER EVENT SESSION [Catch_Invoice_Insert] ON SERVER STATE = START; GO
会话启动后,直接在当前窗口执行你准备好的INSERT测试语句,执行完成后运行下面的SQL停止会话并查询所有捕获到的操作:
-- 停止会话 ALTER EVENT SESSION [Catch_Invoice_Insert] ON SERVER STATE = STOP; -- 查询全链路执行记录,按执行时间排序 SELECT event_data.value('(event/@timestamp)[1]', 'datetime2') AS 执行时间, event_data.value('(event/action[@name="object_name"]/value)[1]', 'sysname') AS 操作涉及对象, event_data.value('(event/action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS 执行SQL内容 FROM ( SELECT CAST(event_data AS XML) AS event_data FROM sys.fn_xe_file_target_read_file('Catch_Invoice_Insert*.xel', NULL, NULL, NULL) ) t ORDER BY 执行时间 ASC; -- 测试完成后删除会话即可 DROP EVENT SESSION [Catch_Invoice_Insert] ON SERVER;
2.2 捕获软件端保存操作触发的动作
如果要对照软件本身点击「保存发票」触发的链路,只需要先执行sp_who2找到软件连接数据库对应的会话ID(SPID,根据登录名、主机名筛选即可),把2.1脚本里过滤条件的sqlserver.session_id = @@SPID替换成sqlserver.session_id = 你查到的软件SPID即可,建议在业务低峰操作,抓完立刻停止会话,对业务的影响可以忽略。
补充说明
- 不要使用SQL Trace/Profiler,该特性从SQL Server 2012开始已被标记为废弃,相同捕获粒度下资源消耗是扩展事件的10倍以上,高并发场景下可能拖慢业务。
- 如果链路中存在跨实例的链接服务器操作,只需要在对应目标实例上创建同配置的扩展事件会话,过滤对应访问的SPID即可抓全链路。
内容的提问来源于stack exchange,提问作者DevOps Admin

