对一条基于分区的简单SQL的优化

本文通过实验模拟了一个关于数据库SQL性能的问题,展示了在相同的数据库和表上,使用常量和变量时,即使执行计划相同,性能也可能存在显著差异。关键在于变量处理方式对分区扫描的影响。


今天遇到一个很奇怪的问题,在同一个数据库上,一条简单SQL访问同样的表,一个用常量,一个用变量,但执行计划看起来一样,返回相同的数据,性能差别很大,经过一番研究,终于发现原因。
下面通过实验模拟这个问题。

[@more@]由于我遇到问题的表是分区表,故在实验中也用分区表模拟。

drop table test purge;
create table test(id int, big_col char(2000))
partition by range(id)
(
partition p0 values less than(1),
partition p1 values less than(2),
partition p2 values less than(3),
partition p3 values less than(4),
partition p4 values less than(5),
partition p5 values less than(6),
partition p6 values less than(7),
partition p7 values less than(8),
partition p8 values less than(9),
partition p9 values less than(10)
)
;
insert into test select mod(rownum,10),'1' from dual connect by level <=10000;
commit;
analyze table test compute statistics for table;

以上SQL模拟10个分区的表,下面我们重现问题:
suk@ORA10G> alter system flush shared_pool;

系统已更改。

suk@ORA10G> var v number
suk@ORA10G> exec :v:=9

suk@ORA10G> select count(1) from test where id>1 and id>:v;


执行计划
----------------------------------------------------------
Plan hash value: 3670302319

--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 732 (1)| 00:00:09 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 25 | 325 | 732 (1)| 00:00:09 | 2 | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 25 | 325 | 732 (1)| 00:00:09 | 2 | 10 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("ID">1 AND "ID">TO_NUMBER(:V))


统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
3502 consistent gets
0 physical reads
9836 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

suk@ORA10G> select count(1) from test where id>:v;


执行计划
----------------------------------------------------------
Plan hash value: 3670302319

--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 813 (1)| 00:00:10 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 500 | 6500 | 813 (1)| 00:00:10 | KEY | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 500 | 6500 | 813 (1)| 00:00:10 | KEY | 10 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("ID">TO_NUMBER(:V))


统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
374 consistent gets
0 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

suk@ORA10G> select count(1) from test where id>:v and id>1 ;


执行计划
----------------------------------------------------------
Plan hash value: 3670302319

--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 813 (1)| 00:00:10 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 25 | 325 | 813 (1)| 00:00:10 | KEY | 10 |
|* 3 | TABLE ACCESS FULL | TEST | 25 | 325 | 813 (1)| 00:00:10 | KEY | 10 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("ID">1 AND "ID">TO_NUMBER(:V))


统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
374 consistent gets
0 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

这个SQL非常简单,甚至连表关联都没有,但是它却引发了很严重的性能问题。它之所以加上条件id>1是因为业务逻辑要求这个sql的id必须大于1,虽然绝大部分情况下输入的变量值也会大于1,但开发人员担心用户输入小于1的值,导致数据错误。
我刚拿到SQL的时候,直接在PL/SQL DEV中查看执行计划,计划看起来一模一样,想不通为什么性能会这么差,后来在SQLPLUS中查看执行计划,才发现其中的端倪。

首先我们看select count(1) from test where id>1 and id>:v,它的执行计划是范围分区扫描,但是我们看到pstart=2,pstop=10,那意味着虽然我们输入的变量是9,但这个SQL仍然要扫描9个分区。
也就是在生成执行计划的时候,用的是id>1这个条件而忽略id>:v这个更好的过滤条件。扫描太多不必要的分区从而导致性能很差。
从第一个SQL看,绑定变量窥视并不起作用。

再看第二个SQL:select count(1) from test where id>:v,这个SQL对应的pstart=key,pstop=10,这说明扫描的分区个数不确定,依输入的变量值而定。

最后看第三个SQL:select count(1) from test where id>:v and id>1,神奇发生了,它和第二个SQL只是条件的顺序发生了变化,但是它的性能要好很多,主要原因是pstart=key,pstop=10,我们这里输入的变量值是9,意味着它只需要扫描2个分区,效率和第二种情况一样。

现象貌似很奇怪,但实际上是可以解释的。首先这个表很简单,没有索引,所以优化器在选择执行计划时可选计划并不多。优化器认为where中的两个条件是平等的,这种情况下,它生成执行计划时并不考虑变量值(没有使用绑定变量窥视),在RBO下,两个平等的条件会优先考虑靠后的条件,而在CBO下更好相反,会优先考虑更靠近where关键字的条件。
这些SQL都是基于CBO的,
第一个SQL,id>1靠近where关键字,因此优先考虑的是id>1,所以pstart=1
第三个SQL,id>:v靠近where关键字,因此优先考虑的是id>:v,所以pstart=key。

从这个结论可以推测,如果变量:v<=1,则第一个SQL比第三个SQL更高效,因为这种情况下它扫描更少分区,大家有兴趣的可以去验证一下。


这个文章重点是为了说明如何去分析这些问题,当你对一些优化的基本知识都有所了解时,看似很诡异的问题也就豁然开朗了。

最后,这个SQL的解决办法是把把条件id>:v放在靠近where的位置,而把id>1放在更后面的位置。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/231499/viewspace-1035616/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/231499/viewspace-1035616/

内容概要:本文系统阐述了Java Persistence API(JPA)的核心概念、技术架构、核心组件及实践应用,重点介绍了JPA作为Java官方定义的对象关系映射(ORM)规范,如何通过实体类、EntityManager、JPQL和persistence.xml配置文件实现Java对象与数据库表之间的映射与操作。文章详细说明了JPA解决的传统JDBC开发痛点,如代码冗余、对象映射繁琐、跨数据库兼容性差等问题,并解析了JPA与Hibernate、EclipseLink等实现框架的关系。同时提供了基于Hibernate和MySQL的完整实践案例,涵盖Maven依赖配置、实体类定义、CRUD操作实现等关键步骤,并列举了常用JPA注解及其用途。最后总结了JPA的标准化优势、开发效率提升能力及在Spring生态中的延伸应用。 适合人群:具备一定Java基础,熟悉基本数据库操作,工作1-3年的后端开发人员或正在学习ORM技术的中级开发者。 使用场景及目标:①理解JPA作为ORM规范的核心原理与组件协作机制;②掌握基于JPA+Hibernate进行数据库操作的开发流程;③为技术选型、团队培训或向Spring Data JPA过渡提供理论与实践基础。 阅读建议:此资源以理论结合实践的方式讲解JPA,建议读者在学习过程中同步搭建环境,动手实现文中示例代码,重点关注EntityManager的使用、JPQL语法特点以及注解配置规则,从而深入理解JPA的设计思想与工程价值。
先看效果: https://pan.quark.cn/s/d787a05b82eb 西门子SCALANCE X系列交换机是西门子公司所提供的工业以太网交换机产品系列,其在工业自动化领域具有广泛的应用。 如果在应用期间遭遇固件升级失误或采用了不相容的固件版本,可能会导致交换机无法正常启动。 在这种情况下,通常能够借助FTP(文件传输协议)来恢复交换机的固件,从而使其恢复正常运作。 本文件详细阐述了利用FTP修复SCALANCE X系列交换机固件的方法,并具体说明了实施步骤。 当SCALANCE X系列交换机的固件出现故障时,设备在启动后会自动激活引导加载程序,并通过故障LED的闪烁来表明设备处于特殊情形。 在这种情形下,交换机能够充当FTP服务器,与客户端建立联系,执行固件数据的传输。 需要特别强调的是,对于SCALANCE X200系列交换机,必须经由端口1来连接FTP客户端。 在实施步骤方面,首先需要为交换机指定一个IP地址。 这一步骤通常借助西门子公司提供的PST(Product Support Tools)软件来实施。 在成功配置IP地址之后,就可以通过FTP协议与交换机内部的FTP服务器建立连接,并借助FTP客户端将固件文件传输到交换机。 需要留意的是,在传输固件文件之前,应当先从西门子技术支持网站获取对应订货号的固件版本文件。 一旦固件文件备妥,就可以开始FTP操作。 这通常涉及打开操作系统的DOS窗口,运用FTP指令连接到交换机的FTP服务器,并输入正确的用户名和密码进行身份验证。 在本案例中,用户名和密码均为“siemens”,并且传输模式设定为二进制。 随后,使用FTP的“put”指令将本地固件文件上传至交换机。 值得留意的是,固件文件名必须严格遵循大小写规则。 上传成功后,...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值