数据库索引失效的一种场景:分析问题的思路和策略

这是公司研发团队发现的一个关于数据库索引失效方面的问题,我们的工程师对该问题进行了分析和解决并写了这份小结。归根揭底还是对开发框架和技术应用的把握上存在纰漏,但个人觉得在分析问题->找出原因->确认解决方案这一思路和策略上本文能起到一定借鉴作用,所以稍微梳理了一下拿出来和大家分享。

问题的现状是测试人员反馈某一个功能操作耗时很长(需要20秒以上),而开发人员核对代码发现无论从业务逻辑上还是代码实现上都没有问题,涉及到的数据查询等功能在数据库中也创建了合适的索引以确保查询效率,于是我们的工程师就去研究了,过程如下:

1.      首先应用内嵌入了一个系统监控平台JavaMelody(此为google出品的一个开源神器 https://code.google.com/p/javamelody/

2.      通过该神器观测到该操作的方法执行帧信息,发现其中耗时最多的一步:

3.      下钻至此方法内部,看到其调用的sql为:

4.      对该sql进行分析,将其摘入PLSQL Developer工具内,并对变量赋值进行验证执行,反复验证多次发现每次速度都还不到1秒:

5.      就此产生疑惑:为何在服务器上执行需要20多秒,而在客户端工具内却飞快呢?猜测跟JDBC有关。为了证实这个想法,做了单元测试,果不其然,在单元测试中同样需要20多秒,为毛通过JDBC查询就这么慢呢?

6.      前思后想,在JDBC中和在客户端中查询最大的区别就在于前者使用了绑定变量,而后者是一个静态的sql。为了进一步验证,把单元测试中的sql变成了静态的(非变量绑定形式),发现快多了,和在第4步PLSQL Developer客户端工具中执行一样快,委屈了JDBC:

原来问题在于变量绑定

问题找到了,那就要分析导致这种结果的原因。简单说来,我们的故事是这样的:

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("queryTimeFrom", queryTimeFrom );
parameters.addValue("queryTimeTo", queryTimeTo);

其中queryTimeFrom和queryTimeTo是方法传进来的参数,类型为java.util.Date。

执行查询调用的是:
List<DrugOrderAdmin> result = getJdbcTemplate().query(sql.toString(), parameters, new DrugOrderAdminMapper());

本以为传进sql里的参数是Date类型,但其实经过了spring框架内部代码那么一折腾,最后变成了Timestamp类型:

其中 isDateValue方法的定义为:

再到oracle中,原本sql中的 AND PLAN_TIME >=?
AND PLAN_TIME <= ?  这个条件,由于传入的参数是Timestamp类型,导致oracle解释器把sql解释为:

AND TO_TIMESTAMP(PLAN_TIME) >= ?
AND TO_TIMESTAMP(PLAN_TIME) <= ?

这样一来,PLAN_TIME字段上的索引就被忽略,走了全表扫描(除非另外建一个函数索引TO_TIMESTAMP(PLAN_TIME)),导致最终查询速度产生了如此巨大的差别。

原因找到了,解决方案也就很明确了,即在绑定变量时强制指定JDBC类型为DATE:

parameters.addValue("queryTimeFrom", queryTimeFrom);改成:
parameters.addValue("queryTimeFrom", queryTimeFrom , Types.DATE);
parameters.addValue("queryTimeTo", queryTimeTo); 改成:
parameters.addValue("queryTimeTo", queryTimeTo, Types.DATE);

再回到PLSQL Developer中,模拟一下传入参数为Timestamp类型的情况,这次采用绑定变量的方式,即在所有需要传入参数的地方预留一个变量(以&开头命名),在执行时客户端会自动弹出变量输入对话框:

这次可以看到,以绑定变量方式执行查询,并且绑定变量为Timestamp类型时,PLSQL Developer同样需要很久,之前的疑惑不释而解:

总结:在使用绑定变量进行sql查询时,一定要注意传入参数的类型和column类型一致,能够明确指定参数类型的情况下尽量明确指定,否则类型不当会导致列的索引失效。

时间: 2024-10-06 14:10:59

数据库索引失效的一种场景:分析问题的思路和策略的相关文章

MySQL索引失效的几种情况

1.索引不存储null值 更准确的说,单列索引不存储null值,复合索引不存储全为null的值.索引不能存储Null,所以对这列采用is null条件时,因为索引上根本 没Null值,不能利用到索引,只能全表扫描. 为什么索引列不能存Null值? 将索引列值进行建树,其中必然涉及到诸多的比较操作.Null值的特殊性就在于参与的运算大多取值为null. 这样的话,null值实际上是不能参与进建索引的过程.也就是说,null值不会像其他取值一样出现在索引树的叶子节点上. 2.不适合键值较少的列(重复

MySQL索引类型 &amp; Mysql索引会失效的几种情况分析

MySQL索引类型介绍 (1)普通索引 这是最基本的索引,它没有任何限制.它有以下几种创建方式: CREATE INDEX indexName ON mytable(username(length)); 如果是CHAR,VARCHAR类型,length可以小于字段实际长度:如果是BLOB和TEXT类型,必须指定 length,下同. ALTER mytable ADD INDEX [indexName] ON (username(length)) CREATE TABLE mytable( ID

Mysql索引会失效的几种情况分析

在做项目的过程中,难免会遇到明明给mysql建立了索引,可是查询还是很缓慢的情况出现,下面我们来具体分析下这种情况出现的原因及解决方法: 索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用

【转-mysql索引失效的几种情形】

索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分(第一个),则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果MySQL估计使用全表扫描要比使用索引快,则不使用索引 此外,查看索引的使用情况show stat

数据库索引失效

今天一个同事突然问我索引为什么失效.说实在的,失效的原因有多种: 但是如果是同样的sql如果在之前能够使用到索引,那么现在使用不到索引,以下几种主要情况: 1. 随着表的增长,where条件出来的数据太多,大于15%,使得索引失效(会导致CBO计算走索引花费大于走全表) 2. 统计信息失效      需要重新搜集统计信息 3. 索引本身失效      需要重建索引 下面是一些不会使用到索引的原因 索引失效 1) 没有查询条件,或者查询条件没有建立索引 2) 在查询条件上没有使用引导列 3) 查询

数据库索引失效原因

1.应尽量避免在where子句中使用!=或者<>操作符,否则引擎将放弃使用索引而进行全表扫描. 2.尽量避免在where子句中使用or来链接条件,否则将导致引擎放弃使用索引而进行全表扫描,即使其中有条件带索引也不会使用,这也是为什么尽量少使用or的原因. 3.对于多列索引,应满足最左匹配原则: 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引. 5.link的模式查询以%开头,索引失效. 6.应尽量避免在wher子句中对字段进行表达式操作,这将导致引擎放弃使用索

Android5.0L下因sensorservice crash导致systemserver重启的另外一种场景分析

一.出问题的场景 1.Sensorservice线程正在处理compass sensor事件的过程中,检查了一次buffer的指针的有效性,并在稍后会传递到AKM获取数据的函数接口中使用 2.Sensorservice线程所在进程的负责跨进程通信的Binder线程在sensorservice线程检查buffer指针之后没有真正使用之前, 收到了disable compass sensor的请求,从log中可以看到compass  sensor先是被disable,disable的同时会free上

B树在数据库索引中的应用剖析(转载)

引言 关于数据库索引,随便Google一个Oracle index,Mysql index总有大量的结果出来,其中不乏某某索引之n条经典建议.笔者认为,较之借鉴,在搞清楚了自己的需求的基础上,对备选方案的原理有个尽可能深入全面的了解会更有利于我们的选择和决策.因为某种方案或者技术呈现出某种优势(包括可能没有被介绍到但一定存在的限制),不是定义出来的,而是因为其实现机制决定的.就像LinkedList和ArrayList分别适用于什么应用不是Document里面定义的,是由其本身的结构决定的.数据

Oracle数据库索引使用及索引失效总结

容易引起oracle索引失效的原因很多: 1.在索引列上使用函数.如SUBSTR,DECODE,INSTR等,对索引列进行运算.需要建立函数索引就可以解决了. 2.新建的表还没来得及生成统计信息,分析一下就好了 3.基于cost的成本分析,访问的表过小,使用全表扫描的消耗小于使用索引. 4.使用<>.not in .not exist,对于这三种情况大多数情况下认为结果集很大,一般大于5%-15%就不走索引而走FTS. 5.单独的>.<. 6.like "%_"