模糊查询时(like语句),模糊匹配的占位符位于条件的首部
B+树索引的键值都是排序的,而条件的左侧使用了占位符,会导致无法按照正常的目录进行匹配,从而导致索引失效
select * from tmp where nick like '%张';
---id为主键,nick未创建索引
select * from tmp where id = 1 or nick = '张一';
>
和<
select * from tmp where id > 1 or id < 100;
在联合索引的场景下,查询条件不满足最左匹配原则
KEY `idx_uid_nick` (`uid`,`nick`)
走联合索引
---满足最左匹配原则
select * from tmp where uid = 1;
select * from tmp where uid = 1 and nick = '张一';
全表查询
select * from tmp where nick = '张一';
索引列参与运算,如加减乘除,会导致数据库需要全表查询出所有字段值,对其计算后与参数值进行比较。
select * from tmp where id + 1 = 2;
优化方式:进程内存计算/SQL查询条件右侧计算
--- 内存计算,得知要查询的id为1
select * from tmp where id = 1;
--- 参数侧计算
select * from tmp where id = 2 - 1;
查询条件使用不等进行比较时,需要慎重,普通索引会查询结果集占比较大时索引会失效
---走索引
select * from tmp where id != 1;
---不走索引
select * from tmp where uid <> 1;
MySQL不对NULL
值进行索引创建,判断IS NULL
或IS NOT NULL
可能会导致索引失效
MySQL建议最好设置为NOT NULL,需要使用NULL的情况使用DEFAULT VALUE代替
select * from tmp where nick IS NOT NULL;
索引列参与了MySQL内置函数,会导致全表扫描,索引失效
select * from tmp where date_add(create_time, INTERVAL 1 DAY) = '2022-07-30 00:00:00';
参数类型与字段类型不匹配,导致MySQL使用了convert
函数进行了类型隐式转换,导致索引失效
---nick为varchar,SQL语句中使用int类型,将参数加上单引号可避免此情况
select * from tmp where nick = 2022;
在《阿里巴巴开发手册》的ORM映射章节中有一条【强制】的规范:
【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。 说明:
- 增加查询分析器解析成本。
- 增减字段容易与
resultMap
配置不一致。- 无用字段增加网络消耗,尤其是
text
类型的字段。
KEY `idx_uid_nick` (`uid`,`nick`)
需要回表查询
select * from tmp where uid = 1 and nick = '张一';
走覆盖索引,无需回表进行主键查询
select uid, nick from tmp where uid = 1 and nick = '张一';
Mysql优化器的其他优化策略,比如优化器认为在某些情况下,全表扫描比走索引快,则它就会放弃索引
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)
不损失精确性的情况下,长度越短越好
key_len
的计算:id_no类型为varchar(18),字符集为utf8mb4_bin
,也就是使用4个字节来表示一个完整的UTF-8。此时,key_len= 18*4=72;
由于该字段类型varchar为变长数据类型,需要再额外添加2个字节。此时,key_len = 72 + 2 = 74;
由于该字段运行为NULL(default NULL),需要再添加1个字节。此时,key_len = 74 + 1 = 75;
MySQL优化器使用了全表扫描,而非辅助索引
select * from tmp where uid > 1 and uid < 100000;
具体原因是查询结果为整行信息,若走辅助索引需要回表操作到聚集索引中查询完整数据。虽然辅助索引中数据是顺序存放的,但是再一次进行书签查找的数据则是无序的,因此变为了磁盘上的离散读操作。
如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。
MySQL 5.6之前,优化器在进行离散读决策的时候,如果数据量比较大,会选择使用聚集索引,全表扫描。
MySQL5.6版本开始支持Multi-Range Read
(MRR/MR2)优化。
Multi-Range Read
优化的目的就是为了减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,这对于IO-bound类型的SQL查询语句可带来性能极大的提升。Multi-Range Read优化可适用于range,ref,eq_ref类型的查询。
MR2优化的优点:
对于InnoDB和MyISAM存储引擎的范围查询和JOIN查询操作,MR2的工作方式如下:
Index Condition Pushdown
(ICP)同样是MySQL 5.6开始支持的一种根据索引进行查询的优化方式。
在支持ICP后,MySQL数据库会在取出索引的同时,判断是否可以进行WHERE条件的过滤,也就是将WHERE的部分过滤操作放在了存储引擎层。
在某些查询下,可以大大减少上层SQL层对记录的索取(fetch),从而提高数据库的整体性能。ICP优化支持range、ref、eq_ref、ref_or_null类型的查询,当前支持MyISAM和InnoDB存储引擎。
当优化器选择ICP优化时,可在执行计划的Extra
列看到Using index condition
提示。
数据唯一性差的字段
比如性别,只有两种可能数据。意味着索引的二叉树级别少,多是平级。这样的二叉树查找无异于全表扫描。
频繁更新的字段
比如loginCount登录次数,频繁变化导致索引也频繁变化,增大数据库工作量,降低效率。
字段不在查询条件内
字段不在where语句出现时不要添加索引。如果where后含IS NULL
/IS NOT NULL
/like '%s%'
等条件,不建议使用索引
参考资料: