目录

 一 MySQL索引介绍

1. 概念

2.优势

3.劣势

4.使用原则

5. mysql索引结构

6. 哪些情况需要创建索引

7. 哪些情况不需要创建索引

8. 创建索引的语句

9. 删除索引的语句

10. 查看表的索引

 二 mysql索引分类及使用

1. 普通索引

2. 唯一索引

3. 主键索引

4. 组合索引

5. 全文索引

三 Explain

1. 概念

2. 功能

3. 使用方法

 4.执行计划包含的信息

 四 单表索引优化案例分析

五  索引失效

1.联合索引(符合索引)不使用第一部分

 2.where条件有数学运算或函数

 3.mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

4. mysql在使用 is null,is not null也无法使用索引

5. like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作,当%加在右边时可以使用

6. 字符串不加单引号索引失效

 7. where条件使用or


 一 MySQL索引介绍

1. 概念

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。可以得到索引的本质:索引是排好序的快速查找数据结构。一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。

2.优势

  • 所有的MySql列类型(字段类型)都可以被索引,也就是可以给任意字段设置索引。
  • 大大加快数据的查询速度。       
  • 提高数据检索的效率降低数据排序的成本

3.劣势

  • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
  • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
  • 索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或者优化查询。

4.使用原则

  • 对经常更新的表就避免对其设置过多的索引,对经常用于查询的字段应该创建索引。
  • 数据量小的表最好不要使用索引,因为由于数据较少,可能查询全部数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果。
  • 在一个列上(字段上)不同值较少的不要建立索引,比如在学生表的"性别"字段上只有男,女两个不同值。相反的,在一个字段上不同值较多的可是建立索引。

5. mysql索引结构

  • B+树索引
  • Hash索引
  • full-text全文索引
  • R-Tree索引

6. 哪些情况需要创建索引

  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 查询中与其它表关联的字段,外键关系建立索引
  • 频繁更新的字段不适合创建索引,因为每次更新不单单是更新了记录,还会更新索引,加重IO负担
  • where条件里用不到的字段不创建索引
  • 单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  • 查询中统计或者分组字段

7. 哪些情况不需要创建索引

  • 表记录太少
  • 经常增删改的表:虽然提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
  • 数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

8. 创建索引的语句

CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key][index_name](col_name[length])[asc|desc]
  • unique|fulltext为可选参数,分别表示唯一索引、全文索引
  • index和key为同义词,两者作用相同,用来指定创建索引
  • col_name为需要创建索引的字段列,该列必须从数据表中该定义的多个列中选择
  • index_name指定索引的名称,为可选参数,如果不指定,默认col_name为索引值
  • length为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度
  • asc或desc指定升序或降序的索引值存储

9. 删除索引的语句

DROP INDEX 索引名 ON 表名;
## 删除book表中的名称为BkNameIdx的索引
DROP INDEX BkNameIdx ON book;

10. 查看表的索引

## 查看表的索引
SHOW INDEX FROM book;

 二 mysql索引分类及使用

MySQL中分为:普通索引,唯一索引,主键索引,组合索引,和全文索引。

1. 普通索引

是最基本的索引,它没有任何限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。。它有以下几种创建方式:

# 1.直接创建索引
CREATE INDEX index_name ON table(column(length))

# 2.修改表结构的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))

# 3.创建表的时候同时创建索引
CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) CHARACTER NOT NULL ,
    `content` text CHARACTER NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    INDEX index_name (title(length))
)

# 4.删除索引
DROP INDEX index_name ON table

2. 唯一索引

与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:

# 1.创建唯一索引
CREATE UNIQUE INDEX indexName ON table(column(length))

# 2.修改表结构
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))

# 3.创建表的时候直接指定
CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) CHARACTER NOT NULL ,
    `content` text CHARACTER NULL ,
    `time` int(10) NULL DEFAULT NULL ,
     UNIQUE indexName (title(length))
);

3. 主键索引

是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引:

CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
     PRIMARY KEY (`id`)
);

4. 组合索引

指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合

#添加组合索引
ALTER TABLE `table` ADD INDEX name_city_age (id,name,age); 

#创建组合索引
CREATE TABLE tab3(
id INT(4) NOT NULL,
name CHAR(20) NOT NULL,
age INT(3) NOT NULL,
info VARCHAR(255),
INDEX multiIdx(id,name,age)
);

 最左前缀:组合索引遵从了最左前缀,利用索引中最左边的列集来匹配行,这样的列集称为最左前缀。例如,这里由id、name和age3个字段构成的索引,索引行中就按id/name/age的顺序存放,索引组合中的字段可以是(id,name,age)、(id,name)或者(id)。如果要查询的字段不构成最左面的前缀原则,那么就不会用索引,比如,age或者(name,age)组合就不会使用索引查询。

5. 全文索引

主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

# 1.创建表的适合添加全文索引
CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) CHARACTER NOT NULL ,
    `content` text CHARACTER NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    FULLTEXT (content)
);

# 2.修改表结构添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content)

# 3.直接创建索引
CREATE FULLTEXT INDEX index_content ON article(content)

三 Explain

1. 概念

Explain(查看执行计划),使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

2. 功能

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的应用
  • 每张表有多少行被优化器查询

3. 使用方法

Explain+SQL语句,例如

# 创建唯一索引
CREATE TABLE tab1(
id INT(5) NOT NULL,
name CHAR(20) NOT NULL,
UNIQUE INDEX uniqId(id)
);

# 查看索引使用信息
EXPLAIN SELECT * FROM tab1 WHERE id = 1;

bool索引 mysql mysql and 索引_数据库

 4.执行计划包含的信息

bool索引 mysql mysql and 索引_bool索引 mysql_02

id: select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

  • id相同,执行顺序由上至下
  • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  • id相同不同,同时存在

select_type:查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询

  • SIMPLE:简单的select查询,查询中不包含子查询或者UNION。
  • PRIMARY:查询中包含任何复杂的子部分,最外层查询则被标记为PRIMARY。
  • SUBQUERY:在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表里。
  • DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)。MySQL会递归执行这些子查询,把结果放在临时表里。
  • UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED。
  • UNION RESULT:从UNION表中获取结果的SELECT。

table:显示这一行的数据是关于哪些表的。

type显示的是访问类型,是较为重要的一个指标

结果值从最好到最坏依次是:system>const>eq_ref>ref>range>index>All

  • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计。
  • const:表示通过索引一次就找到了,const用于比较primary key或则unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。
  • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
  • ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体。
  • range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不会扫描全部索引。
  • index:Full Index Scan,index与All区别为index类型只遍历索引树。这通常比All快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
  • all:Full Table Scan,将遍历全表以找到匹配的行。

一般来说,得保证查询至少达到range级别,最好能达到ref

possible_keys:显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用。

key:实际使用的索引。如果为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中,不会出现在possible_keys列表中。(覆盖索引:查询的字段与建立的复合索引的个数一一吻合)

key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。查询中与其它表关联的字段,外键关系建立索引。

rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。

Extra:包含不适合在其他列中显示但十分重要的额外信息。

  • Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作成为“文件排序”。
  • Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
  • Using index:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
  • Using where:表明使用了where过滤。
  • Using join buffer:使用了连接缓存。
  • impossible where:where子句的值总是false,不能用来获取任何元组。(查询语句中where的条件不可能被满足,恒为False)
  • select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
  • distinct:优化distinct操作,在找到第一匹配的元组后即停止找相同值的动作。

 四 单表索引优化案例分析

#创建表的SQL语句
CREATE TABLE article(
	id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
	author_id INT(10) UNSIGNED NOT NULL,
	category_id INT(10) UNSIGNED NOT NULL,
	views INT(10) UNSIGNED NOT NULL,
	comments INT(10) UNSIGNED NOT NULL,
	title VARBINARY(255) NOT NULL,
	content TEXT NOT NULL
);

INSERT INTO article(author_id,category_id,views,comments,title,content) 
VALUES(1,1,1,1,'1','1'),(2,2,2,2,'2','2'),(3,3,3,3,'3','3');

 在没有创建索引的时候,使用explain查看执行计划

# 查询category_id=1 且comments>1的情况下,views最多的article_id
EXPLAIN 
SELECT id,author_id 
FROM article 
WHERE category_id=1 
AND comments>1 
ORDER BY views DESC 
LIMIT 1;

 结论:type是ALL,最坏的情况,extra中出现了using filesort,也是最坏的情况,必须进行优化

bool索引 mysql mysql and 索引_sql_03

#开始优化
#创建索引
#alter table article add index idx_article_ccv(category_id,comments,views);
CREATE INDEX idx_article_ccv ON article(category_id,comments,views);

#第二次explain
EXPLAIN 
SELECT id,author_id 
FROM article 
WHERE category_id=1 
AND comments>1 
ORDER BY views DESC 
LIMIT 1;

bool索引 mysql mysql and 索引_数据库_04

 type变成了range,这是可以忍受的,但是extra里using filesort是无法接受的。但是已经创建了索引,为什么没有用?因为按照Btree索引的工作原理,先降序category_id,如果遇到相同的category_id,再排序comments,如果遇到相同的comments,再排序views。当comments字段在联合索引中处于中间位置时,因comments>1条件是一个范围值,故MySQL无法利用索引再对后面的views部分进行检索,即range类型查询字段后面的索引无效。

#删除第一次创建的索引
DROP INDEX idx_article_ccv ON article;

#第2次新建索引
CREATE INDEX idx_article_cv ON article(category_id,views);

#第3次explain
EXPLAIN 
SELECT id,author_id 
FROM article 
WHERE category_id=1 
AND comments>1 
ORDER BY views DESC 
LIMIT 1;

bool索引 mysql mysql and 索引_mysql_05

 结论:type变成ref,extra中的using filesort也消失了,结果非常理想。

五  索引失效

以具体的案例说明失效的情况,首先先创建员工表和索引:

#创建员工表
CREATE TABLE staffs(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(24) NOT NULL DEFAULT "" COMMENT '姓名',
	age INT NOT NULL DEFAULT 0 COMMENT '年龄',
	pos VARCHAR(20) NOT NULL DEFAULT "" COMMENT '职位',
	add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
) CHARSET utf8 COMMENT '员工记录表';

INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW()),('july',23,'dev',NOW()),('2000',23,'dev',NOW());


#创建索引
ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(NAME,age,pos);

1.联合索引(符合索引)不使用第一部分

创建联合索引,但是没有遵循最左前缀法则,则会出现索引失效的情况。如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。(带头大哥不能死,中间兄弟不能断哈哈哈)

bool索引 mysql mysql and 索引_mysql_06

 2.where条件有数学运算或函数

bool索引 mysql mysql and 索引_mysql_07

 3.mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

bool索引 mysql mysql and 索引_sql_08

4. mysql在使用 is null,is not null也无法使用索引

bool索引 mysql mysql and 索引_mysql_09

5. like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作,当%加在右边时可以使用

bool索引 mysql mysql and 索引_数据库_10

6. 字符串不加单引号索引失效

bool索引 mysql mysql and 索引_bool索引 mysql_11

 7. where条件使用or

bool索引 mysql mysql and 索引_sql_12

  注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引