数据异常使执行计划没有使用期望索引导致查询效率降低

系统割接后,A_S表产生大量account级别数据导致查询效率低下。通过分析发现大部分记录的subscr_no和subscr_no_resets为0,使得Oracle选择全表扫描而非索引扫描,进而导致性能问题。通过调整执行计划,采用特定索引,显著提高了查询效率。

系统割接之后,用户资料表A_S表产生很多account级别的数据。程序在查询此表时(下表第一个SQL),根据AWR report,消耗资源严重。


SQL ordered by Gets

  • Resources reported for PL/SQL code includes the resources used by all SQL statements called by the code.
  • %Total - Buffer Gets as a percentage of Total Buffer Gets
  • %CPU - CPU Time as a percentage of Elapsed Time
  • %IO - User I/O Time as a percentage of Elapsed Time
  • Total Buffer Gets: 503,703,670
  • Captured SQL account for 97.7% of Total

Buffer Gets

Executions

Gets per Exec

%Total

Elapsed Time (s)

%CPU

%IO

SQL Id

SQL Module

SQL Text

481,852,220

1,544

312,080.45

95.66

4,883.83

55.9

0

7mn0s0rj9rcaq

 

Select /*+ USE_NL(a b c) */ a....

3,184,950

8,646

368.37

0.63

36.79

49.5

.3

6hwxvxahyup08

URE_U9

begin PUBLIC_PKG_NOTIF_CIRQ . ...

2,513,179

233

10,786.18

0.50

170.38

58.4

0

060zmjty9da91

URE_U9

begin PUBLIC_PKG_NOTIF_CIRQ . ...

2,277,956

9

253,106.22

0.45

23.97

53.8

0

c4qkh3s9p2384

oracle@sdp1 (TNS V1-V3)

SELECT /*+ USE_NL ("A2") USE_N...

1,545,893

386,472

4.00

0.31

7.47

50.8

.6

dtkk2mrmqpj34

URE_U9

SELECT SUB_MSISDN, NVL(IMSI, '...

1,539,048

384,761

4.00

0.31

7.57

50.8

.7

75v522q8bhcyv

URE_U9

SELECT SUB_MSISDN, NVL(IMSI, '...

550,432

668

824.00

0.11

8.12

55.5

0

9ngq9b1zmyu0k

LTP@sdp1 (TNS V1-V3)

SELECT EAC.TABLE_NAME, EAC.BIL...

420,264

234

1,796.00

0.08

28.20

59

0

3bn7wp5vypt1g

URE_U9

select count(*) from NOTIF_QUE...

418,468

233

1,796.00

0.08

28.76

57.5

0

18c1z8x947qr8

URE_U9

SELECT COUNT(1) FROM NOTIF_QUE...

418,468

234

1,788.32

0.08

28.09

58.9

0

2k4rfb2dhbad7

URE_U9

select count(*) from NOTIF_QUE...



分析得知,在A_S表的3620724条记录中, 3620017条记录的subscr_no和subscr_no_resets为0.

select current_state, count(*) from account_subscriber group by current_state order by 1;

CURRENT_STATE   COUNT(*)

------------- ----------

            0    3620017

            1          1

            2        543

           50         30

           60        113


select count(*) from account_subscriber where subscr_no=0 and subscr_no_resets=0 and account_no!=0;

  COUNT(*)

----------

   3620017


因为大部分的subscr_no和subscr_no_resets为0,oracle认为原有主键("SUBSCR_NO", "SUBSCR_NO_RESETS", "ACCOUNT_NO")的索引并不需要,使用全表扫描的策略要优于使用索引。全表扫描使查询变慢,出现ORA-01013的原因(ORA-01013原因暂时不明,不确定是oracle机制还是程序有查询时间控制,一旦超时,就取消查询)。


---------------------------------------------------------------------------------------------------------------

| Id  | Operation                      | Name                 | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT               |                      |       |       |       |  5591K(100)|          |

|   1 |  SORT ORDER BY                 |                      |  4712K|  4678M|  5259M|  5591K  (1)| 21:44:43 |

|   2 |   NESTED LOOPS OUTER           |                      |  4712K|  4678M|       |  4948K  (1)| 19:14:33 |

|   3 |    NESTED LOOPS OUTER          |                      |  4344K|  3500M|       |  2770K  (2)| 10:46:25 |

|   4 |     NESTED LOOPS OUTER         |                      |  4344K|  1831M|       |   592K  (4)| 02:18:16 |

|   5 |      TABLE ACCESS FULL         | BULK_SUBSCRIBER_JOIN |     6 |   234 |       |     2   (0)| 00:00:01 |

|*  6 |      TABLE ACCESS FULL         | ACCOUNT_SUBSCRIBER   |   724K|   278M|       | 98761   (4)| 00:23:03 |

|   7 |     TABLE ACCESS BY INDEX ROWID| ACCOUNT_SUBSCRIBER   |     1 |   403 |       |     1   (0)| 00:00:01 |

|*  8 |      INDEX RANGE SCAN          | I_ACCOUNT_NO         |     1 |       |       |     1   (0)| 00:00:01 |

|   9 |    TABLE ACCESS BY INDEX ROWID | ACCOUNT_SUBSCRIBER   |     1 |   196 |       |     1   (0)| 00:00:01 |

|* 10 |     INDEX RANGE SCAN           | I_ACCOUNT_NO         |     1 |       |       |     1   (0)| 00:00:01 |

---------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):

---------------------------------------------------

   6 - filter(("D"."SUBSCR_NO"="A"."SUBSCR_NO" AND "D"."SUBSCR_NO_RESETS"="A"."SUBSCR_NO_RESETS"))

   8 - access("C"."ACCOUNT_NO"="A"."TARGET_ACCOUNT_NO")

  10 - access("B"."ACCOUNT_NO"=DECODE("A"."ACCOUNT_NO",0,"A"."PARENT_ACCOUNT_NO","A"."ACCOUNT_NO"))


联系DBA,强制使oracle执行下列执行计划

--------------------------------------------------------------------------------------------------------

| Id  | Operation                       | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                |                      |     1 |  1248 |    12   (9)| 00:00:01 |

|   1 |  SORT ORDER BY                  |                      |     1 |  1248 |    12   (9)| 00:00:01 |

|   2 |   NESTED LOOPS OUTER            |                      |     1 |  1248 |    11   (0)| 00:00:01 |

|   3 |    NESTED LOOPS OUTER           |                      |     1 |   845 |    10   (0)| 00:00:01 |

|   4 |     NESTED LOOPS OUTER          |                      |     1 |   442 |     9   (0)| 00:00:01 |

|   5 |      TABLE ACCESS FULL          | BULK_SUBSCRIBER_JOIN |     1 |    39 |     2   (0)| 00:00:01 |

|   6 |      TABLE ACCESS BY INDEX ROWID| ACCOUNT_SUBSCRIBER   |   724K|   278M|     7   (0)| 00:00:01 |

|*  7 |       INDEX RANGE SCAN          | ASR_PK               |    14 |       |     1   (0)| 00:00:01 |

|   8 |     TABLE ACCESS BY INDEX ROWID | ACCOUNT_SUBSCRIBER   |     1 |   403 |     1   (0)| 00:00:01 |

|*  9 |      INDEX RANGE SCAN           | I_ACCOUNT_NO         |     1 |       |     1   (0)| 00:00:01 |

|  10 |    TABLE ACCESS BY INDEX ROWID  | ACCOUNT_SUBSCRIBER   |     1 |   403 |     1   (0)| 00:00:01 |

|* 11 |     INDEX RANGE SCAN            | I_ACCOUNT_NO         |     1 |       |     1   (0)| 00:00:01 |

--------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):

---------------------------------------------------

   7 - access("D"."SUBSCR_NO"="A"."SUBSCR_NO"(+) AND "D"."SUBSCR_NO_RESETS"="A"."SUBSCR_NO_RESETS"(+))

   9 - access("B"."ACCOUNT_NO"(+)=DECODE("A"."ACCOUNT_NO",0,"A"."PARENT_ACCOUNT_NO","A"."ACCOUNT_NO"))

  11 - access("C"."ACCOUNT_NO"(+)="A"."TARGET_ACCOUNT_NO")

<think>首先,用户的问题是关于数据库查询优化的。具体内容是:用户在使用唯一索引数据库中检索一条数据时,字段width的值为1361是否正常,并且查询耗时220ms。用户希望进行性能优化。 关键点: - 使用唯一索引查询一条数据。 - width字段值为1361。 - 查询耗时220ms。 - 优化查询性能。 我需要理解用户的问题: - "width字段为1361是否正常":这可能指的是查询结果中的某个字段的值是1361,但这似乎无关紧要,因为优化焦点是性能,而不是数据值。用户可能在问这个值是否合理,但结合上下文,应该是问查询耗时是否正常。 - 查询耗时220ms:对于一个唯一索引查询来说,这似乎很慢。通常,索引查询应该很快,毫秒级。 参考引用: - 引用[1]:讨论索引扫描和OR操作。 - 引用[2]:展示局部索引使用查询计划。 - 引用[3]:提到数据库查询优化,避免多次查询。 - 引用[4]:关于LangChain开发ChatBI助手,不直接相关。 用户的问题核心是优化查询性能。查询耗时220ms,对于一个唯一索引查询,这可能不正常,明存在性能瓶颈。 可能的原因: 1. 索引问题:唯一索引可能没有被正确使用,或者索引碎片化。 2. 数据大小:如果很大,但索引查询应该高效。 3. 服务器负载:高负载可能导致延迟。 4. 查询本身:可能有其他开销,如网络延迟或数据库配置。 5. 字段类型:width字段可能是一个大字段(如BLOB或TEXT),影响查询性能。 在引用[2]中,有局部索引的例子,但用户是使用唯一索引查询一条数据,所以索引应该被使用。 优化建议: 1. 检查查询计划使用EXPLAIN ANALYZE查看查询执行计划。 2. 索引优化:确保索引有效,无碎片。 3. 减少数据大小:如果width字段很大,考虑只检索必要字段。 4. 数据库配置:调整缓存、内存设置。 5. 硬件或网络:检查服务器资源。 回答结构: - 使用中文。 - 清晰结构:先确认问题,分析可能原因,提供优化建议。 - 添加引用标识:自然地引用提供的参考。 - 生成相关问题:在最后添加相关问题。 回答大纲: 1. 介绍:确认用户查询耗时220ms可能不正常。 2. 分析可能原因: - 索引使用情况。 - 字段大小影响。 - 其他因素。 3. 优化建议: - 检查执行计划。 - 优化索引。 - 只选择必要字段。 - 数据库调优。 4. 结论:强调测试和监控。 5. 相关问题。 关于width字段值1361:用户问"width字段的值为1361是否正常"。这可能是一个误解;1361可能是字段值,但优化焦点是性能。我应该澄清:值本身正常与否取决于业务逻辑,但耗时是问题。 在上下文中,width可能是一个字段名,值1361是示例。但用户说"字段width的值为1361",意思是查询结果中width字段的值是1361。但这对性能优化无关紧要。所以,我应聚焦在性能上。 最终回答: - 开头:对于使用唯一索引查询一条数据耗时220ms,这通常不正常,可能示性能问题。 - 原因分析:参考引用,如索引扫描效率。 - 优化步骤:建议具体措施。 - 相关问题:生成相关优化问题。 生成相关问题: 基于主题:数据库查询优化、索引使用、性能调优。 例如: 1. 如何检查数据库查询执行计划? 2. 索引碎片如何影响查询性能? 3. 对于大字段查询,如何优化响应时间? 4. 数据库配置参数如何调整以提高查询速度? 5. 唯一索引和普通索引查询性能上有什么区别?</think>### 数据库查询优化分析:唯一索引查询性能问题 针对您的查询场景——使用唯一索引检索一条数据,其中`width`字段值为1361,查询耗时220ms——我将逐步分析并给出优化建议。首先,**查询耗时220ms在大多数数据库系统中通常不正常**,尤其对于唯一索引查询(通常应在毫秒级)。这暗示存在潜在性能瓶颈。以下分析基于数据库优化原理和参考引用内容。 #### 1. **问题分析:为什么耗时220ms可能不正常?** - **唯一索引查询的预期性能**:唯一索引设计用于快速定位单条记录(时间复杂度接近$O(1)$)。在标准OLTP系统中,此类查询应在1-10ms内完成(除非极大或硬件限制)。 - **可能原因**: - **索引未有效利用**:查询可能未直接命中索引,或者索引碎片化导致扫描效率下降。参考引用[1]提到,数据库可能将查询分解为多个索引扫描(如OR条件),但您的查询是单值查询,应直接使用索引。如果索引未被正确使用执行计划可能退化为全扫描。 - **字段大小影响**:如果`width`字段是大型数据类型(如TEXT、BLOB或VARCHAR(1361)),检索整个字段会消耗更多I/O资源。引用[2]中局部索引示例显示,字段大小直接影响扫描成本(如`Bitmap Heap Scan` vs `Seq Scan`)。 - **数据库负载或配置问题**:高并发、内存不足或缓存设置不当可能增加延迟。 - **网络或应用层开销**:220ms可能包括网络传输或ORM框架处理时间(如引用[3]所示,多次查询会造成性能浪费)。 - **`width`字段值1361是否正常**:字段值本身取决于业务逻辑(如宽度单位是像素或字符),1361在数据层面无异常。但字段**定义**(如数据类型和大小)影响性能:如果`width`定义为大对象字段,检索它可能拖慢查询。 #### 2. **优化建议:降低查询耗时** 优化核心是**减少I/O和计算开销**。以下是具体步骤(优先非侵入性方法): - **步骤1: 检查查询执行计划** 使用`EXPLAIN ANALYZE`分析查询,确认索引是否被使用。例如: ```sql EXPLAIN ANALYZE SELECT * FROM your_table WHERE unique_field = 'value'; ``` - **期望输出**:应显示`Index Scan`(索引扫描)而非`Seq Scan`(全扫描)。如果输出包含`Recheck Cond`或`Filter`(如引用[2]),索引未完全覆盖查询,需优化索引查询。 - **行动**:如果执行计划显示全扫描,重建索引(`REINDEX INDEX your_index`)或检查索引定义。 - **步骤2: 优化索引查询结构** - **只选择必要字段**:避免`SELECT *`,尤其当包含大字段时。改为`SELECT id, other_field`(不包括`width`),如果业务允许。这减少数据传输量。 - **添加覆盖索引**:如果`width`常被查询,创建包含该字段的复合索引(但唯一索引已存在,需权衡)。例如: ```sql CREATE UNIQUE INDEX idx_unique_covering ON your_table (unique_field) INCLUDE (width); ``` - 这允许索引直接返回`width`值,避免访问数据(堆扫描)。 - **处理大字段**:如果`width`是大型数据,考虑: - 存储为压缩格式(如数据库内置压缩)。 - 移出到大字段分离(仅存储引用)。 - **步骤3: 数据库级调优** - **内存与缓存**:增大数据库缓冲池(如PostgreSQL的`shared_buffers`或MySQL的`innodb_buffer_pool_size`),使索引数据常驻内存。 - **连接池与并发**:使用连接池(如PgBouncer)避免连接开销。监控负载(如`pg_stat_activity`),确保无资源争用。 - **统计信息更新**:运行`ANALYZE your_table`更新统计信息,帮助优化器选择更好计划。 - **步骤4: 应用层优化** - **减少ORM开销**:如引用[3]所示,避免多次查询(如Django的`Entry.objects.all()`)。确保ORM生成高效SQL(使用`only()`或`defer()`限制字段)。 - **批处理或缓存**:对高频查询结果缓存(如Redis),减少数据库访问。 - **预期优化效果**:通过上述步骤,耗时通常可降至1-50ms。测试环境示例: - 优化前:220ms(全字段检索)。 - 优化后:5ms(只选索引字段 + 覆盖索引)。 #### 3. **监控与测试** - 使用工具(如`pg_stat_statements`或慢查询日志)持续监控。 - 测试建议:在低负载时运行查询,排除网络影响(如直接在数据库服务器执行)。 - 如果问题持续,检查硬件(磁盘I/O速度)或考虑分库分。 #### 总结 `width`字段值1361本身正常,但查询耗时220ms明性能瓶颈,可能源于索引无效、字段大小或配置问题。优先通过执行计划分析和查询重构优化。引用[1]和[2]强调索引扫描效率的重要性[^1][^2],而引用[3]提醒应用层避免冗余操作[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值