首批通过分布式安全可靠测评,为关键业务系统打造
SQL Audit
更新时间:2026-04-09 19:53:00
GV$OB_SQL_AUDIT 是最常用的 SQL 监控视图,能够记录每一次 SQL 请求的来源、执行状态、资源消耗及等待事件,除此之外还记录了 SQL 文本、执行计划等关键信息。该视图是诊断 SQL 问题的利器。
该视图字段较多,我们分段介绍。其主要字段介绍如下:
TENANT_ID:请求的租户 ID。SVR_IP:接受请求的服务端节点 IP。CLIENT_IP:发送请求的客户端 IP。REQUEST_TIME:请求到达时间。REQUEST_ID:请求的 ID,标识请求的一次执行,随时间而递增。外部监控系统可以将该字段作为游标拉取审计信息。IS_INNER_SQL:是否内部 SQL 请求。IS_EXECUTOR_RPC:当前请求是否 RPC 请求。SQL_ID:标识一条具体的 SQL,同一条 SQL 的多次执行具有相同的SQL_ID和不同的REQUEST_ID。QUERY_SQL:SQL 语句完整的文本。OceanBase 数据库支持通过SQL_ID和SQL_TEXT绑定执行计划。SID:标识一个 Session,可以关联该连接上的所有 SQL,及等待事件。对应GV$OB_PROCESSLIST视图中的ID字段。TX_ID:标识一个事务,可以关联该事务的所有 SQL。如果一个事务中连续执行的两条 SQL 的起止时间存在较大差距,说明链路上的耗时较大。TRACE_ID:该 SQL 请求的 trace ID,可以关联查询其他监控指标,也可以关联查询日志。IS_HIT_PLAN:是否命中 Plan Cache 中的 Plan。SQL 优化比较耗时,为了避免反复执行 SQL 优化,会将生成的计划放进 Plan Cache,再次执行时直接从 Plan Cache 获取 Plan。未命中 Plan Cache 的情况称为硬解析,命中 Plan Cache 的情况称为软解析。硬解析会影响 SQL 性能,增加耗时。如果租户 Plan Cache 命中率过低,需要确认 Plan Cache 空间是否过小导致 Plan 频繁被淘汰。PLAN_ID:执行计划 ID,可以关联查询该计划的详细信息和统计信息。PLAN_HASH:执行计划的 Hash 值。PLAN_TYPE:执行计划的类型,取值为0、1、2、3,其中:1、2、3分别对应本地计划、远程计划、分布式计划;0表示无执行计划,例如 commit 语句。AFFECTED_ROWS:影响行数。RETURN_ROWS:返回行数。RET_CODE:执行结果返回码。EVENT:最长等待事件名称。P1TEXT ~ P3TEXT:等待事件参数 1 ~ 3。P1 ~ P3:等待事件参数的值 1~3。LEVEL:等待事件的 level 级别。WAIT_CLASS_ID:等待事件所属的类别 ID。WAIT_CLASS:等待事件所属的类别名称。STATE:等待事件的状态。WAIT_TIME_MICRO:该等待事件所等待的时间(微秒)。TOTAL_WAIT_TIME_MICRO:执行过程所有等待的总时间(微秒)。TOTAL_WAITS:执行过程总等待的次数。ELAPSED_TIME:本次执行的总时间(从请求到达到执行结束),由多个子阶段组成:NET_TIME:发送 RPC 到接收到请求的时间。NET_WAIT_TIME:接收到请求到进入队列时间。QUEUE_TIME:队列时间,反映当前租户的请求积压情况。DECODE_TIME:出队列后 Decode 时间。GET_PLAN_TIME:生成执行计划的时间,反映当前租户 Plan Cache 的健康状况。EXECUTE_TIME:计划的执行时间。
EXECUTE_TIME:本次实际执行的时间,由 CPU 时间和TOTAL_WAIT_TIME_MICRO组成。TOTAL_WAIT_TIME_MICRO由APPLICATION_WAIT_TIME、CONCURRENCY_WAIT_TIME、USER_IO_WAIT_TIME、SCHEDULE_TIME等几个部分组成。EXECUTE_TIME为实际上花费执行的总时间,包含 CPU 计算所需的时间(CPU_TIME)和各种等待时间(TOTAL_WAIT_TIME_MICRO)。APPLICATION_WAIT_TIME:所有 application 类事件的总时间。CONCURRENCY_WAIT_TIME:所有 concurrency 类事件的总时间。USER_IO_WAIT_TIME:所有user_io类事件的总时间。SCHEDULE_TIME:所有`schedule 类事件的时间。
- 逻辑读:请求执行中读取数据时会首先读取各级缓存(对应
ROW_CACHE_HIT、BLOOM_FILTER_CACHE_HIT、BLOCK_CACHE_HIT等字段),如果全部没有命中会产生一次实际的磁盘读取(对应DISK_READS字段)。通过统计缓存读取次数和磁盘读取次数,可以得出该请求执行过程中扫描行数的多少(扫描行数不等于实际物理读,会首先扫描各级缓存),从而判断该 SQL 是否需要优化。ROW_CACHE_HIT:行缓存命中次数。BLOOM_FILTER_CACHE_HIT:bloom filter缓存命中次数。BLOCK_CACHE_HIT:块缓存命中次数。DISK_READS:物理读次数。
RETRY_CNT:重试次数。TABLE_SCAN:判断该请求是否含全表扫描。CONSISTENCY_LEVEL:一致性级别。MEMSTORE_READ_ROW_COUNT:MemStore 中读的行数。SSSTORE_READ_ROW_COUNT:SSStore 中读的行数。REQUEST_MEMORY_USED:该请求消耗的内存。INSERT_DUPLICATE_ROW_COUNT: 记录insertup或者replace into时重复行数的数量
GV$OB_SQL_AUDIT 视图的数据存放在一个可配置的内存空间中,每个租户在每个节点上都有一块独立的缓存,当内存使用或记录数达到淘汰上限时会触发自动淘汰,最久的数据优先淘汰。有经验的 DBA 在排查 SQL 问题时,往往第一件事就是关闭 SQL Audit 功能以保存现场,避免抖动现场的监控数据被淘汰。
通过以下参数可以控制 SQL Audit 功能的行为:
enable_sql_audit:集群配置项,控制全部租户的 SQL Audit 功能是否开启,动态生效。ob_enable_sql_audit:租户级别的系统变量,控制当前租户是否开启 SQL Audit 功能,动态生效。ob_sql_audit_percentage:租户级别的系统变量,控制当前租户 SQL Audit 功能占用租户内存的百分比,动态生效。为防止 SQL Audit 占用过高内存,系统设置其内存上限为 1GB。
通过 GV$OB_SQL_AUDIT 视图,我们可以方便的查询 SQL 执行的各种维度信息。
查询执行耗时超过 100ms 的 SQL。
obclient> select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10; +------------+----------------------------+--------------+------------+--------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | request_id | usec_to_time(request_time) | ELAPSED_TIME | QUEUE_TIME | EXECUTE_TIME | query_sql | +------------+----------------------------+--------------+------------+--------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | 1538599798 | 2023-03-08 11:00:46.089711 | 335152 | 462 | 329196 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538601580 | 2023-03-08 11:00:47.411316 | 276913 | 1420 | 275345 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538603976 | 2023-03-08 11:00:49.258464 | 154873 | 461 | 154236 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538613501 | 2023-03-08 11:00:56.123111 | 188973 | 688 | 188144 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538712684 | 2023-03-08 11:02:07.504777 | 288516 | 1137 | 287180 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538743161 | 2023-03-08 11:02:29.135127 | 289585 | 26 | 289380 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538749786 | 2023-03-08 11:02:33.890317 | 294356 | 45 | 294180 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538792259 | 2023-03-08 11:03:04.626596 | 192843 | 128 | 192569 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538799117 | 2023-03-08 11:03:09.567622 | 201594 | 55 | 201388 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | | 1538804299 | 2023-03-08 11:03:13.274090 | 235720 | 241 | 235302 | select request_id,usec_to_time(request_time),ELAPSED_TIME,QUEUE_TIME,EXECUTE_TIME,query_sql from v$OB_SQL_AUDIT where ELAPSED_TIME > 100000 limit 10 | +------------+----------------------------+--------------+------------+--------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 10 rows in set (0.28 sec)查询最近 1000 条 SQL 的平均排队时间。
obclient> select /*+ query_timeout(30000000) */ avg(queue_time) from v$OB_SQL_AUDIT where request_id > (select max(request_id) from v$OB_SQL_AUDIT) - 1000 ; +-----------------+ | avg(queue_time) | +-----------------+ | 350.8740 | +-----------------+ 1 row in set (0.26 sec)查询占用租户资源最多的 SQL,按照
执行时间 * 执行次数倒序排序,如果该租户当前存在容量不足(租户 CPU 使用率被打爆),可以通过该语句判断是否为 SQL 问题,及疑点 SQL。obclient> select SQL_ID, avg(ELAPSED_TIME), avg(QUEUE_TIME), avg(ROW_CACHE_HIT + BLOOM_FILTER_CACHE_HIT + BLOCK_CACHE_HIT + DISK_READS) avg_logical_read, avg(execute_time) avg_exec_time, count(*) cnt, avg(execute_time - TOTAL_WAIT_TIME_MICRO ) avg_cpu_time, avg( TOTAL_WAIT_TIME_MICRO ) avg_wait_time, WAIT_CLASS, avg(retry_cnt) from v$OB_SQL_AUDIT group by 1 order by avg_exec_time * cnt desc limit 10; +----------------------------------+-------------------+-----------------+------------------+---------------+--------+--------------+---------------+------------+----------------+ | SQL_ID | avg(ELAPSED_TIME) | avg(QUEUE_TIME) | avg_logical_read | avg_exec_time | cnt | avg_cpu_time | avg_wait_time | WAIT_CLASS | avg(retry_cnt) | +----------------------------------+-------------------+-----------------+------------------+---------------+--------+--------------+---------------+------------+----------------+ | 2705182A6EAB699CEC8E59DA80710B64 | 54976.9269 | 43.8605 | 17664.2727 | 54821.5828 | 11759 | 54821.5828 | 0.0000 | OTHER | 0.0000 | | 32AB97A0126F566064F84DDDF4936F82 | 1520.9832 | 380.7903 | 63.7847 | 789.6781 | 63632 | 789.6781 | 0.0000 | OTHER | 0.0000 | | A5F514E873BE9D1F9A339D0DA7481D69 | 44032.5553 | 44.5149 | 8943.7834 | 43878.1405 | 1039 | 43878.1405 | 0.0000 | OTHER | 0.0000 | | 31FD78420DB07C11C8E3154F1658D237 | 7769857.0000 | 35.7500 | 399020.7500 | 7769682.7500 | 4 | 7769682.7500 | 0.0000 | NETWORK | 1.0000 | | C48AEE941D985D8DEB66892228D5E845 | 8528.6227 | 0.0000 | 0.0000 | 8450.4047 | 1601 | 8450.4047 | 0.0000 | OTHER | 0.0000 | | 101B7B79DFA9AE801BEE4F1A234AD294 | 158.2296 | 41.7211 | 0.0000 | 46.0345 | 286758 | 46.0345 | 0.0000 | OTHER | 0.0000 | | 1D0BA376E273B9D622641124D8C59264 | 1774.5924 | 0.0049 | 0.0000 | 1737.4885 | 5081 | 1737.4885 | 0.0000 | OTHER | 0.0000 | | 64CF75576816DB5614F3D5B1F35B1472 | 1801.8767 | 747.0343 | 0.0000 | 827.1674 | 10340 | 827.1674 | 0.0000 | OTHER | 0.0000 | | 23D1C653347BA469396896AD9B20DCA1 | 5564.9419 | 0.0000 | 0.0000 | 5478.2228 | 1257 | 5478.2228 | 0.0000 | OTHER | 0.0000 | | FA4F493FA5CE2DCC64F51CF3754F96C6 | 2478.3956 | 378.7557 | 3.1040 | 1731.1802 | 3357 | 1731.1802 | 0.0000 | OTHER | 0.0000 | +----------------------------------+-------------------+-----------------+------------------+---------------+--------+--------------+---------------+------------+----------------+ 10 rows in set (1.34 sec)说明
- 租户抖动时,表现一般是 “租户 CPU 被打满” 和 “所有 SQL RT 飙升”,此时首要判断 SQL RT 飙升是否是第一案发现场(是 SQL 自己的问题导致 RT 飙升,还是其他问题导致 SQL RT 飙升)。
- 上面介绍的这条 SQL 是一大利器,基于
SQL_ID做聚合,按照资源占用量倒序排序(资源占用量可以认为是avg_exec_time * cnt),观察 top 几条 SQL 是否存在明显异常,从而判断是否是第一案发现场。