ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

10 mysql选错索引

2019-08-06 10:00:31  阅读:255  来源: 互联网

标签:选错 10 mysql 索引 between where 语句 select


10 mysql选错索引

在mysql表中可以支持多个索引,有的sql不指定使用哪个索引,由mysql自己来决定,但是有时候mysql选错了索引,导致执行很慢。

例子

CREATE TABLE `t10` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB;

往表中插入10w记录

过程

delimiter ;;
create procedure idata_t11()
begin
  declare i int;
  set i=1;
  while(i<=100000)do
    insert into t10 values(i, i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata_t11();

分析sql语句

mysql> select * from t where a between 10000 and 20000;

SESSION A

SESSION B

start transaction with consistent snapshot;

 

 

delete from t;

call idata_t11()

 

explain  select * from t10 where a between 10000 and 20000;

commit;

 

Session B的查询语句explain  select * from t10 where a between 10000 and 20000;就不会在选择所有a了,可以通过慢查询日志

set long_query_time=0;
select * from t where a between 10000 and 20000; /*Q1*/
select * from t force index(a) where a between 10000 and 20000;/*Q2*/

优化器的逻辑

选择索引是优化器的工作,而优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句

在数据库里面,扫描行数是影响执行代价的因素之一,扫描的行数越少,意味着访问磁盘数据的次数越少,消耗的cpu资源越少。

当然,扫描行数并不是唯一的判断标准,优化器还会结合是否使用临时表、是否排序等因素综合判断。

扫描行数是怎么判断的?

MySQL在真正开始语句执行之前,并不能精确地知道满足这个条件的记录是多少,只能根据统计信息来估算记录数。

这个统计信息就是索引的”区分度”,显然,一个索引上不同的值越多,这个索引的区分度也就越高,而一个索引上不同的值的个数,称为”基数”,这个基数越大越好。

Mysql采样统计的方法,采样统计的时候,innodb默认会选择N个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数。

而数据表是会持续更新的,索引统计信息也不会固定不变,所以,当变更的行数超过1/M的时候,会自动触发重新做一次索引统计。

在mysql中,有两种存储索引统计的方式,可以通过参数innodb_stats_persitent

(system@127.0.0.1:3306) [test]> show variables like 'innodb_stats_persistent';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_stats_persistent | ON    |
+-------------------------+-------+

--设置为on,表示统计信息会持久化到存储,默认N=20,M=10

--设置为off,表示统计信息只存储在内存中,默认N=8,M=16

其实索引统计只是一个输入,对于一个具体的语句来说,优化器还要判断,执行这个语句要扫描多少行

统计信息不准确,使用analyze table t10;

mysql> explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

索引选择异常和处理

一种方法是,采用force index(t)强行选择一个索引。

第二种方式就是,可以考虑修改语句,引导mysql使用我们期望的索引。

之前优化器选择使用索引b,因为它认为使用索引b可以避免排序(b本身是索引,已经是有序的,如果选择索引b的话,不需要在做排序只需要遍历),所以即使扫描行数更多,也判定为代价更小。

现在使用了 order by b,a,要求按照b,a排序,就意味着使用这两个索引都需要排序,因此扫描行数变成了影响决策的主要条件,此时就选择了a索引。

当然,这种修改并不是通用的优化手段,只是刚好在这个语句有limit 1,如果都有满足条件的行,要逻辑结果一致才可以这么修改。

第三种是在有些场景下,我们可以新建一个更合适的索引,来提供给优化器做选择,或者删掉误用的索引。

标签:选错,10,mysql,索引,between,where,语句,select
来源: https://www.cnblogs.com/yhq1314/p/11307175.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有