索引失效了?看看这几个常见的原因!

文章介绍了MySQL数据库中索引失效的几个常见原因,包括联合索引中断、数据类型不匹配、查询条件使用函数、前模糊查询、OR查询、建立索引时使用函数以及索引区分度不高等。通过示例详细解释了这些情况如何导致索引无法用于查询优化。

索引是 MySQL 数据库中优化查询性能的重要工具,通过对查询条件和表数据的索引,MySQL可以快速定位数据,提高查询效率。但是,在实际的数据库开发和维护中,我们经常会遇到一些情况,导致索引失效,从而使得查询变得非常缓慢,甚至无法使用索引来优化查询,这会严重影响系统的性能。那么,是什么原因导致了索引失效呢?

常见的情况有:

  • 索引中断
  • 数据类型不匹配
  • 查询条件使用函数操作
  • 前模糊查询
  • OR 查询
  • 建立索引时使用函数
  • 索引区分度不高

下面我通过实际的例子来具体说说。假设现在我们有一张人物表,建表语句如下:

 CREATE TABLE `person` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT,
   `name` varchar(64) NOT NULL,
   `score` int(11) NOT NULL,
   `age` int(11) NOT NULL,
   `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
复制代码

1、联合索引中断

在使用联合索引进行查询时,如果联合索引中的某一个列出现了索引中断的情况,那么整个联合索引都会失效,无法继续使用索引来优化查询。

例如:对于联合索引 (name, score),如果条件中如果只有 score,则会导致索引失效。

 CREATE INDEX idx_name_score ON person  (`name`,`score`);
 select * from person where score = 90
复制代码

而下面的情况都会使用索引:

 select * from person where name = '31a'
 select * from person where score = 90 and name = '31a'
 select * from person where name = '31a' and score = 90
复制代码

2、数据类型不匹配

如果我们在查询条件中使用了一个不匹配索引的数据类型的值,那么 MySQL 将无法使用该索引来优化查询,从而导致索引失效。

例如:如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则会导致索引失效。

 CREATE INDEX idx_name ON person (`name`);
 -- 这里 name 是 varchar 类型
 select * from person where name = 31
复制代码

但是如果索引是 int 类型,而查询参数是 varchar 类型,因为字符串隐式转为数值,不存在歧义,所以会走索引。

 CREATE INDEX idx_age ON person (`age`);
 -- 这里 age 是 int 类型
 select * from person where age = '90'
复制代码

MySQL 为什么不把 31 隐式转换字符串呢?这个问题在 MySQL 官方文档中给出了答案。

针对数值1,与字符串'1', '1a', '001', '1 '等多种情况均相等,会存在歧义。不妨看个例子:

我们插入两条数据:

 INSERT INTO test.person (id, name, score, age, create_time) VALUES(1, '00031', 90, 18, '2023-04-15 16:29:39');
 INSERT INTO test.person (id, name, score, age, create_time) VALUES(2, '31a', 96, 19, '2023-04-15 16:29:39');
复制代码

然后执行查询操作:

 select * from persion where name = 31;
复制代码

3、查询条件使用函数操作

当我们在查询条件中使用函数操作时,这将导致索引失效。例如:

 CREATE INDEX idx_name ON person (`name`);
 select * from person where UPPER(name) = '31A';
复制代码

4、前模糊查询

如果我们在查询条件中使用了前模糊查询,那么 MySQL 将无法使用 B-Tree 索引的前缀匹配查询,从而导致索引失效。例如:

 CREATE INDEX idx_name ON person (`name`);
 select * from person where name LIKE '%a';
复制代码

5、OR 查询

当我们在查询条件中使用 OR 连接多个条件时,OR 前后条件都包含索引则走索引,OR 前后有一个不包含索引则索引失效。例如:

 CREATE INDEX idx_age ON person (`age`);
 select * from person where name = 'John' OR age > 20;
复制代码

6、建立索引时使用函数

如果在建立索引时使用了函数操作,即使使用了索引列,索引也不会生效。例如:

 CREATE INDEX idx_name ON person (LOWER(name));
 -- 如果使用 LOWER(name) 函数建立索引,那么下面查询将导致索引失效
 select * from person where name = 'John';
复制代码

7、索引区分度不高

如果索引列的值区分度不高,MySQL 可能会放弃使用索引,选择全表扫描,导致索引失效。例如我们创建了下面两条索引:

 CREATE INDEX idx_name ON person (`name`);
 CREATE INDEX idx_create_time ON person (`create_time`);
复制代码

然后插入 100000 条数据:

 create PROCEDURE `insert_person`()
 begin
     declare c_id integer default 3;
     while c_id <= 100000 do
         insert into person values(c_id, concat('name',c_id), c_id + 100, c_id + 10, date_sub(NOW(), interval c_id second));
         set c_id = c_id + 1;
     end while;
 end;
 CALL insert_person();
复制代码

接着执行:

 explain select * from person where NAME>'name84059' and create_time>'2023-04-15 13:00:00'
复制代码

结果如下:

通过上面的执行计划可以看到:type=All,说明是全表扫描。

<think> 嗯,用户这次的问题很明确,就是要预防Oracle索引失效常见原因。结合之前的对话历史,用户已经了解如何检测和重建失效索引,现在更关注预防措施。 从用户引用的资料来看,索引失效主要涉及几个关键点:隐式数据类型转换、函数包裹索引列、空值处理、统计信息不准确等。用户提供的引用[1][2][4]都指向这些典型场景,特别是引用[4]提到"不等于运算符导致索引失效"的情况值得重点展开。 用户身份应该是Oracle DBA或开发人员,对性能优化有较高要求。ta的深层需求可能是想建立系统性的预防机制,而不仅仅是解决单次失效问题。考虑到索引失效对生产系统影响较大,需要给出可落地的长期维护建议。 预防措施需要分层说明:设计阶段要避免常见陷阱,开发阶段注意SQL写法,运维阶段加强监控。特别要强调统计信息维护的重要性,因为这是很多DBA容易忽视的点。分区索引的特殊性也需要单独说明,引用[2]提到的分区索引维护方案可以融入这部分。 对于函数索引的使用要谨慎建议,虽然它能解决部分失效问题,但引用[3]指出它会增加存储和写入开销。需要平衡利弊,给出适用场景的明确边界。 最后用表格总结核心要点很必要,能让用户快速对应问题场景和解决方案。相关问题的设计要延续性能优化主线,特别是分区索引监控和自动重建脚本都是DBA实际需要的进阶内容。 </think>以下是预防Oracle索引失效常见原因的系统性方法,结合官方文档和最佳实践整理而成: --- ### 一、设计阶段预防措施 1. **避免隐式数据类型转换** - **问题**:`WHERE varchar_column = 123`(数字与字符比较导致索引失效)[^1][^4] - **解决**:统一数据类型,强制显式转换: ```sql -- 正确写法 WHERE varchar_column = TO_CHAR(123); ``` 2. **谨慎使用函数/表达式** - **问题**:`WHERE UPPER(name) = 'JOHN'`(函数包裹索引列)[^2] - **解决**: - 使用函数索引:`CREATE INDEX idx_upper_name ON emp(UPPER(name));` - 改写查询:`WHERE name = 'JOHN'`(保持原字段格式) 3. **处理空值(NULL)优化** - **问题**:`WHERE column IS NULL` 可能无法使用索引[^4] - **解决**: ```sql -- 添加默认值 ALTER TABLE emp MODIFY (commission NUMBER DEFAULT 0); -- 或使用组合索引 CREATE INDEX idx_comm ON emp(commission, 1); ``` --- ### 二、SQL开发规范 1. **避免索引列参与计算** ```sql -- 错误写法(索引失效) WHERE salary*1.1 > 5000; -- 正确写法 WHERE salary > 5000/1.1; ``` 2. **慎用不等于(<>/!=)和NOT IN** - **问题**:优化器可能放弃索引[^4] - **替代方案**: ```sql -- 使用范围查询替代 WHERE status > 'A' OR status < 'A'; ``` 3. **LIKE查询优化** - **问题**:`LIKE '%abc%` 无法使用索引 - **解决**: ```sql -- 前缀匹配可使用索引 WHERE name LIKE 'abc%'; ``` --- ### 三、运维监控策略 1. **定期更新统计信息** ```sql -- 手动收集表统计信息 EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMP'); ``` - **频率**:高频DML表每日收集,静态表每周收集[^1] 2. **分区索引维护** - 分区表需单独维护子分区索引: ```sql ALTER INDEX idx_sales REBUILD PARTITION p2023; ``` 3. **索引健康监控脚本** ```sql -- 定期检查失效索引(加入定时任务) SELECT owner, index_name, status FROM dba_indexes WHERE status = 'UNUSABLE'; ``` --- ### 四、架构设计建议 | **场景** | **风险点** | **预防方案** | |--------------------|-------------------|----------------------------------| | 频繁更新字段 | 索引碎片化 | 使用`REVERSE KEY`索引 | | 大表范围查询 | 全表扫描 | 组合索引+`WHERE`条件字段顺序匹配 | | JSON/LOB字段查询 | 函数索引失效 | 使用物化视图或搜索索引 | | 高并发写入 | 索引维护开销 | 评估索引必要性,采用异步维护 | --- ### 五、长期维护机制 1. **自动化重建策略** ```sql -- 自动重建失效索引(PL/SQL脚本示例) BEGIN FOR idx IN (SELECT index_name FROM user_indexes WHERE status='UNUSABLE') LOOP EXECUTE IMMEDIATE 'ALTER INDEX '|| idx.index_name ||' REBUILD'; END LOOP; END; ``` 2. **SQL审核工具** - 部署SQL审核规则(如避免`SELECT *`、强制绑定变量等) 3. **性能基线比对** - 使用AWR报告对比索引使用率变化 > **关键点**:索引失效的60%源于SQL写法不当,30%因统计信息过期,10%是硬件/存储异常[^1][^4]。定期结合`EXPLAIN PLAN`分析执行计划是核心防御手段。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值