侧边栏壁纸
  • 累计撰写 72 篇文章
  • 累计创建 31 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

盘点Mysql慢查询的12个原因(未完待续)-Peak-Gao

PeakGao
2024-11-27 / 0 评论 / 0 点赞 / 6 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
部分素材可能会来自网络,若不小心影响到您的利益,请联系我们删除。

在这里插入图片描述

1、sql没加索引

很多时候,我们的慢查询,都是因为没有加索引,如果没有加索引的话,会导致全表扫描,因此在where的条件列建立索引,尽量避免全表扫描
反例:
在这里插入图片描述

正例:
在这里插入图片描述

2、sql索引不生效

有时候明明添加了索引却不生效,在以下场景不生效,如下图
在这里插入图片描述

2.1 隐式的类型转换,索引失效

CREATE TABLE USER (
id INT ( 11 ) NOT NULL AUTO_INCREMENT,
userId VARCHAR ( 32 ) NOT NULL,
age VARCHAR ( 16 ) NOT NULL,
NAME VARCHAR ( 255 ) NOT NULL,
PRIMARY KEY ( id ),
KEY idx_userid ( userId ) USING BTREE 
) ENGINE = INNODB DEFAULT CHARSET = utf8

userId是字符串类型,是B+树普通索引, 而这是传一个数字类型,就会导致索引失效
在这里插入图片描述
在这里插入图片描述

为什么第一条语句未加单引号就不走索引了呢?
这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。隐式的类型转换,索引会失效。

2.2 查询条件包含or,可能导致索引失效

在这里插入图片描述

对于or + 没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,还得全表扫描,也就是需要三步过程:全表扫描+索引扫描+合并,如果他一开始就走全表扫描,直接一遍扫描就完事Mysql优化器出于效率与成本考虑,遇到or条件,让索引失效,但是倮or条件列都加了索引,索引可能走可能不走,可以考虑拆成两条sql

2.3 like通配符可能导致索引失效

这里指like查询以%开头,才会导致索引失效
在这里插入图片描述

将%放后面,还是正常走索引
在这里插入图片描述

2.3.1 避免索引失效

1.使用覆盖索引(查询方式)
主键索引:叶子节点存储数据、辅助索引(非主键索引):叶子节点存储主键=回表
在这里插入图片描述

2.将%放后面
在这里插入图片描述

2.4 查询条件不满足联合索引的最左匹配原则

mysql建立联合索引时,会遵循最左匹配规则,即最左优先,如 建立一个(userId,name)联合索引,相当于建立(userId)、(userId ,name)三个索引

在这里插入图片描述

因为name列不满足mysql最左原则匹配(不是联合索引的第一个列),导致索引失效
在这里插入图片描述
在这里插入图片描述

2.5在索引上使用mysql内置函数

DROP TABLE USER
CREATE TABLE `user` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`userId` VARCHAR ( 32 ) NOT NULL,
`login_time` datetime NOT NULL,
PRIMARY KEY ( `id` ),
KEY `idx_userId` ( `userId` ) USING BTREE,
KEY `idx_login_time` ( `login_Time` ) USING BTREE 
) ENGINE = INNODB AUTO_INCREMENT = 2 DEFAULT CHARSET = utf8

虽然login_time加了索引,但是使用了mysql内置函数:DATE_ADD(),直接凉凉,如图
在这里插入图片描述

可以将内置函数的逻辑转移到右边索引就生效了,如下:
在这里插入图片描述

2.6 对索引进行列运算(如,+、-、*、/),索引不生效

虽然age加索引了,但是对他进行运算,导致索引失效, 所以不可以对索引列进行运算,可以在代码处理好,再传参进去
在这里插入图片描述

2.7 索引字段上使用(!= 或者 < >),索引可能失效

避免拿索引列作为条件查询使用 != 、<> 、not in,这和mysql优化器有关,如果优化器觉得即使走了索引,还是需要扫描很多很多行的话,不如直接不走索引
在这里插入图片描述

2.8 索引字段上使用is null, is not null,索引可能失效

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `card` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE,
  KEY `idx_card` (`card`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

单个name和card字段加上索引,加上is not null 或is null 其实是会走索引的,但是用or连接索引就会失效

explain select * from user where name is not null
explain select * from user where card is not null

explain select * from user where name is  null
explain select * from user where card is  null

explain select * from user where  name is not null or card is not null
explain select * from user where  name is  null or card is  null

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

很多时候,也是因为数据量问题,导致mysql优化器放弃走索引,同时我们用explain分析sql时,如果type=range时这个可能因为数据量问题导致索引无效

2.9 左右连接,关联的字段编码格式不一样

DROP table user
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `user_job` (
  `id` int(11) NOT NULL,
  `userId` int(11) NOT NULL,
  `job` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

user表的name字段编码是utf8mb4,而user_job表的name字段编码为utf8
在这里插入图片描述
执行左外连接查询,user_job表还是走全表扫描,如下:
在这里插入图片描述
如果把它们的name字段改为编码一致,相同的SQL,还是会走索引

在这里插入图片描述
所以大家在做表关联时,注意一下关联字段的编码问题哈。

2.10 优化器选错了索引

MySQL 中一张表是可以支持多个索引的。你写SQL语句的时候,没有主动指定使用哪个索引的话,用哪个索引是由MySQL来确定的。
我们日常开发中,不断地删除历史数据和新增数据的场景,有可能会导致MySQL选错索引。那么有哪些解决方案呢?

  • 使用force index 强行选择某个索引
  • 修改你的SQl,引导它使用我们期望的索引
  • 优化你的业务逻辑
  • 优化你的索引,新建一个更合适的索引,或者删除误用的索引。

3、limit深分页

limit深分页问题,会导致慢查询,应该大家都司空见惯了吧。

3.1 limit深分页为什么会变慢

limit深分页为什么会导致SQL变慢呢?假设我们有表结构如下:

CREATE TABLE account (
  id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  name varchar(255) DEFAULT NULL COMMENT '账户名',
  balance int(11) DEFAULT NULL COMMENT '余额',
  create_time datetime NOT NULL COMMENT '创建时间',
  update_time datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  KEY idx_name (name),
  KEY idx_create_time (create_time) //索引
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表'

你知道以下SQL,执行过程是怎样的嘛?

select id,name,balance from account where create_time > '2020-09-19' limit 100000,10

这个SQL的执行流程:
1.通过普通二级索引树idx_create_time,过滤create_time条件,找到满足条件的主键id
2.通过主键id,回到id主键索引树,找到满足记录的行,然后取出需要展示的列(回表过程)
3.扫描满足条件的100010行,然后扔掉前100000行,返回。
在这里插入图片描述
limit深分页,导致SQL变慢原因有两个:

  • limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。
  • limit 100000,10 扫描更多的行数,也意味着回表更多的次数

未完待续

3.2 如何优化深分页问题

4、单表数据量太大

4.1 单表数据量太大为什么会变慢?

4.2 一棵B+树可以存多少数据量

4.3 如何解决单表数据量太大,查询变慢的问题

5、 join或子查询过多

6、in元素过多

7、数据库在刷脏页

7.1 什么是脏页

7.2 一条更新语句是如何执行的

7.3 为什么会出现脏页呢?

7.4 什么时候会刷脏页(flush)?

7.5 为什么刷脏页会导致SQL变慢呢?

8、order by 文件排序

8.1 order by 的 Using filesort文件排序

8.2 order by文件排序效率为什么较低

8.3 如何优化order by的文件排序

9、拿不到锁

10、delete + in 子查询不走索引

11、group by 使用临时表

11.1 group by的执行流程

11.2 group by可能会慢在哪里?

11.3 如何优化group by呢?

12、系统硬件或网路资源

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区