DB2事件监视器(EVENT MONITOR)是数据库自带的一种诊断工具,用于捕获特定事件发生时数据库内部的关键信息。当数据库活动繁忙、应用响应迟缓时,DBA最常做的一件事就是找出哪些SQL语句消耗了最多资源,或者哪些语句引发了锁等待。事件监视器通过定义监视条件,可以把这些SQL语句的完整文本、执行消耗、时间戳等数据记录下来,为后续分析提供第一手素材。

理解事件监视器捕获SQL的原理,需要先明确它和快照(Snapshot)的区别。快照是某个时间点数据库状态的静态副本,而事件监视器是持续记录满足条件的事件流。例如,当一个SQL语句执行时间超过阈值,事件监视器可以在语句完成时立即写入一条记录,而快照只能在手工触发时才能看到当前正在执行的语句。因此事件监视器更适合捕获那些稍纵即逝的执行细节。
事件监视器的类型与SQL捕获能力
DB2提供了多种事件监视器类型,每种类型关注不同的事件集合。对SQL捕获而言,最常用的是语句事件监视器(STATEMENT事件)和活动事件监视器(ACTIVITIES事件)。语句事件监视器记录SQL语句的编译和执行信息,包括语句开始时间、结束时间、CPU消耗、读取的行数、排序次数、锁等待时间等。活动事件监视器则更偏向于工作负载管理,可以记录活动单元(Unit of Work)的完整生命周期,适合深度分析事务行为。
还有一种包事件监视器(PACKAGE事件)和连接事件监视器(CONNECTION事件),前者记录包的加载和缓存命中情况,后者记录数据库连接的建立和断开。如果你想单纯捕获SQL语句的执行情况,语句事件监视器是最直接的选择。它可以在语句完成时触发,也可以在语句开始时触发,具体取决于你定义的写入条件。比如可以设置只记录执行时间超过500毫秒的语句,避免日志被海量快速查询淹没。
在决定使用哪种事件监视器之前,需要评估捕获的数据量和分析需求。如果把所有SQL都记录下来,在高并发环境下可能每分钟产生上千条记录,不仅占用大量磁盘空间,还会影响数据库性能。实践中的常见做法是结合阈值条件,只捕获慢SQL或者由特定应用发出的SQL。DB2支持通过WHERE子句定义过滤条件,例如限制APPL_NAME等于某个应用名称,或者ELAPSED_TIME大于某个值。
创建事件监视器捕获SQL的具体步骤
下面演示如何创建一个语句事件监视器,并把捕获到的SQL写入文件。首先需要确认DB2实例的权限,创建事件监视器通常需要SQLADM或DBADM权限。接着使用CREATE EVENT MONITOR语句定义监视器的名称、事件类型和输出目标。输出目标可以是文件(WRITE TO FILE)或者管道(WRITE TO PIPE),文件方式更常用,便于后续用工具读取。
CREATE EVENT MONITOR sql_capture FOR STATEMENTS WRITE TO FILE '/db2mon/sql_capture' MAXFILES 10 MAXFILESIZE 1000 BUFFERSIZE 8 AUTOSTART;
上述语句创建了一个名为sql_capture的语句事件监视器,输出到目录/db2mon/sql_capture下。MAXFILES 10表示最多保留10个日志文件,MAXFILESIZE 1000表示每个文件最大1000个4KB页,BUFFERSIZE 8指定缓冲区大小。AUTOSTART选项让数据库启动时自动激活该监视器。如果不加AUTOSTART,则需要手动执行SET EVENT MONITOR sql_capture STATE = 1来启动。
如果需要捕获特定条件的SQL,可以在语句事件监视器中加入WHERE子句。DB2的语句事件监视器支持基于语句属性的过滤,比如APPL_NAME、AUTHID、EXECUTABLE_ID、STMT_TEXT等。例如只捕获执行时间超过200毫秒的语句,可以这样写:
CREATE EVENT MONITOR slow_sql FOR STATEMENTS WRITE TO FILE '/db2mon/slow_sql' MAXFILES 20 MAXFILESIZE 500 WHERE EVENT_TYPE = 'STATEMENT' AND ELAPSED_TIME > 200 AUTOSTART;
创建完成后,使用以下命令查看事件监视器状态:
SELECT EVMONNAME, EVENT_MON_STATE, TARGET_TYPE FROM SYSCAT.EVENTMONITORS;
启动和停止事件监视器的命令分别是SET EVENT MONITOR sql_capture STATE = 1和SET EVENT MONITOR sql_capture STATE = 0。停止后,可以把生成的日志文件用db2evmon工具格式化,或者通过表函数读取。db2evmon是命令行工具,直接对二进制日志文件进行解析,输出可读的文本格式。
解读事件监视器输出与性能分析
事件监视器生成的日志文件是二进制格式,不能直接查看。DB2提供了db2evmon工具将其转换为文本报告。假设日志文件路径为/db2mon/sql_capture/00000000.evt,可以执行命令db2evmon -db sample -evm sql_capture > /tmp/sql_report.txt,生成包含所有捕获语句的详细报告。报告中每个语句记录包含语句文本、开始和结束时间戳、CPU时间、执行时间、读取的行数、锁等待时间、排序溢出次数等字段。
分析报告时,重点关注几个高频指标。EXECUTION_TIME(或ELAPSED_TIME)表示语句从开始到结束的总耗时,包括等待锁和IO的时间。CPU_TIME表示实际使用CPU的时间,两者差异大说明语句在等待资源。ROWS_READ表示读取的行数,如果这个值很高但结果集很小,通常意味着缺少合适的索引导致全表扫描。SORT_OVERFLOWS表示排序过程中溢出到磁盘的次数,频繁溢出会严重影响性能,需要检查排序内存配置。
除了db2evmon,还可以使用SQL表函数直接查询事件监视器文件。例如将文件注册为表函数:
SELECT * FROM TABLE(SYSPROC.EVMON_FORMAT_UE_TO_TABLE(
'/db2mon/sql_capture',
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
)) AS T;
该方法可以把事件数据转换成关系表,方便用SQL进行聚合统计。比如按语句文本分组,统计每个SQL的总执行次数和平均执行时间,快速找出最耗时的TOP SQL。不过要注意,EVMON_FORMAT_UE_TO_TABLE函数要求监视器以非格式化方式写入(默认就是非格式化),并且文件路径需正确指向包含日志文件的目录。
常见误区与优化建议
使用事件监视器捕获SQL时,一个常见的误区是不加过滤条件地全量记录。在业务高峰期,全量捕获会显著增加数据库开销,甚至导致I/O瓶颈。正确的做法是先通过监控工具识别可疑时间段,再在低峰期开启带有阈值的监视器,或者只对特定应用、特定用户进行捕获。另外,很多人忽视MAXFILES和MAXFILESIZE参数的规划,导致日志文件无限增长撑爆磁盘。务必根据业务量估算每小时产生的事件数量,合理设置文件滚动策略。
另一个问题是监视器输出目录的权限和空间。DB2实例用户必须对输出目录有写权限,且目录所在文件系统要有足够的剩余空间。如果空间不足,监视器会自动停止,但可能不会给出明显报错。建议将输出目录放在独立的高性能磁盘上,避免与数据文件争抢IO。对于需要长期保留的SQL捕获数据,可以定期将旧的日志文件归档到备份存储。
最后,事件监视器捕获SQL只是诊断的第一步。拿到SQL文本和性能指标后,还需要结合执行计划(EXPLAIN)进一步分析。可以提取出高成本的SQL,在开发环境中使用db2expln或db2advis工具生成访问计划,检查是否缺少索引、是否存在不必要的表扫描。只有把事件监视器的数据与执行计划分析结合起来,才能真正定位并解决SQL性能问题。