当前位置: 首页 > news >正文

【SQL优化案例】SQL执行频率问题与优化效果预期

案例一:SQL执行频率太高 - 业务逻辑

1. 问题 SQL

有三个sql执行次数太高,8M的表一天能有 110TB 的逻辑读,猜测应该在一个业务逻辑

涉及表:JUDGE_TASK (8M)、JUDGE_TASK_HIS (归档表,52M)

status 字段:0-未处理,1-处理成功,2-处理中,9-处理失败

SQL 及执行情况:

1)85m3gxkjzaqkt

SELECT INST_ID,DTYPE,STATUS,NODEID,ERROR_MSG,CREATEDATE,EXECUTEDATE FROM JUDGE_TASK  WHERE STATUS = '0' AND ROWNUM <= 500 ORDER BY CREATEDATE;
  • 业务逻辑:查询 JUDGE_TASK 任务表 中还 未处理 的任务,返回500行按创建时间排序
  • 执行频率:每30分钟执行 126,508 次,≈ 每秒 70 次,早晚都是 TOP SQL
  • 平均逻辑读:1000 块次,一天产生逻辑读:≈ 58.7 亿块次 ≈ 43.73 TB

2)dnbf276hzw04k

INSERT INTO JUDGE_TASK_HIS(INST_ID,DTYPE,STATUS,NODEID,ERRMSG,CREATEDATE,EXECUTEDATE) SELECT INST_ID,DTYPE,STATUS,NODEID,ERROR_MSG,CREATEDATE,EXECUTEDATE FROM JUDGE_TASK  WHERE STATUS='1' OR STATUS='9';
  • 业务逻辑: 将 JUDGE_TASK 任务 处理成功 和 处理失败 的记录插入到 JUDGE_TASK_HIS 表(归档)
  • 执行频率:每30分钟执行 126,599 次,≈ 每秒 70 次,早晚都是 TOP SQL
  • 平均逻辑读:1000 块次,一天产生逻辑读:≈ 58.7 亿块次 ≈ 43.73 TB

3)cyypmad5n2b1m

DELETE FROM JUDGE_TASK  WHERE STATUS='1' OR STATUS='9';
  • 业务逻辑:删除已处理任务(成功+失败,已归档)
  • 执行频率:每30分钟执行 125,046 次,≈ 每秒 70 次
  • 平均逻辑读:1000 块次,一天产生逻辑读:≈ 37.18 亿块次 ≈ 27.7 TB

每个月只处理几千条任务,但是三条 SQL 加起来,单表每天逻辑读 110TB+,远超正常业务规模。并且JUDGE_TASK 15天仅有 60条 insert,每天需要执行六百万次,查询/归档/删除过于频繁,属于 业务逻辑设计不合理

SQL>  select status,count(*) from PANDA.JUDGE_TASK group by status order by count(*) desc;no rows selectedSQL>  select * from PANDA.JUDGE_TASK;no rows selectedSQL> @seg PANDA.JUDGE_TASKSEG_MB OWNER                SEGMENT_NAME                   SEG_PART_NAME                  SEGMENT_TYPE         SEG_TABLESPACE_NAME                BLOCKS     HDRFIL     HDRBLK
---------- -------------------- ------------------------------ ------------------------------ -------------------- ------------------------------ ---------- ---------- ----------8 PANDA                 JUDGE_TASK                                          TABLE                DATA999                              1024        544    138560952 PANDA                 JUDGE_TASK_HIS                                      TABLE                DATA999                              6656        544    18745692 rows selected.-- 历史记录
SQL> select count(*) from PANDA.JUDGE_TASK_HIS;COUNT(*)
----------667816
-- 估算每月任务量
SELECT TO_CHAR(TRUNC(EXECUTEDATE, 'MM'), 'YYYY-MM') AS month,COUNT(*) AS cntFROM PANDA.JUDGE_TASKGROUP BY TRUNC(EXECUTEDATE, 'MM')ORDER BY month;MONTH	CNT
1	2018-09	541
2	2018-10	1417
3	2018-11	7414
4	2018-12	5875
5	2019-01	4118
......
74	2024-10	5059
75	2024-11	6235
76	2024-12	8751
77	2025-01	10422
78	2025-02	10459
79	2025-03	1096
80	2025-04	2199
81	2025-05	3490
82	2025-06	4910
83	2025-07	2480-- 根据 sql_id 估算一天产生的逻辑读
SELECT AVG(BUFFER_GETS) * 24 / 10 ,ROUND((AVG(BUFFER_GETS) * 24 / 10 * 8192)/ POWER(1024, 4), 2 ) AS LOGICAL_READ_TB_24H
FROM DBMT.GETS_STAT_HIST WHERESNAP_TIME <= TO_DATE('20250723', 'yyyymmdd')--AND INSTANCE_NUMBER = 1AND sql_id = '85m3gxkjzaqkt';PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  dnbf276hzw04k, child number 0
-------------------------------------
INSERT INTO JUDGE_TASK_HIS(INST_ID,DTYPE,STATUS,NODEID,E
RRMSG,CREATEDATE,EXECUTEDATE) SELECT
INST_ID,DTYPE,STATUS,NODEID,ERROR_MSG,CREATEDATE,EXECUTEDATE FROM
JUDGE_TASK  WHERE STATUS='1' OR  STATUS='9'Plan hash value: 3244728187-----------------------------------------------------------------------------------------------------
| Id  | Operation                | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |                          |       |       |   223 (100)|          |
|   1 |  LOAD TABLE CONVENTIONAL | JUDGE_TASK_HIS 			|       |       |            |          |
|*  2 |   TABLE ACCESS FULL      | JUDGE_TASK    		    |   822 |   183K|   223   (1)| 00:00:01 |
-----------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - filter(("STATUS"='1' OR "STATUS"='9'))22 rows selected.-- 索引信息
TABLE           TABLE           Index                                  COLUMN                     Col
OWNER           NAME            Name                       UCPTDVS NAME                       Pos DESC
--------------- -------------   -------------------------- ------- ------------------------- ---- ----
PANDA           JUDGE_TASK      IDX_INST_ID_STATUS          NNNNNVY INST_ID                   1 ASCNNNNNVY STATUS                    2 ASCJUDGE_TASK_HIS  IDX_INST_ID_STATUS_HIS      NNNNNVY INST_ID                   1 ASCNNNNNVY STATUS                    2 ASCPLAN                     GET     DISK    WRITE              ROWS      ROWS USER_IO(MS)         ELA(MS)         CPU(MS) CLUSTER(MS)    PLSQL
END_TI I NAME               HASH VALUE EXEC           PRE EXEC PRE EXEC PER EXEC ROW_P    PRE EXEC PRE FETCH    PER EXEC        PRE EXEC        PRE EXEC    PER EXEC PER EXEC
------ - --------------- ------------- ---------- ------------ -------- -------- ----- ----------- --------- ----------- --------------- --------------- ----------- --------
26 11  1 PANDA               3244728187 11.W              1,009        0        0 0               0         0           0           2,230           1,410           0        0
26 11  1 PANDA               3244728187 11.W              1,009        0        0 0               0         0           0           2,237           1,413           0        0
26 12  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,164           1,328           0        0
26 12  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,155           1,335           0        0
26 13  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           1,961           1,118           0        0
26 13  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,136           1,317           0        0
26 14  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,055           1,226           0        0
26 14  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,095           1,252           0        0
26 15  1 PANDA               3244728187 12.W              1,009        0        0 0               0         0           0           2,072           1,228           0        0
26 15  1 PANDA               3244728187 11.W              1,009        0        0 0               0         0           0           2,220           1,397           0        0
......

2. 优化实施

(1)业务逻辑优化(首选方案)

  • 降低执行频率:建议 30s~1min 执行一次任务处理或者重构一下将相关SQL模块封装出去,而不是毫秒级频繁执行。
  • 合理批量处理:判断任务优先级,如果不要求实时,可以批量归档/删除,而非频繁小批量。

猜测业务逻辑如下:

检查status='0'是否有任务未处理-> 处理任务 -> 处理成功和失败的(STATUS='1' OR STATUS='9')任务归档在JUDGE_TASK_HIS->删除JUDGE_TASK任务记录(STATUS='1' OR STATUS='9')

(2)数据库层面优化(预备方案)

  • STATUS 字段建立索引(或组合索引):
CREATE INDEX PANDA.JUDGE_TASK_status ON PANDA.JUDGE_TASK(status);

访问路径优化,将目前执行计划 TABLE ACCESS FULL,建索引后预期转为 INDEX RANGE SCAN,逻辑读可由 ~658 降至几十。

3. 优化效果(预期)

很简单的一个业务每天能有110TB的逻辑读,全库每天才产生1500TB左右。但生产上还未优化,该业务不属于核心,在一个没见过的用户下,业务还在定位,预期效果逻辑读是从 110TB+/天 → <1TB/天。

案例二:SQL执行频率太高 - 4M 表进AWR TOP SQL

1. 问题 SQL

PANDA.ZONE_LIST 仅 13 行数据,核心表,频繁访问。每分钟执行9122次,每次执行0.17ms,逻辑读70块次,一天概产生读13.18TB数据。

SELECT T.ZONE FROM ZONE_LIST T  WHERE T.PARTITION = :1;-- 测试
SQL> SELECT T.ZONE FROM ZONE_LIST T  WHERE T.PARTITION = 'G';Elapsed: 00:00:00.00Execution Plan
----------------------------------------------------------
Plan hash value: 3167746917---------------------------------------------------------------------------------------
| Id  | Operation         | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                   |     1 |     6 |    28   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| ZONE_LIST 		  |     1 |     6 |    28   (0)| 00:00:01 |
---------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("T"."PARTITION"='G')Statistics
----------------------------------------------------------0  recursive calls0  db block gets70  consistent gets0  physical reads0  redo size549  bytes sent via SQL*Net to client421  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed 
2. 优化实施

(1)索引优化--(数据库层优化建议)

为查询条件建立唯一索引 (PARTITION, ZONE)优化前执行计划走TABLE ACCESS FULL,Consistent gets = 70,优化后执行计划走INDEX RANGE SCAN UN_ZONE_LIST_PARTGConsistent gets = 1 逻辑读从 70 → 1,降幅 98.5%

--- 创建索引
create UNIQUE index PANDA.UN_ZONE_LIST_PARTG on PANDA.ZONE_LIST(PARTITION,ZONE);SQL> SELECT T.ZONE FROM ZONE_LIST T  WHERE T.PARTITION = 'G';Elapsed: 00:00:00.01Execution Plan
----------------------------------------------------------
Plan hash value: 586154005-----------------------------------------------------------------------------------------
| Id  | Operation        | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                      |     1 |     6 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| UN_ZONE_LIST_PARTG |     1 |     6 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - access("T"."PARTITION"='G')Statistics
----------------------------------------------------------0  recursive calls0  db block gets1  consistent gets0  physical reads0  redo size549  bytes sent via SQL*Net to client421  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed

(2)应用层面优化建议

现在主要矛盾是应用层频繁请求数据库,应用层-数据库层面有一个东西可以解决这一问题-中间件缓存,对于热点数据,可以放入缓存中间件 比如 Redis,避免频繁访问 DB。案例二是一个只有select的表,比较好修改,案例一也可以引用这种方法,但会涉及到缓存一致性问题,一致性越强代码越不好改动。

但其实除了应用层,Oracle也提供了两种方法,一个是KEEP Buffer Pool 专为“热点小表”准备的一块独立内存区,启用后这些表的数据块不再被 LRU 算法换出,从而避免反复磁盘 I/O,提高访问速度。上面方式可以降低物理读,对于热点表逻辑读可以通过Oracle11g的一种新特性Result Cache,可以用来缓存SQL Query Result Cache:存储 SQL 查询的结果集和PL/SQL Function Result Cache:用于存储 PL/SQL 函数的结果集。

配合result_cache_max_resultresult_cache_mode参数/*+ result_cache */hint可直接访问结果集不需要再查询这个sql,比如案例二可将逻辑读降为0。

3. 优化效果(预期)

单靠建索引即可将 每天 I/O 量由 13.18TB → <1TB;若再结合缓存/队列,能进一步大幅降低数据库压力。

指标

优化前

优化后

降幅

平均逻辑读

70 blocks

1 block

-98.5%

每天逻辑读量

≈ 13.18 TB

≈ 0.986 TB

-92.5%

平均响应耗时

0.17 ms

0.01 ms

提升17倍

执行频率影响

高频仍然存在

可通过缓存/队列进一步优化

-

SQL优化效果预期

上面说一个4M的表的SQL进AWR TOP SQL,一天概产生读13.18TB数据,一个8M的表和一个简单任务的处理逻辑3个SQL一天产生110TB 的逻辑读,还有前面几篇讲到的

【SQL优化案例】Oracle统计信息缺失 - 墨天轮

【SQL优化案例】索引问题 - 墨天轮

【SQL优化案例】SQL改写 - 用 UNION ALL 替代 OR - 墨天轮

【SQL优化案例】表结构与数据分布问题 - 墨天轮

这些案例都是很常见的优化案例,相较于慢SQL,TOP SQL优化起来并不困难,但是在实施之后怎么观察预期效果呢?

除了单个SQL实施之后的执行时间、逻辑读大小,常见的指标有CPU使用率、操作系统负载,AAS,DB CPU、DB TIME等。

在实施40+SQL左右,可以观察到实例每天产生的逻辑读降了30%-50%。

CPU和操作系统负载下降幅度也很明显

实例一

实例二

http://www.dtcms.com/a/353183.html

相关文章:

  • NumPy/PyTorch/C char数组内存排布
  • 网站防爆破安全策略分析
  • python项目开发:创建虚拟环境
  • 利用机器学习优化Backtrader策略原理与实践
  • 深入解析函数栈帧创建与销毁
  • 斯塔克工业技术日志:用基础模型打造 “战甲级” 结构化 AI 功能
  • 预测模型及超参数:1.传统机器学习:SVR与KNN
  • 网页版云手机怎么样
  • Enduro 克隆游戏 — 基于 HTML、CSS 与 JavaScript 的完整教程模板
  • 23种设计模式——单例模式(Singleton)​详解
  • 金仓数据库文档系统全面升级:用户体验焕然一新
  • CPU、IO、网络与内核参数调优
  • Linux 性能调优实战:CPU、磁盘 I/O、网络与内核参数
  • 系统架构设计师备考第8天——嵌入式系统
  • 工业网络安全:保护制造系统和数据
  • Linux 系统CPU-IO-网络-内核参数的调优
  • 【学习笔记】GB 42250-2022标准解析
  • 手写MyBatis第36弹:MyBatis执行流程中SQL命令类型解析
  • Effective c++ 35条款详解
  • docker run 后报错/bin/bash: /bin/bash: cannot execute binary file总结
  • Python计算点云的欧式、马氏、最近邻、平均、倒角距离(Chamfer Distance)
  • iOS技术之通过Charles抓包http、https数据
  • 【开题答辩全过程】以Trlig(服装网站)为例,包含答辩的问题和答案
  • ETH PPS 配置链路
  • 车载诊断架构 --- 基于整车功能的正向诊断需求开发
  • Ruoyi-cloud 微服务部署双方案:本地与 K8S 实践手册
  • FastAPI + SQLModel 从 0 搭到完整 CRUD
  • 腾讯云人脸库技术架构深度解析
  • Github 3k+ star,中后台管理系统框架,支持多款 UI 组件库,兼容PC、移动端!比商业系统还专业!!
  • IntelliJ IDEA Debug 模式功能指南