热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

Oracle单表查询某列最大最小值的性能问题

在Oracle10g中,有一个单表查询的SQL语句,它没有where子句,只是简单地同时求某列最大值和最小值。按照理解,它应该走全索引扫描

在Oracle 10g中,有一个单表查询的SQL语句,它没有where子句,只是简单地同时求某列最大值和最小值。按照理解,它应该走全索引扫描

在Oracle 10g中,有一个单表查询的SQL语句,它没有where子句,只是简单地同时求某列最大值和最小值。
按照理解,它应该走全索引扫描,但它却走了全表扫描。单表的数据量有点大,组成也有点复杂,LOB字段很多,索引有点多,加lob的索引一起有13个。这下性能就差很多,本来预计毫秒级别的操作变成了分钟。在其他同版本的库上,索引较少时,会走全索引扫描,但性能也不好,查询时的一致性读也很大。

SQL是这样:select max(updateid),min(updateid) from dbcenter.TABLE_NAME ;
很简单,而且updateid列上有一个唯一索引。索引也分析过,但现在执行起来却性能差的很,致命的全表扫描。


首先,使用set autotrace trace exp stat得到真实的执行计划。
SQL> set timing on
SQL> set autotrace trace exp stat
SQL> set linesize 300

-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 373K (1)| 01:14:42 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
| 2 | TABLE ACCESS FULL| TABLE_NAME | 8665K| 57M| 373K (1)| 01:14:42 |
-------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
1700621 consistent gets
1506260 physical reads
0 redo size
602 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL>

从结果中可以看到走的就是全表扫描。从统计值看,也是真正的全表扫描了,从头扫到尾巴的那种,没办法,表中这个字段的值又不是排序的,不全部扫完不知道最大最小值的。

很显然,这不是最优的结果。我认为最理想应该是走updateid列的索引,一个索引快速全扫描就行。

猜测,会不会是索引多了不知道如何选择。在select子句中是不主动选择索引的?

但是,我使用hint也没有效果,优化器依然没有选择走这个索引。


select/*+index_ffs(TABLE_NAME IDX55021287)*/ MAX(updateid), MIN(updateid) from dbcenter.TABLE_NAME;

Elapsed: 00:03:28.77

Execution Plan
----------------------------------------------------------


-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 373K (1)| 01:14:42 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
| 2 | TABLE ACCESS FULL| TABLE_NAME | 8665K| 57M| 373K (1)| 01:14:42 |
-------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
1701902 consistent gets
1497285 physical reads
0 redo size
602 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

但是,如果只查max或min时,,会走索引。

select MIN(updateid) from dbcenter.TABLE_NAME ;

Execution Plan
----------------------------------------------------------
Plan hash value: 3935799349

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 373K (1)| 01:14:42 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
| 2 | INDEX FULL SCAN (MIN/MAX)| IDX55021287 | 8665K| 57M| | |
------------------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
524 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

性能也好的很,一致性读只有3。这样的结果也很好理解。索引是唯一索引,已经排序好的,求一个最大值,肯定只要扫描索引的开始或者结束部分的数据块即可。


因此,需要分析一下这个SQL的执行计划产生的过程。我使用event 10053 trace name context forever ,level 1方法来完成这个操作。

alter system flush shared_pool;
alter session set "_optimizer_search_limit"=15;
oradebug setmypid;
oradebug event 10053 trace name context forever ,level 1;
explain plan for select max(updateid),min(updateid) from dbcenter.TABLE_NAME ;

***************************************
SINGLE TABLE ACCESS PATH
-----------------------------------------
BEGIN Single Table Cardinality Estimation
-----------------------------------------
Table: TABLE_NAME Alias: TABLE_NAME
Card: Original: 8663996 Rounded: 8663996 Computed: 8663996.00 Non Adjusted: 8663996.00
-----------------------------------------
END Single Table Cardinality Estimation
-----------------------------------------
Access Path: TableScan
Cost: 373495.00 Resp: 373495.00 Degree: 0
Cost_io: 372211.00 Cost_cpu: 18442053762
Resp_io: 372211.00 Resp_cpu: 18442053762
******** Begin index join costing ********
****** trying bitmap/domain indexes ******
Access Path: index (FullScan)
Index: IDX242025
resc_io: 25019.00 resc_cpu: 1911171307
ix_sel: 1 ix_sel_with_filters: 1
Cost: 2515.21 Resp: 2515.21 Degree: 0
Access Path: index (FullScan)
Index: IDX94341804
resc_io: 31023.00 resc_cpu: 1953914433
ix_sel: 1 ix_sel_with_filters: 1
Cost: 3115.90 Resp: 3115.90 Degree: 0
Access Path: index (FullScan)
Index: PK_TABLE_NAME
resc_io: 25217.00 resc_cpu: 1912567352
ix_sel: 1 ix_sel_with_filters: 1
Cost: 2535.02 Resp: 2535.02 Degree: 0
Access Path: index (FullScan)
Index: IDX242025
resc_io: 25019.00 resc_cpu: 1911171307
ix_sel: 1 ix_sel_with_filters: 1
Cost: 2515.21 Resp: 2515.21 Degree: 0
****** finished trying bitmap/domain indexes ******
******** End index join costing ********
Best:: AccessPath: TableScan
Cost: 373495.00 Degree: 1 Resp: 373495.00 Card: 8663996.00 Bytes: 0
***************************************

从结果看,优化器在index join costing操作时,并没有将IDX55021287索引计算进来。

即使我使用了alter session set "_optimizer_search_limit"=15;将限制值从5提升到15也没有效果。或许,index join costing操作时引入的索引数量不是这个参数控制。

最大最小值的查询操作,就不应该在SQL中一步完成,应该分步骤实现。很显然,oracle的查询重写没有那么智能,没有将其分开。即使在11g也不行,我测试过了。


推荐阅读
  • 数据仓库ETL开发过程中,如何有效进行测试是一个关键问题。由于数据仓库通常包含大量数据,并且出于安全考虑,开发库和测试库的数据与生产库不完全一致,这给测试带来了挑战。本文将探讨适用于ETL开发的测试方法,包括单元测试、集成测试以及具体测试技术。 ... [详细]
  • 周排行与月排行榜开发总结
    本文详细介绍了如何在PHP中实现周排行和月排行榜的开发,包括数据库设计、数据记录和查询方法。涉及的知识点包括MySQL的GROUP BY、WEEK和MONTH函数。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • Spring Data JdbcTemplate 入门指南
    本文将介绍如何使用 Spring JdbcTemplate 进行数据库操作,包括查询和插入数据。我们将通过一个学生表的示例来演示具体步骤。 ... [详细]
  • 解决SQL Server数据库sa登录名无法连接的问题
    在安装SQL Server数据库后,使用Windows身份验证成功,但使用SQL Server身份验证时遇到问题。本文将介绍如何通过设置sa登录名的密码、启用登录名状态以及开启TCP协议来解决这一问题。 ... [详细]
  • 在将Web服务器和MySQL服务器分离的情况下,是否需要在Web服务器上安装MySQL?如果安装了MySQL,如何解决PHP连接MySQL服务器时出现的连接失败问题? ... [详细]
  • SQL 连接详解与应用
    本文详细介绍了 SQL 连接的概念、分类及实际应用,包括内连接、外连接、自连接等,并提供了丰富的示例代码。 ... [详细]
  • 本文介绍了如何使用Flume从Linux文件系统收集日志并存储到HDFS,然后通过MapReduce清洗数据,使用Hive进行数据分析,并最终通过Sqoop将结果导出到MySQL数据库。 ... [详细]
  • 本文介绍了如何在 Spring 3.0.5 中使用 JdbcTemplate 插入数据并获取 MySQL 表中的自增主键。 ... [详细]
  • BIEE中的最终用户界面被称为Presentation Layer(展现层)。展现层呈现的内容与用户在Web报表开发界面中看到的一致,使用业务语言进行描述,隐藏了技术细节,如星型模型。本文将详细介绍展现层的设计要点及其与业务模型层的关系。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • PHP 使用 Cookie 进行访问授权的方法
    本文介绍了如何使用 PHP 和 Cookie 实现访问授权,包括表单验证、数据库查询和会话管理等关键步骤。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 操作系统如何通过进程控制块管理进程
    本文详细介绍了操作系统如何通过进程控制块(PCB)来管理和控制进程。PCB是操作系统感知进程存在的重要数据结构,包含了进程的标识符、状态、资源清单等关键信息。 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
author-avatar
黄镇郁皓伟嘉惟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有