范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

导致数据库慢查询的12个常见原因,以及对应的解决方法

  前言
  大家好,我是 小满只想睡觉 。
  日常开发中,我们经常会遇到 数据库慢查询 。那么导致数据慢查询都有哪些常见的原因?今天就跟大家聊聊导致数据库慢查询的12个常见原因,以及对应的解决方法。
  1. SQL没加索引
  很多时候,我们的慢查询,都是因为没有加索引。如果没有加索引的话,会导致全表扫描的。因此,应考虑在where的条件列,建立索引,尽量避免全表扫描。
  反例:select * from user_info where name ="捡田螺的小男孩公众号" ; 复制代码
  正例://添加索引 alter table user_info add index idx_name (name); 复制代码
  2. SQL 索引不生效
  有时候我们明明加了索引了,但是索引却不生效。在哪些场景,索引会不失效呢?主要有以下十大经典场景:
  2.1 隐式的类型转换,索引失效
  我们创建一个用户user表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,可能导致索引失效
  我们还是用这个表结构: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加了索引,但是age没有加索引的。我们使用了or,以下SQL是不走索引的,如下:
  对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程:全表扫描+索引扫描+合并。如果它一开始就走全表扫描,直接一遍扫描就完事。Mysql优化器出于效率与成本考虑,遇到or条件,让索引失效,看起来也合情合理嘛。
  注意:如果or条件的列都加了索引,索引可能会走也可能不走,大家可以自己试一试哈。但是平时大家使用的时候,还是要注意一下这个or,学会用explain分析。遇到不走索引的时候,考虑拆开两条SQL。2.3. like通配符可能导致索引失效。
  并不是用了like通配符,索引一定会失效,而是like查询是以%开头,才会导致索引失效。
  like查询以%开头,索引失效explain select * from user where userId like "%123"; 复制代码
  把%放后面,发现索引还是正常走的,如下:explain select * from user where userId like "123%"; 复制代码
  既然like查询以%开头,会导致索引失效。我们如何优化呢?使用覆盖索引把%放后面2.4 查询条件不满足联合索引的最左匹配原则
  MySQl建立联合索引时,会遵循最左前缀匹配的原则,即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了(a)、(a,b)、(a,b,c)三个索引。
  假设有以下表结构:CREATE TABLE user (   id int(11) NOT NULL AUTO_INCREMENT,   user_id varchar(32) NOT NULL,   age  varchar(16) NOT NULL,   name varchar(255) NOT NULL,   PRIMARY KEY (id),   KEY idx_userid_name (user_id,name) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 复制代码
  有一个联合索引idx_userid_name,我们执行这个SQL,查询条件是name,索引是无效:explain select * from user where name ="捡田螺的小男孩"; 复制代码
  因为查询条件列name不是联合索引idx_userid_name中的第一个列,索引不生效
  在联合索引中,查询条件满足最左匹配原则时,索引才正常生效。
  2.5 在索引列上使用mysql的内置函数
  表结构: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(),索引直接GG,如图:
  一般这种情况怎么优化呢?可以把内置函数的逻辑转移到右边,如下:explain  select * from user where login_time = DATE_ADD("2022-05-22 00:00:00",INTERVAL 1 DAY); 复制代码
  2.6 对索引进行列运算(如,+、-、*、/),索引不生效
  表结构:CREATE TABLE `user` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `userId` varchar(32) NOT NULL,   `age` int(11) DEFAULT NULL,   PRIMARY KEY (`id`),   KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 复制代码
  虽然age加了索引,但是因为它进行运算,索引直接迷路了, 如图:
  所以不可以对索引列进行运算,可以在代码处理好,再传参进去。2.7 索引字段上使用(!= 或者 < >),索引可能失效
  表结构:CREATE TABLE `user` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `userId` int(11) NOT NULL,   `age` int(11) DEFAULT NULL,   `name` varchar(255) NOT NULL,   PRIMARY KEY (`id`),   KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 复制代码
  虽然age加了索引,但是使用了!= 或者< >,not in这些时,索引如同虚设。如下:
  其实这个也是跟mySQL优化器有关,如果优化器觉得即使走了索引,还是需要扫描很多很多行的哈,它觉得不划算,不如直接不走索引。平时我们用!= 或者< >,not in的时候,留点心眼哈。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字段加上索引,并查询name为非空的语句,其实会走索引的,如下:
  单个card字段加上索引,并查询name为非空的语句,其实会走索引的,如下:
  但是它两用or连接起来,索引就失效了,如下:
  很多时候,也是因为数据量问题,导致了MySQL优化器放弃走索引。同时,平时我们用explain分析SQL的时候,如果type=range,要注意一下哈,因为这个可能因为数据量问题,导致索引没无效。2.9 左右连接,关联的字段编码格式不一样
  新建两个表,一个user,一个user_jobCREATE 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的执行流程:通过普通二级索引树idx_create_time,过滤create_time条件,找到满足条件的主键id。通过主键id,回到id主键索引树,找到满足记录的行,然后取出需要展示的列(回表过程)扫描满足条件的100010行,然后扔掉前100000行,返回。
  limit深分页,导致SQL变慢原因有两个:limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。limit 100000,10 扫描更多的行数,也意味着回表更多的次数。3.2 如何优化深分页问题
  我们可以通过减少回表次数来优化。一般有标签记录法和延迟关联法。
  标签记录法
  就是标记一下上次查询到哪一条了,下次再来查的时候,从该条开始往下扫描。就好像看书一样,上次看到哪里了,你就折叠一下或者夹个书签,下次来看的时候,直接就翻到啦。
  假设上一次记录到100000,则SQL可以修改为:select  id,name,balance FROM account where id > 100000 limit 10; 复制代码
  这样的话,后面无论翻多少页,性能都会不错的,因为命中了id索引。但是这种方式有局限性:需要一种类似连续自增的字段。
  延迟关联法
  延迟关联法,就是把条件转移到主键索引树,然后减少回表。如下:select  acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN (SELECT a.id FROM account a WHERE a.create_time > "2020-09-19" limit 100000, 10) AS acct2 on acct1.id= acct2.id; 复制代码
  优化思路就是,先通过idx_create_time二级索引树查询到满足条件的主键ID,再与原表通过主键ID内连接,这样后面直接走了主键索引了,同时也减少了回表。4. 单表数据量太大4.1 单表数据量太大为什么会变慢?
  一个表的数据量达到好几千万或者上亿时,加索引的效果没那么明显啦。性能之所以会变差,是因为维护索引的B+树结构层级变得更高了,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。4.2 一棵B+树可以存多少数据量
  大家是否还记得,一个B+树大概可以存放多少数据量呢?
  InnoDB存储引擎最小储存单元是页,一页大小就是16k。
  B+树叶子存的是数据,内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中找到需要的数据;
  假设B+树的高度为2的话,即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=根结点指针数*单个叶子节点记录行数。如果一行记录的数据大小为1k,那么单个叶子节点可以存的记录数 =16k/1k =16.非叶子节点内存放多少指针呢?我们假设主键ID为bigint类型,长度为8字节(面试官问你int类型,一个int就是32位,4字节),而指针大小在InnoDB源码中设置为6字节,所以就是8+6=14字节,16k/14B =16*1024B/14B = 1170
  因此,一棵高度为2的B+树,能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树,能存放1170 *1170 *16 =21902400,也就是说,可以存放两千万左右的记录。B+树高度一般为1-3层,已经满足千万级别的数据存储。
  如果B+树想存储更多的数据,那树结构层级就会更高,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。4.3 如何解决单表数据量太大,查询变慢的问题
  一般超过千万级别,我们可以考虑分库分表了。
  分库分表可能导致的问题:事务问题跨库问题排序问题分页问题分布式ID
  因此,大家在评估是否分库分表前,先考虑下,是否可以把部分历史数据归档先,如果可以的话,先不要急着分库分表。如果真的要分库分表,综合考虑和评估方案。比如可以考虑垂直、水平分库分表。水平分库分表策略的话,range范围、hash取模、range+hash取模混合等等。5. join 或者子查询过多
  一般来说,不建议使用子查询,可以把子查询改成join来优化。而数据库有个规范约定就是:尽量不要有超过3个以上的表连接。为什么要这么建议呢? 我们来聊聊,join哪些方面可能导致慢查询吧。
  MySQL中,join的执行算法,分别是:Index Nested-Loop Join和Block Nested-Loop Join。Index Nested-Loop Join:这个join算法,跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引。Block Nested-Loop Join:这种join算法,被驱动表上没有可用的索引,它会先把驱动表的数据读入线程内存join_buffer中,再扫描被驱动表,把被驱动表的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。
  join过多的问题:
  一方面,过多的表连接,会大大增加SQL复杂度。另外一方面,如果可以使用被驱动表的索引那还好,并且使用小表来做驱动表,查询效率更佳。如果被驱动表没有可用的索引,join是在join_buffer内存做的,如果匹配的数据量比较小或者join_buffer设置的比较大,速度也不会太慢。但是,如果join的数据量比较大时,mysql会采用在硬盘上创建临时表的方式进行多张表的关联匹配,这种显然效率就极低,本来磁盘的 IO 就不快,还要关联。
  一般情况下,如果业务需要的话,关联2~3个表是可以接受的,但是关联的字段需要加索引哈。如果需要关联更多的表,建议从代码层面进行拆分,在业务层先查询一张表的数据,然后以关联字段作为条件查询关联表形成map,然后在业务层进行数据的拼装。6. in元素过多
  如果使用了in,即使后面的条件加了索引,还是要注意in后面的元素不要过多哈。in元素一般建议不要超过500个,如果超过了,建议分组,每次500一组进行哈。
  反例:select user_id,name from user where user_id in (1,2,3...1000000);  复制代码
  如果我们对in的条件不做任何限制的话,该查询语句一次性可能会查询出非常多的数据,很容易导致接口超时。尤其有时候,我们是用的子查询,in后面的子查询,你都不知道数量有多少那种,更容易采坑(所以我把in元素过多抽出来作为一个小节)。如下这种子查询:select * from user where user_id in (select author_id from artilce where type = 1); 复制代码
  正例是,分批进行,每批500个:select user_id,name from user where user_id in (1,2,3...500); 复制代码
  如果传参的ids太多,还可以做个参数校验什么的if (userIds.size() > 500) {     throw new Exception("单次查询的用户Id不能超过200"); } 复制代码7. 数据库在刷脏页7.1 什么是脏页
  当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为"脏页"。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为"干净页"。一般有更新SQL才可能会导致脏页,我们回忆一下:一条更新语句是如何执行的7.2 一条更新语句是如何执行的?
  以下的这个更新SQL,如何执行的呢?update t set c=c+1 where id=666; 复制代码对于这条更新SQL,执行器会先找引擎取id=666这一行。如果这行所在的数据页本来就在内存中的话,就直接返回给执行器。如果不在内存,就去磁盘读入内存,再返回。执行器拿到引擎给的行数据后,给这一行C的值加一,得到新的一行数据,再调用引擎接口写入这行新数据。引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,但是此时redo log 是处于prepare状态的哈。执行器生成这个操作的binlog,并把binlog写入磁盘。执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。
  InnoDB 在处理更新语句的时候,只做了写日志这一个磁盘操作。这个日志叫作redo log(重做日志)。平时更新SQL执行得很快,其实是因为它只是在写内存和redo log日志,等到空闲的时候,才把redo log日志里的数据同步到磁盘中。
  有些小伙伴可能有疑惑,redo log日志不是在磁盘嘛?那为什么不慢?其实是因为写redo log的过程是顺序写磁盘的。磁盘顺序写会减少寻道等待时间,速度比随机写要快很多的。7.3 为什么会出现脏页呢?
  更新SQL只是在写内存和redo log日志,等到空闲的时候,才把redo log日志里的数据同步到磁盘中。这时内存数据页跟磁盘数据页内容不一致,我们称之为脏页。7.4 什么时候会刷脏页(flush)?
  InnoDB存储引擎的redo log大小是固定,且是环型写入的,如下图(图片来源于MySQL 实战 45 讲):
  那什么时候会刷脏页?有几种场景:redo log写满了,要刷脏页。这种情况要尽量避免的。因为出现这种情况时,整个系统就不能再接受更新啦,即所有的更新都必须堵住。内存不够了,需要新的内存页,就要淘汰一些数据页,这时候会刷脏页
  InnoDB 用缓冲池(buffer pool)管理内存,而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。MySQL 认为系统空闲的时候,也会刷一些脏页MySQL 正常关闭时,会把内存的脏页都 flush 到磁盘上7.5 为什么刷脏页会导致SQL变慢呢?redo log写满了,要刷脏页,这时候会导致系统所有的更新堵住,写性能都跌为0了,肯定慢呀。一般要杜绝出现这个情况。一个查询要淘汰的脏页个数太多,一样会导致查询的响应时间明显变长。8. order by 文件排序
  order by就一定会导致慢查询吗?不是这样的哈,因为order by平时用得多,并且数据量一上来,还是走文件排序的话,很容易有慢SQL的。听我娓娓道来,order by哪些时候可能会导致慢SQL哈。8.1 order by 的 Using filesort文件排序
  我们平时经常需要用到order by ,主要就是用来给某些字段排序的。比如以下SQL:select name,age,city from staff where city = "深圳" order by age limit 10; 复制代码
  它表示的意思就是:查询前10个,来自深圳员工的姓名、年龄、城市,并且按照年龄小到大排序。
  查看explain执行计划的时候,可以看到Extra这一列,有一个Using filesort,它表示用到文件排序。8.2 order by文件排序效率为什么较低
  order by用到文件排序时,为什么查询效率会相对低呢?
  order by的文件排序,分为全字段排序和rowid排序。它是拿max_length_for_sort_data和结果行数据长度对比,如果结果行数据长度超过max_length_for_sort_data这个值,就会走rowid排序,相反,则走全字段排序。rowid排序
  rowid排序,一般需要回表去找满足条件的数据,所以效率会慢一点。以下这个SQL,使用rowid排序,执行过程是这样:select name,age,city from staff where city = "深圳" order by age limit 10; 复制代码MySQL 为对应的线程初始化sort_buffer,放入需要排序的age字段,以及主键id;从索引树idx_city, 找到第一个满足 city="深圳’条件的主键id,也就是图中的id=9;到主键id索引树拿到id=9的这一行数据, 取age和主键id的值,存到sort_buffer;从索引树idx_city拿到下一个记录的主键id,即图中的id=13;重复步骤 3、4 直到city的值不等于深圳为止;前面5步已经查找到了所有city为深圳的数据,在sort_buffer中,将所有数据根据age进行排序;遍历排序结果,取前10行,并按照id的值回到原表中,取出city、name 和 age三个字段返回给客户端。
  全字段排序
  同样的SQL,如果是走全字段排序是这样的:select name,age,city from staff where city = "深圳" order by age limit 10; 复制代码MySQL 为对应的线程初始化sort_buffer,放入需要查询的name、age、city字段;从索引树idx_city, 找到第一个满足 city="深圳’条件的主键 id,也就是图中的id=9;到主键id索引树拿到id=9的这一行数据, 取name、age、city三个字段的值,存到sort_buffer;从索引树idx_city 拿到下一个记录的主键id,即图中的id=13;重复步骤 3、4 直到city的值不等于深圳为止;前面5步已经查找到了所有city为深圳的数据,在sort_buffer中,将所有数据根据age进行排序;按照排序结果取前10行返回给客户端。
  sort_buffer的大小是由一个参数控制的:sort_buffer_size。如果要排序的数据小于sort_buffer_size,排序在sort_buffer内存中完成如果要排序的数据大于sort_buffer_size,则借助磁盘文件来进行排序。
  借助磁盘文件排序的话,效率就更慢一点。因为先把数据放入sort_buffer,当快要满时。会排一下序,然后把sort_buffer中的数据,放到临时磁盘文件,等到所有满足条件数据都查完排完,再用归并算法把磁盘的临时排好序的小文件,合并成一个有序的大文件。8.3 如何优化order by的文件排序
  order by使用文件排序,效率会低一点。我们怎么优化呢?因为数据是无序的,所以就需要排序。如果数据本身是有序的,那就不会再用到文件排序啦。而索引数据本身是有序的,我们通过建立索引来优化order by语句。我们还可以通过调整max_length_for_sort_data、sort_buffer_size等参数优化;
  大家有兴趣可以看下我之前这篇文章哈:看一遍就理解:order by详解9. 拿不到锁
  有时候,我们查询一条很简单的SQL,但是却等待很长的时间,不见结果返回。一般这种时候就是表被锁住了,或者要查询的某一行或者几行被锁住了。我们只能慢慢等待锁被释放。
  举一个生活的例子哈,你和别人合租了一间房子,这个房子只有一个卫生间的话。假设某一时刻,你们都想去卫生间,但是对方比你早了一点点。那么此时你只能等对方出来后才能进去。
  这时候,我们可以用show processlist命令,看看当前语句处于什么状态哈。10. delete + in子查询不走索引!
  之前见到过一个生产慢SQL问题,当delete遇到in子查询时,即使有索引,也是不走索引的。而对应的select + in子查询,却可以走索引。
  MySQL版本是5.7,假设当前有两张表account和old_account,表结构如下:CREATE TABLE `old_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`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT="老的账户表";  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`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT="账户表"; 复制代码
  执行的SQL如下:delete from account where name in (select name from old_account); 复制代码
  查看执行计划,发现不走索引:
  但是如果把delete换成select,就会走索引。如下:
  为什么select + in子查询会走索引,delete + in子查询却不会走索引呢?
  我们执行以下SQL看看:explain select * from account where name in (select name from old_account); show WARNINGS; //可以查看优化后,最终执行的sql 复制代码
  结果如下:select `test2`.`account`.`id` AS `id`,`test2`.`account`.`name` AS `name`,`test2`.`account`.`balance` AS `balance`,`test2`.`account`.`create_time` AS `create_time`,`test2`.`account`.`update_time` AS `update_time` from `test2`.`account`  semi join (`test2`.`old_account`) where (`test2`.`account`.`name` = `test2`.`old_account`.`name`) 复制代码
  可以发现,实际执行的时候,MySQL对select in子查询做了优化,把子查询改成join的方式,所以可以走索引。但是很遗憾,对于delete in子查询,MySQL却没有对它做这个优化。
  日常开发中,大家注意一下这个场景哈,11、group by使用临时表
  group by一般用于分组统计,它表达的逻辑就是根据一定的规则,进行分组。日常开发中,我们使用得比较频繁。如果不注意,很容易产生慢SQL。11.1 group by的执行流程
  假设有表结构:CREATE TABLE `staff` (   `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT "主键id",   `id_card` varchar(20) NOT NULL COMMENT "身份证号码",   `name` varchar(64) NOT NULL COMMENT "姓名",   `age` int(4) NOT NULL COMMENT "年龄",   `city` varchar(64) NOT NULL COMMENT "城市",   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT="员工表"; 复制代码
  我们查看一下这个SQL的执行计划:explain select city ,count(*) as num from staff group by city; 复制代码
  Extra 这个字段的Using temporary表示在执行分组的时候使用了临时表Extra 这个字段的Using filesort表示使用了文件排序
  group by是怎么使用到临时表和排序了呢?我们来看下这个SQL的执行流程select city ,count(*) as num from staff group by city; 复制代码创建内存临时表,表里有两个字段city和num;全表扫描staff的记录,依次取出city = "X"的记录。判断临时表中是否有为 city="X"的行,没有就插入一个记录 (X,1);如果临时表中有city="X"的行,就将X这一行的num值加 1;遍历完成后,再根据字段city做排序,得到结果集返回给客户端。 这个流程的执行图如下:
  临时表的排序是怎样的呢?
  就是把需要排序的字段,放到sort buffer,排完就返回。在这里注意一点哈,排序分全字段排序和rowid排序如果是全字段排序,需要查询返回的字段,都放入sort buffer,根据排序字段排完,直接返回如果是rowid排序,只是需要排序的字段放入sort buffer,然后多一次回表操作,再返回。11.2 group by可能会慢在哪里?
  group by使用不当,很容易就会产生慢SQL 问题。因为它既用到临时表,又默认用到排序。有时候还可能用到磁盘临时表。如果执行过程中,会发现内存临时表大小到达了上限(控制这个上限的参数就是tmp_table_size),会把内存临时表转成磁盘临时表。如果数据量很大,很可能这个查询需要的磁盘临时表,就会占用大量的磁盘空间。11.3 如何优化group by呢?
  从哪些方向去优化呢?方向1:既然它默认会排序,我们不给它排是不是就行啦。方向2:既然临时表是影响group by性能的X因素,我们是不是可以不用临时表?
  我们一起来想下,执行group by语句为什么需要临时表呢?group by的语义逻辑,就是统计不同的值出现的个数。如果这个这些值一开始就是有序的,我们是不是直接往下扫描统计就好了,就不用临时表来记录并统计结果啦?
  可以有这些优化方案:group by 后面的字段加索引order by null 不用排序尽量只使用内存临时表使用SQL_BIG_RESULT
  大家可以看下我这篇文章哈: 看一遍就理解:group by详解12. 系统硬件或网络资源如果数据库服务器内存、硬件资源,或者网络资源配置不是很好,就会慢一些哈。这时候可以升级硬件配置。这就好比你的计算机有时候很卡,你可以加个内存条什么的一个道理。如果数据库压力本身很大,比如高并发场景下,大量请求到数据库来,数据库服务器CPU占用很高或者IO利用率很高,这种情况下所有语句的执行都有可能变慢的哈。这时候你开始排查是不是出什么问题啦。最后
  如果测试环境的数据库一些参数配置,和生产的环境参数配置不一致啊,也容易产生慢SQL哈。之前见过一个慢SQL的生产案例,就是测试环境用了index merge,所以查看explain执行计划时,是可以走索引的,但是到了生产,确实全表扫描,最后排查发现是生产环境配置把index merge关闭了。
  如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、g,您的支持是我坚持写作最大的动力。

四分钟连造两球,西蒙尼这次还要送走他吗开云伙伴马竞在对阵弱旅加的斯的比赛中再度遭遇了一场爆冷,球队尽管非常顽强地在02落后的情况下连追两球扳平了场上的比分,但在补时第98分钟却被对手绝杀击败,这样的结果还是非常让人失望近郊自驾游,合创汽车带你感受纯电车的百变魅力疫情的远途出行变成了奢望,周末短途郊游也不失为一个好选择,这次我们带着露营烧烤装备,驾驶着入手的新车,出发前往距离北京市中心约130公里的水库,近距离欣赏了高山峡谷景观的美妙,自驾金鸡奖红毯关晓彤翻车,佟丽娅干瘪,柳岩性感,倪妮生图赢麻了每年的红毯都成了大型比美现场,今年也不例外根据不完全统计的是这一次亮相红毯的嘉宾就有数百位女星,用周迅的话说就是好多人!当天红毯现场嘉宾云集,自然是看点多多,跟着小编的脚步来看看吧世界VR产业暨元宇宙博览会开展民众体验科技魅力图为一位七旬老人戴上VR眼镜观看沉浸感十足的家乡风光。刘占昆摄图为一名男童戴着VR眼镜体验火灾逃生现场知识教学。刘占昆摄图为一位男子跟着画面中的影像作出相应动作,体验AR健身的独特詹娜身穿法式豹纹裙外出气质完美裙子性感火辣的味道肯豆小弟帅气头条创作挑战赛大家好我是书香。观赏维密天使超模走秀就是一种享受,肯豆詹娜是卡戴珊家族的金四妹,国际模特最性感漂亮的美少女。热门话题,只要一说起卡戴珊家族,大家都会说说聊聊卡戴珊家族64岁麦当娜穿亮片短裤,舔狗粮碗里的水引发争议老太太就更尴尬了当地时间11月10日,麦当娜在社交媒体上分享了一组美照。令粉丝们高兴的是,这位64岁的资深流行歌手的眉毛终于长回来了。虽然现在还不是特别清楚,但好在比没有的时候好多了,看起来也没那2022年泰国小姐和2022年泰国环球小姐是穿同一件礼服最近,恩法瓦拉哈加冕2022年国际小姐前十名之后与泰国相关媒体进行了交流。出席活动时,2022年国际小姐亚军穿着漂亮礼服,炫耀性感的一环和迷人的曲线。恩法的李子色连衣裙虽很简单,但略胜一头男篮加时进决赛恭喜杜指导,恭喜全新男篮8067进军世锦赛决赛!64平,男篮世锦赛预选赛中国巴林,旗鼓相当的进去了加时赛,老将周鹏确实是实至名归,得益于他的回应球(三分球)的投进,常规赛最后时间才7连败!西部第13!再见勇士,库里5冠梦被击碎,超越詹姆斯太难勇士客场115122不敌国王,遭遇开赛客场7连败。本赛季至今,勇士客场一胜难求,7个客场全输了。据统计,本赛季是勇士队史自1989年以来首次开季客场7连败。198990赛季,勇士开中国篮球目前的问题不是教练问题,而是球员的水平实力问题杜峰真男儿,危难时候勇挑重担,我不认为他比其他名帅水平高,但他有爱国情怀,所以明知烫手山芋还是接了过来,这就是担当!也许卸下国帅,回归俱乐部是他最好的选择。杜指导是不是打压辽宁队,恭喜中国女篮!再获500万奖金,篮协决定获好评,姚明这次做对了11月14日消息,在之前的2022年女篮世界杯上,中国女篮在不被看好的情况下,一路顽强拼搏无惧对抗,时隔28年再次获得亚军,完美展现了无畏金兰的巾帼风采。在女篮夺得亚军之后,球队也
小雨的海南游记之2天涯海角说明()内为爸爸点评这次海南之旅,大家定居后的第一站就是天涯海角,(应为句号)这里是海南最有名也最有意义的景点(之一)。天涯海角,顾名思义,因景区两块巨石分别刻有天涯海角及郭沫若先告别!35岁山东外援要离队!在中国赚3。5亿,进30个联赛进球近几年,金元足球潮水退去,中国足球环境并不好。稳定压倒一切,很多球队运营困难,山东泰山是非常稳定的一支球队。虽然在中超最后争夺战败于武汉三镇,屈居亚军。不过,之后在郝伟的带领下,山山东男篮五连胜不足喜!艰难击败残阵江苏暴露诸多隐患?北京时间1月17日下午,山东男篮迎来了春节前的最后一场联赛,结果,面对只有8人的江苏队,山东男篮打得非常丑陋,尽管最终艰难取胜,但是暴露出了诸多问题,实在是让人担忧。首先,至今没有春节最后一战!山东战胜江苏,豪取五连胜结束第二阶段北京时间1月17日,CBA第二阶段最后一场比赛,山东9491战胜江苏豪取五连胜结束第二阶段的比赛。技术统计山东队乔文瀚三分球10投7中得23分,兰兹博格得22分,吉伦沃特得20分,蔚来ES8对碰小鹏G9,充电续航层面见真招不久前,蔚来全新ES8上市,售价52。8万63。8万元,让这款车的热度再次涨了起来。其实无论是老款ES8还是新款ES8,它的价格已经是妥妥的豪车价格了,按理说各方面应该是做得不错的泰国曼谷彩灯点亮欢乐中国年中国日报曼谷1月16日电(记者杨万丽)当地时间1月16日晚,点亮耀华力春节灯光展在泰国曼谷唐人街开幕,此次春节灯光展将持续至2月15日。中国驻泰国大使馆公使衔参赞杨欣泰国旅游与体育美国黄金储备8133吨,俄罗斯2298吨,日本不足1000吨,我国多少呢老美黄金储备有8000多吨,咱们有多少?中国大妈们手里到底又有多少黄金呢?临近春节,向来都是咱们的黄金销售旺季。老话讲买房不如买田,买田不如买黄金,中国大妈的囤金能力向来很强。可要自主研发与合作联创并举天翼云加速算力基础设施升级1月12日,以南北东西,焕然一新为主题的超聚变新品发布会如期举行。天翼云科技有限公司基础架构事业部副总经理郑文明受邀参会,并发表携手共赢加速算力基础设施升级的主题演讲,分享了天翼云教你怎样把苹果手机通讯录导入到华为先说一段废话,如果不想看可以直接从第二个自然段开始看。为什么要说废话呢?就是凑字,没别的,哈哈。很多人买了新手机不知道如何将旧手机的照片,视频,通讯录导入到新手机,那么我来分享一下CMR研究显示,2023年印度5G智能手机出货量将增长70据印度经济时报1月17日报道,根据网络媒体研究公司(CMR)的一份报告,到2023年底,印度5G智能手机的出货量将同比增长70,5G智能手机的销量将比2020年首次推出时增长13倍别看手机圈没啥新闻,其实各大手机厂商都在憋大招别看手机圈没啥新闻,其实各大手机厂商都在憋大招,打算干票大的。不信我给你盘一盘。如无意外,新年后的第一款旗舰,将由三星给大伙安排。毕竟人家老早就已经官宣了,Galaxy新机将于2月