本文目录导读:

- 核心原则:索引失效的常见场景(反模式排查)
- 规范化的索引生效流程(DDL + SQL + Application)
- 完整生效流程示例(Java + MySQL)
- 特殊情况:全文索引(Elasticsearch / MySQL Fulltext)
- 日常开发规范清单
在Java(特别是结合数据库如MySQL、PostgreSQL,或结合Elasticsearch等搜索技术)中,索引生效流程的规范性直接决定了查询性能,这里的“规范”通常指:确保索引被正确创建、被查询优化器正确使用、且不会因数据变更或DDL操作而失效。
以下从 数据库索引(以MySQL InnoDB为例,最典型)和 Java应用层(如JPA/Hibernate慢查询治理)两个维度,梳理索引生效的规范性流程。
核心原则:索引失效的常见场景(反模式排查)
索引不是创建了就会自动生效,规范流程的第一步是避免索引失效,以下是Java + MySQL中最常见的索引失效场景:
- 隐式类型转换:
WHERE varchar_col = 123(Java传入数字,SQL中字段是字符串,索引失效)。 - 函数操作:
WHERE DATE(time_col) = '2024-01-01'(在索引列上使用函数)。 - LIKE 通配符前缀:
WHERE name LIKE '%张三'(%放在前面)。 - OR 条件:
WHERE a = 1 OR b = 2(如果a和b不是都独立有索引,可能导致全表扫描)。 - 联合索引最左前缀失效:联合索引
(a, b, c),查询条件只用到b或c(跳过了a)。 - 不等号(!=, <>):通常会导致索引失效(部分数据库优化器会走全表扫描)。
- IS NULL / IS NOT NULL:取决于数据库版本和列属性,可能部分生效。
- 数据量小:优化器认为全表扫描比回表更快,主动放弃索引。
规范化的索引生效流程(DDL + SQL + Application)
DDL 设计规范(建表/加索引)
- 明确索引类型:区分普通索引、唯一索引、联合索引、全文索引。
- 联合索引顺序:选择性最高的列放最左侧(去重率越高越优先)。
- 避免冗余索引:
(a, b)和(a, b, c)是冗余的(前者可以覆盖后者最左前缀)。 - 使用前缀索引:对字符串(varchar(255))长文本,
INDEX idx_name(name(10))。
SQL 编写规范(查询侧)
- 强制走指定索引(兜底手段):
SELECT * FROM user FORCE INDEX(idx_user_name) WHERE name = '张三';
- 避免在索引列上计算:
-- 错误 WHERE YEAR(create_time) = 2024 -- 正确(范围查询) WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01'
- Join 字段字符集一致:MySQL 跨表 Join 时,如果两个表的字段字符集不同(如utf8 vs utf8mb4),索引会失效。
Java 应用层规范(JPA / MyBatis)
- MyBatis 动态SQL:确保
<if test>不会生成WHERE 1=1加上OR导致索引失效。 - 避免 N+1 查询:使用
@EntityGraph或@BatchSize或手动关联查询(而不是循环内查单条)。 - 分页查询:使用
LIMIT配合索引排序(避免ORDER BY走文件排序)。
验证流程(必须的规范动作)
每一步加完索引后,必须通过 EXPLAIN 验证是否真正走索引。
EXPLAIN SELECT * FROM user WHERE name = '张三';
关键字段:
type:const>ref>range>index>ALL(尽量达到ref或range)key:显示实际使用的索引名(如果为NULL表示没走索引)rows:扫描行数(期望远小于总行数)Extra:避免出现Using filesort、Using temporary、Using where; Using index(索引覆盖是好的)
完整生效流程示例(Java + MySQL)
场景:用户表按 phone 和 status 查询
-- 1. DDL 创建联合索引(phone选择性好放左侧)
ALTER TABLE user ADD INDEX idx_phone_status (phone, status);
-- 2. Java 代码编写(确保传入类型与数据库一致)
// 正确:phone是varchar,Java传入String
String phone = "13800138000";
Integer status = 1;
// 错误隐患:如果phone是varchar,传入Long类型会隐式转换
// Long phone = 13800138000L; -- 导致索引失效!
// MyBatis Mapper 写法
// <select id="findByPhoneAndStatus">
// SELECT * FROM user
// WHERE phone = #{phone} AND status = #{status}
// </select>
验证步骤:
EXPLAIN SELECT * FROM user WHERE phone = '13800138000' AND status = 1; -- 期望结果:type = ref, key = idx_phone_status, rows = 1
监控与告警(Java侧)
- 使用 Aliyun RDS 慢查询日志 或 p6spy(JDBC代理)自动打印SQL执行计划。
- 配置
Druid或HikariCP的慢查询阈值(slowSqlMillis=500)。 - 在 Java 代码层 添加 AOP 切面,记录执行时间超过阈值的 SQL,并人工审查索引情况。
特殊情况:全文索引(Elasticsearch / MySQL Fulltext)
如果是 ES 或 Solr 场景,索引生效的规范包括:
- Mapping 前:明确字段是否需要分词(
keywordvstext)。 - 查询类型:
term(精确匹配走索引) vsmatch(分词后走索引)。wildcard前缀模糊匹配会导致性能问题。 - 禁止搜索 或
_all字段(大量字段盲搜)。 - 使用
explain API查看打分和命中情况。
日常开发规范清单
| 阶段 | 规范动作 | 检查点 |
|---|---|---|
| 建表 | 评估查询字段,建联合索引 | 选择性高的列放左侧,避免冗余索引 |
| 编码 | SQL 避免函数、隐式转换、LIKE前缀% | 使用 MyBatis <trim> 或 @Query 指定参数类型 |
| 测试 | 加索引后运行 EXPLAIN |
type ≠ ALL,key 不为空,rows 小 |
| 发布 | 同步索引 DDL 给 DBA 审核 | 线上环境先 pt-online-schema-change 无损加索引 |
| 运维 | 开启慢查询日志 >1s 的记录,周报分析 | 每周抽检 Top 10 慢 SQL,做索引优化 |
最终铁定律:索引不生效的原因,90% 是 Java 代码传参类型错误 或 SQL 写法破坏了索引规则。 建议在团队内统一使用
EXPLAIN作为代码审查的必过项。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播