走进SQL审计视图——《OceanBase诊断系列》之二

1. 前言

在SQL性能诊断上,OceanBase有一个非常实用的功能 ------ SQL审计视图(gvsql_audit)。在OceanBase 4.0.0及更高版本中,该功能是 gvob_sql_audit。它可以使开发和运维人员更方便地排查在OceanBase上运行过的任意一条SQL,无论这些SQL是成功与否,都有详细的运行信息记录。这些信息包括客户端和服务端的IP端口、SQL语句、执行时间、执行节点、执行计划ID、会话ID、执行时间、等待时间、总时间、排队时间、以及相关的块读取信息和执行报错信息等。

|-------------------|--------------------------------------------------------------|
| 查询方式 | 说明 |
| (g)vob_sql_audit | OceanBase 4.0.0.0 及以上版本,gvxx查询该租户所有机器vxxx查询该租户本机器(不保证路由准确) | | (g)vsql_audit | OceanBase 4.0.0.0 以下版本gvxx查询该租户所有机器vxxx查询该租户本机器(不保证路由准确) |

sql_audit是基于虚拟表__all_virtual_sql_audit的视图, 该虚拟表对应的数据存放在一个可配置的内存空间中,能够记录并显示每一次SQL请求的来源、执行状态及统计信息,由于存放这些记录的内存是有限的,因此到达一定内存使用量,会触发淘汰。

  • sql_audit 每隔 1s 会检测后台任务并根据以下标准决定是否淘汰:
    • sql_audit 内存最大可使用上限为 avail_mem_limit = min (OBServer 可使用内存 *10%,sql_audit_memory_limit)。
    • 当 avail_mem_limit 在 [64M, 100M] 范围内时, 内存使用达到 avail_mem_limit-20M 时触发淘汰。
    • 当 avail_mem_limit 在 [100M, 5G] 范围内时, 内存使用达到 availmem_limit*0.8 时触发淘汰。
    • 当 avail_mem_limit 在 [5G, +∞)范围内时, 内存使用达到 availmem_limit-1G 时触发淘汰。
    • 当 sql_audidt 记录数超过 900 万条时,触发淘汰。
  • sql_audit 根据以下标准决定是否停止淘汰:
    • 如果是达到内存上限触发淘汰则:
    • 当 avail_mem_limit 在 [64M, 100M] 时, 内存使用淘汰到 avail_mem_limit-40M 时停止淘汰。
    • 当 avail_mem_limit 在 [100M, 5G] 时, 内存使用淘汰到 availmem_limit*0.6 时停止淘汰。
    • 当 avail_mem_limit 在 [5G, +∞] 时, 内存使用淘汰到 availmem_limit-2G 时停止淘汰。
    • 如果是达到记录数上限触发的淘汰则淘汰到 800 万行记录时停止淘汰。

2. sql_audit视图字段介绍

|-------------------------|---------------------|----------------------|
| 字段名称 | 类型 | 描述 |
| SVR_IP | varchar(32) | ip地址 |
| SVR_PORT | bigint(20) | 端口号 |
| REQUEST_ID | bigint(20) | 请求的id号 |
| TRACE_ID | varchar(128) | 这条语句的trace_id |
| CLIENT_IP | varchar(32) | 发送请求的client ip |
| CLIENT_PORT | bigint(20) | 发送请求的client port |
| TENANT_ID | bigint(20) | 发送请求的租户id |
| TENANT_NAME | varchar(64) | 发送请求的租户 名称 |
| USER_ID | bigint(20) | 发送请求的用户id |
| USER_NAME | varchar(64) | 发送请求的用户名称 |
| SQL_ID | varchar(32) | 这条SQL的id |
| QUERY_SQL | varchar(32768) | 实际的SQL语句 |
| PLAN_ID | bigint(20) | 执行计划id |
| AFFECTED_ROWS | bigint(20) | 影响行数 |
| RETURN_ROWS | bigint(20) | 返回行数 |
| PARTITION_CNT | bigint(20) | 该请求涉及的分区数 |
| RET_CODE | bigint(20) | 执行结果返回码 |
| EVENT | varchar(64) | 最长等待事件名称 |
| P1TEXT | varchar(64) | 等待事件参数1 |
| P1 | bigint(20) unsigned | 等待事件参数1的值 |
| P2TEXT | varchar(64) | 等待事件参数2 |
| P2 | bigint(20) unsigned | 等待事件参数2的值 |
| P3TEXT | varchar(64) | 等待事件参数3 |
| P3 | bigint(20) unsigned | 等待事件参数3的值 |
| LEVEL | bigint(20) | 等待事件的level级别 |
| WAIT_CLASS_ID | bigint(20) | 等待事件所属的class id |
| WAIT_CLASS# | bigint(20) | 等待事件所属的class 的下标 |
| WAIT_CLASS | varchar(64) | 等待事件所属的class 名称 |
| STATE | varchar(19) | 等待事件的状态 |
| WAIT_TIME_MICRO | bigint(20) | 该等待事件所等待的时间 |
| TOTAL_WAIT_TIME_MICRO | bigint(20) | 执行过程所有等待的总时间 |
| TOTAL_WAITS | bigint(20) | 执行过程总等待的次数 |
| RPC_COUNT | bigint(20) | 发送rpc个数 |
| PLAN_TYPE | bigint(20) | 执行计划类型 |
| IS_INNER_SQL | tinyint(4) | 是否内部sql请求 |
| IS_EXECUTOR_RPC | tinyint(4) | 当前请求是否rpc请求 |
| IS_HIT_PLAN | tinyint(4) | 是否命中plan_cache |
| REQUEST_TIME | bigint(20) | 开始执行时间点 |
| ELAPSED_TIME | bigint(20) | 接收到请求到执行结束消耗 总时间 |
| NET_TIME | bigint(20) | 发送rpc到接收到请求时间 |
| NET_WAIT_TIME | bigint(20) | 接收到请求到进入队列时间 |
| QUEUE_TIME | bigint(20) | 请求在队列等待事件 |
| DECODE_TIME | bigint(20) | 出队列后decode时间 |
| GET_PLAN_TIME | bigint(20) | 开始process到获得plan时间 |
| EXECUTE_TIME | bigint(20) | plan执行消耗时间 |
| APPLICATION_WAIT_TIME | bigint(20) unsigned | 所有application类事件的总时间 |
| CONCURRENCY_WAIT_TIME | bigint(20) unsigned | 所有concurrency类事件的总时间 |
| USER_IO_WAIT_TIME | bigint(20) unsigned | 所有user_io类事件的总时间 |
| SCHEDULE_TIME | bigint(20) unsigned | 所有schedule类事件的时间 |
| ROW_CACHE_HIT | bigint(20) | 行缓存命中次数 |
| BLOOM_FILTER_CACHE_HIT | bigint(20) | bloom filter缓存命中次数 |
| BLOCK_CACHE_HIT | bigint(20) | 块缓存命中次数 |
| BLOCK_INDEX_CACHE_HIT | bigint(20) | 块索引缓存命中次数 |
| DISK_READS | bigint(20) | 物理读次数 |
| EXECUTION_ID | bigint(20) | 执行ID |
| SESSION_ID | bigint(20) | session id |
| RETRY_CNT | bigint(20) | 重试次数 |
| TABLE_SCAN | tinyint(4) | 判断该请求是否含全表扫描 |
| CONSISTENCY_LEVEL | bigint(20) | 一致性级别 |
| MEMSTORE_READ_ROW_COUNT | bigint(20) | MEMSTORE中的读行数 |
| SSSTORE_READ_ROW_COUNT | bigint(20) | SSSTORE中读的行数 |
| REQUEST_MEMORY_USED | bigint(20) | 该请求消耗的内存 |

  • 一些重要的事件间隔

3. 基于sql_audit的诊断case

3.1. 最近100s某个租户的TOP SQL耗时监控

  • 检查语句:

    -- OceanBase 4.0.0.0及以上版本,请替换tenant_name的值为实际的租户名
    select /+read_consistency(weak),query_timeout(100000000)/ SQL_ID,count(1),avg(ELAPSED_TIME),avg(EXECUTE_TIME),avg(QUEUE_TIME),avg(AFFECTED_ROWS),avg(GET_PLAN_TIME)
    from gv$ob_sql_audit
    where time_to_usec(now(6))-request_time <1000000000
    and tenant_name='test_tenant'
    group by SQL_ID order by avg(ELAPSED_TIME)*count(1) desc limit 20;

    -- OceanBase 4.0.0.0以下版本,请替换tenant_name的值为实际的租户名
    select /+read_consistency(weak),query_timeout(100000000)/ SQL_ID,count(1),avg(ELAPSED_TIME),avg(EXECUTE_TIME),avg(QUEUE_TIME),avg(AFFECTED_ROWS),avg(GET_PLAN_TIME)
    from gv$sql_audit
    where time_to_usec(now(6))-request_time <1000000000
    and tenant_name='test_tenant'
    group by SQL_ID order by avg(ELAPSED_TIME)*count(1) desc limit 20 ;

  • 期望值: 观察SQL 整体耗时,cpu_time, 物理读及逻辑消耗是否合理,一般单行insert 和 主键查询 500us以内

  • 对应建议:通过SQL语义与表结构比对,确认执行计划是否合理,耗时是否正常

3.2. 查看集群中 SQL 请求流量是否均匀

  • **思路:**我们首先可以查出某个时间段内数据库中所有 SQL 并按照 server 级别进行聚合,再统计该时间段内每台机器上的 QPS。

  • 语句:

    -- OceanBase 4.0.0.0及以上版本,请替换t1.tenant_id的值为实际租户的值
    select t2.zone, t1.svr_ip, count(*) as QPS
    from oceanbase.gv$ob_sql_audit t1, oceanbase.__all_server t2
    where t1.svr_ip = t2.svr_ip and t1.tenant_id = 1001
    and IS_EXECUTOR_RPC = 0 and request_time > (time_to_usec(now()) - 1000000)
    and request_time < time_to_usec(now())
    group by t1.svr_ip order by QPS;

    -- OceanBase 4.0.0.0以下版本,请替换t1.tenant_id的值为实际租户的值
    select t2.zone, t1.svr_ip, count(*) as QPS
    from oceanbase.gv$ob_sql_audit t1, oceanbase.__all_server t2
    where t1.svr_ip = t2.svr_ip and t1.tenant_id = 1001
    and IS_EXECUTOR_RPC = 0 and request_time > (time_to_usec(now()) - 1000000)
    and request_time < time_to_usec(now())
    group by t1.svr_ip order by QPS;

3.3. 某个时间段请求次数排在 TOP-N 的 SQL

  • **思路:**我们首先可以查出某个时间段内数据库中所有 SQL 并按照 sql_id 级别进行聚合,再统计该时间段内每个SQL_ID的 QPS,取出top值。

  • 语句:

    -- OceanBase 4.0.0.0及以上版本,请替换tenant_id的值为实际租户的值
    select SQL_ID, count(*) as QPS, avg(t1.elapsed_time) RT
    from oceanbase.gv$ob_sql_audit t1
    where tenant_id = 1001 and IS_EXECUTOR_RPC = 0
    and request_time > (time_to_usec(now()) - 10000000)
    and request_time < time_to_usec(now())
    group by t1.sql_id order by QPS desc limit 10;

    -- OceanBase 4.0.0.0以下版本,请替换tenant_id的值为实际租户的值
    select SQL_ID, count(*) as QPS, avg(t1.elapsed_time) RT
    from oceanbase.gv$sql_audit t1
    where tenant_id = 1001 and IS_EXECUTOR_RPC = 0
    and request_time > (time_to_usec(now()) - 10000000)
    and request_time < time_to_usec(now())
    group by t1.sql_id order by QPS desc limit 10;

3.4. 定位所有SQL中消耗CPU最多的sql

**思路:**消耗CPU的时间是elapsed_time - queue_time,因为queue_time的过程中是在排队,并不消耗cpu. 排查消耗CPU最多的sql在cpu飙高的场景非常有用

语句:

-- OceanBase 4.0.0.0及以上版本,请替换tenant_id的值为实际租户的值
select sql_id, substr(query_sql, 1, 20) as query_sql, 
      sum(elapsed_time - queue_time) sum_t, count(*) cnt, 
      avg(get_plan_time), avg(execute_time)    
from oceanbase.gv$ob_sql_audit     
where   tenant_id = 1001      
       and request_time > (time_to_usec(now()) - 10000000)     
       and request_time < time_to_usec(now())
group by sql_id order by sum_t desc   limit 10;



-- OceanBase 4.0.0.0以下版本,请替换tenant_id的值为实际租户的值
select sql_id, substr(query_sql, 1, 20) as query_sql, 
      sum(elapsed_time - queue_time) sum_t, count(*) cnt, 
      avg(get_plan_time), avg(execute_time)    
from oceanbase.gv$sql_audit     
where   tenant_id = 1001      
       and request_time > (time_to_usec(now()) - 10000000)     
       and request_time < time_to_usec(now())
group by sql_id order by sum_t desc   limit 10;

3.5. 查看SQL的执行是否出现大量请求不合理的使用了远程执行

**思路:**sql_audit的PLAN_TYPE字段可以看到该SQL的执行计划类型,

  • plan_type=1 :本地执行计划。性能最好。
  • plan_type=2 : 远程执行计划。
  • plan_type=3 : 分布式执行计划。包含本地执行计划和远程执行计划。

一般情况下,如果出现远程执行比较多时可能时出现切主或proxy客户端路由不准的情况。

语句:

-- OceanBase 4.0.0.0及以上版本,请替换tenant_id的值为实际租户的值
select count(*), plan_type    
from oceanbase.gv$ob_sql_audit     
where tenant_id = 1001          
      and IS_EXECUTOR_RPC = 0          
      and request_time > (time_to_usec(now()) - 10000000)         
      and request_time < time_to_usec(now()) 
group by plan_type ;


-- OceanBase 4.0.0.0以下版本,请替换tenant_id的值为实际租户的值
select count(*), plan_type    
from oceanbase.gv$sql_audit     
where tenant_id = 1001          
      and IS_EXECUTOR_RPC = 0          
      and request_time > (time_to_usec(now()) - 10000000)         
      and request_time < time_to_usec(now()) 
group by plan_type ;

3.6. 查询全表扫描的SQL

**思路:**sql_audit的TABLE_SCAN字段是标识语句是否走了全表扫描,=1 表示全表扫描了。可以进一步分析一下SQL是否可以添加索引来防止全表扫描:

语句:

-- OceanBase 4.0.0.0及以上版本,请替换tenant_id的值为实际租户的值
select query_sql 
from oceanbase.gv$ob_sql_audit 
where table_scan = 1 and tenant_id = 1001 
group by sql_id;

-- OceanBase 4.0.0.0以下版本,请替换tenant_id的值为实际租户的值
select query_sql 
from oceanbase.gv$sql_audit 
where table_scan = 1 and tenant_id = 1001 
group by sql_id;

3.7 如何分析RT突然抖动的SQL?

在线上如果出现RT抖动,但RT并不是持续很高的情况,可以考虑在抖动出现后,立刻将sql audit关闭(alter system set ob_enable_sql_audit = 0),从而确保该抖动的SQL请求在sql audit中存在;然后通过3.3章节的【某个时间段请求次数排在 TOP-N 的 SQL】,分析有异常的SQL。

如果在sql_audit中找到了对应的RT异常请求,则可以分析该请求在sql audit中记录:

  • 查看retry次数是否很多(RETRY_CNT, 如果次数很多,则是否考虑是否有锁冲突或切主等情况)
  • 查看queue time是不是很大(QUEUE_TIME字段)
  • 查看获取执行计划时间(GET_PLAN_TIME), 如果时间很长,一般会伴随IS_HIT_PLAN = 0, 表示没有命中plan cache)
  • 查看EXECUTE_TIME是否很长,如果很长,则

a. 查看是否有很长等待事件耗时

b. 查看访问的行数是否很多, 看SSSTORE_READ_ROW_COUNT, MEMSTORE_READ_ROW_COUNT两个字段, 比如大小账号场景可能导致rt抖动。

|-----|------------------------------------------|
| 第一篇 | "神医"的修炼秘籍------《OceanBase诊断系列》之一 |
| 第二篇 | 一起走进sql_audit性能视图------《OceanBase诊断系列》之二 |

相关推荐
OceanBase数据库官方博客1 天前
半连接转内连接 | OceanBase SQL 查询改写
sql·oceanbase·分布式数据库
OceanBase数据库官方博客2 天前
解析在OceanBase创建分区的常见问题|OceanBase 用户问题精粹
oceanbase·分布式数据库·分区
OceanBase数据库官方博客2 天前
半连接转内连接规则的原理与代码解析 |OceanBase查询优化
sql·oceanbase·分布式数据库
IT培训中心-竺老师4 天前
OceanBase 数据库分布式与集中式 能力
数据库·分布式·oceanbase
靖顺5 天前
【OceanBase 诊断调优】—— OceanBase 数据库网络速率配置方案
网络·数据库·oceanbase
尚雷558012 天前
OceanBase 社区版 4.0 离线方式升级bp1至bp2 指南(含避坑总结)
oceanbase
五月高高12 天前
Linux部署oceanbase
linux·oceanbase
靖顺16 天前
【OceanBase 诊断调优】—— 统计信息自动收集超时导致的估行不准 SQL 选择错索引
数据库·sql·oceanbase
it界的哈士奇16 天前
Oceanbase离线集群部署
oceanbase