前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mysql优化-索引

Mysql优化-索引

作者头像
码客说
发布2019-10-21 17:19:44
1.3K0
发布2019-10-21 17:19:44
举报
文章被收录于专栏:码客码客

查看MYSQL版本

代码语言:javascript
复制
select version();

InnoDB引擎的要求MYSQL版本5.6及以上支持全文索引 MyISAM各版本均支持全文索引

概念

Innodb和Myisam是两种类类型

下面介绍一下他们的区别:

区别

Innodb

Myisam

事务

安全

非安全

行级

表级

效率

索引

聚集索引

非聚集索引

外键

支持

不支持

使用环境

需要事务,大量增,改

多查询,不需要事务

下面说说他们的区别 聚集索引:

  1. 属于Innodb。
  2. 按照主键B+树的排列方式存放,子节点存放的就是数据。 如果没有主键,以第一列为聚集索引.
  3. 只有一个聚集索引。
  4. 普通索引指向聚集索引。

非聚集索引:

  1. 属于MyIsam。
  2. 普通索引和非聚集索引没什么区别。
  3. 存放的是地址。

聚集索引与非聚集索引

  1. 聚集索引,常见就是主键,一个表中只能拥有一个聚集索引。一个表中可以拥有多个非聚集索引。
  2. 使用聚集索引的查询效率要比非聚集索引的效率要高,但是如果需要频繁去改变聚集索引的值,写入性能并不高,因为需要移动对应数据的物理位置。
  3. 非聚集索引在查询的时候可以的话就避免二次查询,这样性能会大幅提升。
  4. 不是所有的表都适合建立索引,只有数据量大表才适合建立索引,且建立在选择性高的列上面性能会更好。

单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上; 用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引);

禁用缓存

因为我们要测试添加索引的效果,所以就先禁用缓存,防止影响测试结果。

代码语言:javascript
复制
show global variables like '%query_cache%';
set global query_cache_size=0;
set global query_cache_type=0;

查询表引擎:

代码语言:javascript
复制
show variables like '%storage_engine%';

表引擎使用innodb.第一次查询也会走数据文件,第二次直接走buffer_pool,也比直接查询数据文件要快

哪些字段可以加索引?

  1. 表的主键、外键必须有索引;
  2. 数据量超过300的表应该有索引;
  3. 经常与其他表进行连接的表,在连接字段上应该建立索引;
  4. 经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
  5. 索引应该建在较高选择性的字段上;
  6. 索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
  7. 复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
    • 复合索引中的主列字段,要是使用较高选择性的字段;
    • 复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
    • 如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
    • 如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
    • 如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
  8. 频繁进行数据操作的表,不要建立太多的索引;
  9. 删除无用的索引,避免对执行计划造成负面影响;

较高选择性:就是通过该字段就可以筛选出满足条件的尽可能少的数据

以上是一些普遍的建立索引时的判断依据。 一言以蔽之,索引的建立必须慎重,对每个索引的必要性都应该经过仔细分析,要有建立的依据。 因为太多的索引与不充分、不正确的索引对性能都毫无益处:在表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。 另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的,不但影响查询性能 还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大

索引的生效规则

对于一个不包含子查询的SQL来说,最终只可能有一个索引生效。

独立索引生效规则

  • 一条sql语句只能使用一个索引是错误的。
  • 生效的索引跟建立索引的顺序以及查询的顺序均无关,和字段的选择性有关,较高选择性的字段对应的索引优先生效。

复合索引的生效规则

如果第一个条件不能单独提供较高的选择性,复合索引将会非常有用。

较高选择性:就是通过该字段作为条件就可以筛选出满足条件的尽可能少的数据。

在复合索引中,索引第一位的column很重要,只要查询语句包含了复合索引的第一个条件,基本上就会使用到该复合索引(可能会使用其他索引)。我们在建符合索引的时候应该按照column的重要性从左往右建。

生效规则

  • 多列索引发挥作用,需要满足左前缀要求
  • 只要包含第一个条件,索引都生效,跟顺序无关

以index(a,b,c)为例

语句

索引是否发挥作用

where a=3

where a=3 and b=5

where a=3 and b=5 and c=4

where b=3

where c=4

where a=3 and c=4

a列能用到索引,c不能

where a=3 and b>10 and c=7

a能,b能,c不能

where a=3 and b like ‘xxx%’ and c=7

a能,b能,c不能

where b=5 and c=4 and a=3

where b=5 and c=4

不能

where b=2 and c=3 and d=5 and a=1

复合索引的选择

比如我们有这样一个SQL

代码语言:javascript
复制
select * from userresult where askid=800808 and uid=110996854;

我们有两种复合索引选择:

  1. idx_1 (askid,uid)
  2. idx_2 (uid,askid)

那到底用哪一个呢,利用【sarg】方法:

代码语言:javascript
复制
select sum(askid=800808),sum(uid=110996854) from userresult;

比如结果如下

  1. sum(askid=800808): 6
  2. sum(uid=110996854): 2

因为通过uid过滤后的数据更少,所以我们用idx_2

索引越多越好?

  • 大多数情况下索引能大幅度提高查询效率,但是过多的索引反而会影响速度。
  • 过小的表,建索引可能会更慢哦。
  • 已有某字段为主索引的多列索引,就没必要添加该字段的独立索引。

索引什么时候生效?

生效的情况

  • like ‘xxxx%’
  • <,<=,=,>,>=,BETWEEN

索引什么时候会失效?

  • 最佳左前缀法则
  • 在索引列上做任何操作(计算、函数、(手动或自动)类型转换),会导致索引失效而转向全表扫描
  • 存储引擎不能使用索引中范围条件右边的列
  • <>,not in ,!=
  • is null,is not null
  • like ‘%xxxx%’
  • 字符串不加单引号索引失效(自动类型转换)
  • or左边有索引、右边没索引也会失效

IN是否能用到索引?

IN能使用到索引,但是当表内存在多个单列索引时,MySQL不会自动选择in条件使用的索引,即使它是最优索引。

使用IN查询时,数据为相同类型的数据是可以正常使用索引的

但是,当IN里面嵌套子查询时索引就失效了!

代码语言:javascript
复制
EXPLAIN 
SELECT SQL_NO_CACHE
* 
FROM t_question 
WHERE testid IN (
	SELECT
		questionid
	FROM
		t_question_knowledge_point
	WHERE
		`pointid` = 105
)
LIMIT 0,10;

索引的使用情况

Jietu20190814-122825@2x
Jietu20190814-122825@2x

原因是当使用select之后使用了函数内部转换,mysql是不支持函数索引的。

怎么解决呢?

直接与子查询进行关联,这种写法相当于IN子查询写法,而且效率有不少的提高

代码语言:javascript
复制
EXPLAIN 
SELECT SQL_NO_CACHE
	a.*
FROM
	t_question a,(
	SELECT
		questionid
	FROM
		t_question_knowledge_point
	WHERE
		`pointid` = 105
	) t2
WHERE
	a.testid = t2.questionid
LIMIT 0,10;

索引的使用情况

Jietu20190814-122914@2x
Jietu20190814-122914@2x

不要用下面的写法

代码语言:javascript
复制
EXPLAIN 
SELECT SQL_NO_CACHE
	a.*
FROM
	t_question a LEFT JOIN(
	SELECT
		questionid
	FROM
		t_question_knowledge_point
	WHERE
		`pointid` = 105
	) t2
ON
	a.testid = t2.questionid
LIMIT 0,10;

索引使用情况

Jietu20190814-122640
Jietu20190814-122640

全文索引(LIKE优化)

优化的方式就是建立全文检索FULLTEXT

使用Mysql全文检索FULLTEXT的先决条件

  • MyISAM 引擎表和 InnoDB 引擎表(MySQL 5.6 及以上版本)都支持中文全文检索。
  • 建立全文检索的字段类型必须是char,varchar,text
  • InnoDB引擎的要求mysql版本5.6及以上 MyISAM没有版本约束
  • 表没有进行分区,进行分区的表是无法创建全文索引的

参数设置

#

参数名称

默认值

最小值

最大值

作用

1

ft_min_word_len

4

1

3600

MyISAM 引擎表全文索引包含的最小词长度

2

ft_query_expansion_limit

20

0

1000

MyISAM引擎表使用 with query expansion 进行全文搜索的最大匹配数

3

innodb_ft_min_token_size

3

0

16

InnoDB 引擎表全文索引包含的最小词长度

4

innodb_ft_max_token_size

84

10

84

InnoDB 引擎表全文索引包含的最大词长度

代码语言:javascript
复制
show global variables like 'ft_%'; --查看 MyISAM 引擎表全文检索相关参数
show global variables like 'innodb_ft%'; --查看 InnoDB 引擎表全文检索相关参数

建立全文检索 在建表中用FullText关键字标识字段,已存在的表用 ALTER TABLE (或 CREATE INDEX) 创建索引

代码语言:javascript
复制
CREATE fulltext INDEX index_name ON table_name(colum_name);

比如我们要搜索这样的题目

代码语言:javascript
复制
关于静摩擦力,下列说法正确的是

我们会用到这样的语句

代码语言:javascript
复制
select * from t_question where `docHtml` like '%关于静摩擦力%' limit 0,10;

优化方式 对docHtml2字段添加FULLTEXT 用以下语句查询

代码语言:javascript
复制
SELECT * FROM t_question WHERE MATCH (docHtml) AGAINST ('+关于静摩擦力*' IN BOOLEAN MODE) limit 0,10;

MATCH() 函数的所有参数必须是从来自于同一张表的列,同时必须是同一个FULLTEXT索引中的一部分,除非MATCH()IN BOOLEAN MODE的。

多个字段建立复合全文索引,MATCH() 函数的参数也必须是多个字段

比如 A+B建立索引

代码语言:javascript
复制
SELECT * FROM t_question WHERE MATCH (A,B) AGAINST ('+关于静摩擦力*' IN BOOLEAN MODE) limit 0,10;

FULLTEXT解析器用“ ”(空格)、“,”(逗号)“.”(点号)作为默认的单词分隔符,因此对于不使用这些分隔符的语言如汉语来说FULLTEXT解析器不能正确的识别单词,对于这种情况需做额外处理。

如果我们用AGAINST ('+关于静摩擦' IN BOOLEAN MODE)是搜索不到的, 因为原语句被拆分为了关于静摩擦力下列说法正确的是 要想查询到就要添加*号 如:AGAINST ('+关于静摩擦力*' IN BOOLEAN MODE)。 另外我们想要用AGAINST ('+静摩擦力*' IN BOOLEAN MODE)是搜索不到的,前面加*号也没用。

order by关键字优化

  • 尽量使用index方式排序,避免使用filesort方式。
  • order by满足两种情况会使用index排序:
    • ①、order by语句使用索引最左前列,
    • ②、使用where子句与order by子句条件列组合满足索引最左前列
  • 双路排序:MySQL4.1之前,两次扫描磁盘
  • 单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列进行输出,效率更高一点,但是它会使用更多的空间,因为它把每一行都保存在内存中了

优化策略: 增大sort_buffer_size参数的设置、增大max_length_for_sort_data参数的设置

group by关键字优化

实质是先排序后进行分组,遵照索引键的最佳左前缀,

当无法使用索引列时,增大sort_buffer_size+max_length_for_sort_data参数的设置

怎样查看索引是否生效?

explain显示了MySQL如何使用索引来处理select语句以及连接表。

可以帮助选择更好的索引和写出更优化的查询语句。

使用方法,在select语句前加上explain就可以了:

如:

代码语言:javascript
复制
explain select surname,first_name form a,b where a.id=b.id

EXPLAIN列的解释:

字段

含义

id

id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行,id列为null的就表是这是一个结果集,不需要使用它来进行查询。

select_type

A:simple:表示不需要union操作或者不包含子查询的简单select查询。有连接查询时,外层的查询为simple,且只有一个B:primary:一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary。且只有一个C:union:union连接的两个select查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是unionD:dependent union:与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响E:union result:包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为nullF:subquery:除了from字句中包含的子查询外,其他地方出现的子查询都可能是subqueryG:dependent subquery:与dependent union类似,表示这个subquery的查询要受到外部表查询的影响H:derived:from字句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select

table

显示的查询表名,如果查询使用了别名,那么这里显示的是别名,如果不涉及对数据表的操作,那么这显示为null,如果显示为尖括号括起来的<derived N>就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。如果是尖括号括起来的<union M,N>,与<derived N>类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。

type

依次从好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL,除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引

possible_keys

显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句。

key

查询真正使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。

key_len

用于处理查询的索引长度,如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查询不一定都能使用到所有的列,具体使用到了多少个列的索引,这里就会计算进去,没有使用到的列,这里不会计算进去。留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。要注意,mysql的ICP特性使用到的索引不会计入其中。另外,key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。

ref

如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func。

rows

这里是执行计划中估算的扫描行数,不是精确值。

Extra

关于MYSQL如何解析查询的额外信息。但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢。

filtered

使用explain extended时会出现这个列,5.7之后的版本默认就有这个字段,不需要使用explain extended了。这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。

type列返回的描述的意义

依次从好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL。 除了all之外,其他的type都可以使用到索引。 除了index_merge之外,其他的type只可以用到一个索引。

含义

system

表中只有一行数据或者是空表,且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index

const

使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。其他数据库也叫做唯一索引扫描

eq_ref

出现在要连接过个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null,唯一索引和主键是多列时,只有所有的列都用作比较时才会出现eq_ref

ref

不像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现,常见与辅助索引的等值查找。或者多列主键、唯一索引中,使用第一个列之外的列作为等值查找也会出现,总之,返回数据不唯一的等值查找就可能出现。

fulltext

全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引

ref_or_null

与ref方法类似,只是增加了null值的比较。实际用的不多。

unique_subquery

用于where中的in形式子查询,子查询返回不重复值唯一值

index_subquery

用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。

range

索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中。

index_merge

表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取所个索引,性能可能大部分时间都不如range

index

索引全表扫描,把索引从头到尾扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询、可以使用索引排序或者分组的查询。

all

这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。

Extra列返回的描述的意义

关键词

含义

Distinct

在select部分使用了distinct关键字。

Not exists

MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了。

Range checked for each Record

没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。

Using filesort

排序时无法使用到索引时,就会出现这个。常见于order by和group by语句中。

Using index

查询时不需要回表查询,直接通过索引就可以获取查询的数据。

Using temporary

看到这个的时候,查询需要优化了。创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上。

Using where

表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server层进行过滤。查询条件中分为限制条件和检查条件,5.6之前,存储引擎只能根据限制条件扫描数据并返回,然后server层根据检查条件进行过滤再返回真正符合查询的数据。5.6.x之后支持ICP特性,可以把检查条件也下推到存储引擎层,不符合检查条件和限制条件的数据,直接不读取,这样就大大减少了存储引擎扫描的记录数量。

using sort_union,using_union,using intersect,using sort_intersection

using intersect:表示使用and的各个索引的条件时,该信息表示是从处理结果获取交集using union:表示使用or连接各个使用索引的条件时,该信息表示从处理结果获取并集using sort_union和using sort_intersection:与前面两个对应的类似,只是他们是出现在用and和or查询信息量大时,先查询主键,然后进行排序合并后,才能读取记录并返回。

using join buffer(block nested loop),using join buffer(batched key accss)

5.6.x之后的版本优化关联查询的BNL,BKA特性。主要是减少内表的循环数量以及比较顺序地扫描查询。

firstmatch(tb_name)

5.6.x开始引入的优化子查询的新特性之一,常见于where字句含有in()类型的子查询。如果内表的数据量比较大,就可能出现这个。

loosescan(m..n)

5.6.x之后引入的优化子查询的新特性之一,在in()类型的子查询中,子查询返回的可能有重复记录时,就可能出现这个。

查看索引数据大小

所有表

代码语言:javascript
复制
SELECT  
    TABLE_NAME,  
    concat(truncate(data_length/1024/1024,2),' MB') '库大小',  
    concat(truncate(index_length/1024/1024,2),' MB')  '索引大小'  
FROM  
    information_schema. TABLES  
WHERE  
    TABLE_SCHEMA = 'xhkjedu_ques_new'  
GROUP BY  
    TABLE_NAME  
ORDER BY  
    data_length DESC;

某个表总大小

代码语言:javascript
复制
SELECT  
    TABLE_SCHEMA '库名',  
    concat(TRUNCATE (sum(data_length) / 1024 / 1024,2),' MB')  '库大小',  
    concat(TRUNCATE (sum(index_length) / 1024 / 1024,2),'MB')  '索引大小'  
FROM  
    information_schema. TABLES  
WHERE  
    table_schema = 'xhkjedu_ques_new'  
AND table_name = 't_question';   
GROUP BY  
    TABLE_SCHEMA  
ORDER BY  
    data_length DESC;

某个表每个索引大小

代码语言:javascript
复制
SELECT
       table_name 表名,
       index_name 索引名,
			 sum(stat_value) 数据页页数,
       concat(round(sum(stat_value)* @@innodb_page_size/10000000,2),'M')  大小
FROM
       mysql.innodb_index_stats
WHERE
			 database_name = 'xhkjedu_question'
       AND table_name = 't_question'
       AND stat_description LIKE 'Number of pages in the index'
GROUP BY
       table_name, index_name;

删除无用索引后索引大小不变

当您的库中删除了大量的数据或者索引后,您可能会发现数据文件尺寸并没有减小。这是因为删除操作后在数据文件中留下碎片所致。不但占用空间,还会影响查询速度。

在多数的设置中,您根本不需要运行OPTIMIZE TABLE。即使您对可变长度的行进行了大量的更新,您也不需要经常运行,每周一次或每月一次即可,只对特定的表运行。

注意该操作会锁定表,数据量大时所需时间较长。

代码语言:javascript
复制
optimize table t_question;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查看MYSQL版本
  • 概念
  • 禁用缓存
  • 哪些字段可以加索引?
  • 索引的生效规则
    • 独立索引生效规则
      • 复合索引的生效规则
      • 索引越多越好?
      • 索引什么时候生效?
      • 索引什么时候会失效?
      • IN是否能用到索引?
      • 全文索引(LIKE优化)
      • order by关键字优化
      • group by关键字优化
      • 怎样查看索引是否生效?
      • 查看索引数据大小
      • 删除无用索引后索引大小不变
      相关产品与服务
      云数据库 SQL Server
      腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档