一、定义
- MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
可以得到索引的本质:索引是数据结构。 - 索引的目的在于提高查询效率,可以类比字典,
如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要a----z,如果我想找到Java开头的单词呢?或者Oracle开头的单词呢? - 可以简单理解为“排好序的快速查找数据结构”。
- 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
思考:delete操作后,索引怎么办?
delete操作实际是将数据激活标志位设为非激活状态,逻辑上不存在,物理上还存在,目的:1.为了保存数据状态,用来做数据分析;2.为了索引。
所以频繁删除、更新的数据不适合建索引。-
我们平常说的索引,如果没有特别指明,都是B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引、复合索引、前缀索引、唯一索引默认都是B+树索引,统称索引。当然除了B+树这种类型的索引之外,还有哈希索引
二、优缺点
1.优点:
- 类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
2.缺点:
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
- 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的.
三、mysql索引分类
1.单值索引
即一个索引只包含单个列,一个表可以有多个单列索引
语法:
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name)
);
单独建单值索引:
CREATE INDEX idx_customer_name ON customer(customer_name);
删除索引:
DROP INDEX idx_customer_name on customer;
2.唯一索引
索引列的值必须唯一,但允许有空值
语法:
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name),
UNIQUE (customer_no)
);
单独建唯一索引:
CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no);
删除索引:
DROP INDEX idx_customer_no on customer ;
3.主键索引
设定为主键后数据库会自动建立索引,innodb为聚簇索引
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id)
);
CREATE TABLE customer2 (id INT(10) UNSIGNED ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id)
);
单独建主键索引:
ALTER TABLE customer
add PRIMARY KEY customer(customer_no);
删除建主键索引:
ALTER TABLE customer
drop PRIMARY KEY ;
修改建主键索引:
必须先删除掉(drop)原索引,再新建(add)索引
4.复合索引
即一个索引包含多个列
语法:
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name),
UNIQUE (customer_name),
KEY (customer_no,customer_name)
);
单独建索引:
CREATE INDEX idx_no_name ON customer(customer_no,customer_name);
删除索引:
DROP INDEX idx_no_name on customer ;
- 增/删/查
创建
CREATE [UNIQUE ] INDEX [indexName] ON table_name(column))
alter mytable add [unique] index [indexname] on (column))
删除
DROP INDEX [indexName] ON mytable;
查看
SHOW INDEX FROM table_name\G
使用ALTER命令
有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。主键索引
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。唯一索引
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
四. Mysql索引结构
Mysql索引结构有BTree索引,Hash索引,full-text全文索引,R-Tree索引,这里主要研究BTree索引。
【初始化介绍】
一颗b树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),
如磁盘块1包含数据项17和35,包含指针P1、P2、P3,
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
五、哪些情况适合建索引
1.适合建立索引
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引(如银行卡号,电信部门手机号)
- 查询中与其它表关联的字段,外键关系建立索引(如:员工表和部门表id)
- 单键/组合索引的选择问题, 组合索引性价比更高(在高并发下倾向于创建组合索引)
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度(排序字段顺序和索引建立的顺序一致)
- 查询中统计或者分组字段
2.不适合建立索引
表记录太少
经常增删改的表,提高了查询速度,同时会降低更新表的速度,如对表进行insert,update和delete,因为每次更新不单单是更新了记录,还会更新索引保存一下索引文件。
-
数据重复且分布平均的表字段(如国籍)
假如有一个表10万行记录,有一个字段A只有True和False两种值,且每个值得分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
索引的选择性是指索引列中不同值得数目与表中记录数得比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99。一个索引的选择性越接近于1,这个索引的效率就越高。 where条件里用不到的字段不创建索引
六、优化性能分析
1.Mysql Query Optimizer(自带的优化器)
Mysql中有专门负责优化select语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供它认为最优的执行计划(它认为最优的数据检索方式,但不见得是DBA认为最优的)
当客户端想Mysql请求一条Query,命令解析器模块完成请求分类,区别出是select并转发给Mysql Query Optimizer时,Mysql Query Optimizer首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对Query中的查询条件进行简化和转换,如去掉一些无用或者显而易见的条件、结构调整等。然后分析Query中的Hint信息(如果有),看显示Hint信息是否可以完全确定Query的执行计划。如果没有Hint或者Hint信息还不足以完全确定执行计划,则会读取涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划。
2.Mysql 常见瓶颈
- cpu在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
- IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
- 服务器硬件性能瓶颈:top, free, iostat 和vmstat来查看系统的性能状态
3.Explain执行计划
1.定义
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是
如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈
官网介绍: http://dev.mysql.com/doc/refman/5.5/en/explain-output.html
2.explain能干嘛
- 表的读取顺序
- 哪些索引可以使用
- 数据读取操作的操作类型
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
3.explain 的使用
explain+select语句
mysql> explain select * from customer;
+----+-------------+----------+-------+---------------+-------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+-------------+---------+------+------+-------------+
| 1 | SIMPLE | customer | index | NULL | customer_no | 406 | NULL | 1 | Using index |
+----+-------------+----------+-------+---------------+-------------+---------+------+------+-------------+
建表测试:
CREATE TABLE t1(id INT(10) AUTO_INCREMENT,content VARCHAR(100) NULL , PRIMARY KEY (id));
CREATE TABLE t2(id INT(10) AUTO_INCREMENT,content VARCHAR(100) NULL , PRIMARY KEY (id));
CREATE TABLE t3(id INT(10) AUTO_INCREMENT,content VARCHAR(100) NULL , PRIMARY KEY (id));
CREATE TABLE t4(id INT(10) AUTO_INCREMENT,content VARCHAR(100) NULL , PRIMARY KEY (id));
INSERT INTO t1(content) VALUES(CONCAT('t1_',FLOOR(1+RAND()*1000)));
INSERT INTO t2(content) VALUES(CONCAT('t2_',FLOOR(1+RAND()*1000)));
INSERT INTO t3(content) VALUES(CONCAT('t3_',FLOOR(1+RAND()*1000)));
INSERT INTO t4(content) VALUES(CONCAT('t4_',FLOOR(1+RAND()*1000)));
字段解释:
1)id
-
id相同,执行顺序由上至下
-
id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- id相同不同,同时存在
id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
衍生 = DERIVED, 衍生虚表s3。
id号每个号码,表示一趟独立的查询。一个sql 的查询趟数越少越好。
2)select_type
查询的类型,主要是用于区别 普通查询、联合查询、子查询等的复杂查询
-
SIMPLE
简单的 select 查询,查询中不包含子查询或者UNION
-
PRIMARY
查询中若包含任何复杂的子部分,最外层查询则被标记为Primary
衍生表a是最外层查询,标记为primary。
-
DERIVED
在FROM列表中包含的子查询被标记为DERIVED(衍生) MySQL会递归执行这些子查询, 把结果放在临时表里。
-
SUBQUERY
在SELECT或WHERE列表中包含了子查询
-
DEPENDENT SUBQUERY
在SELECT或WHERE列表中包含了子查询,子查询基于外层
- UNCACHEABLE SUBQUREY
DEPENDENT SUBQUERY查询不同于UNCACHEABLE SUBQUREY查询。对于DEPENDENT SUBQUERY查询,对于来自其外部上下文的变量的每组不同值,子查询只重新计算一次。对于UNCACHEABLE SUBQUREY,将对外部上下文的每一行重新评估子查询。
-
UNION
若第二个SELECT出现在UNION之后,则被标记为UNION; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
- UNION RESULT
从UNION表获取结果的SELECT
3)table
显示这一行的数据是关于哪张表的
4) type
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge >
unique_subquery > index_subquery > range > index > ALL
工作中常见:
system>const>eq_ref>ref>range>index>ALL
一般来说,得保证查询至少达到range级别,最好能达到ref。
显示查询使用了何种类型, 从最好到最差依次是: system>const>eq_ref>ref>range>index>ALL
system
表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计-
const (常量)
表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快 如将主键置于where列表中,MySQL就能将该查询转换为一个常量
-
eq_ref
唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
t1加载过来进行全表扫描,t2中只有一条记录匹配。(ceo)
-
ref
非唯一性索引扫描,返回匹配某个单独值的所有行. 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而, 它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
-
range
只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引 一般就是在你的where语句中出现了between、<、>、in等的查询 这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。
-
index
出现index是sql使用了索引但是没用通过索引进行过滤,一般是使用了覆盖索引或者是利用索引进行了排序分组
- all
Full Table Scan,将遍历全表以找到匹配的行
-
index_merge
在查询过程中需要多个索引组合使用,通常出现在有 or 的关键字的sql中
-
ref_or_null
对于某个字段既需要关联条件,也需要null值得情况下。查询优化器会选择用ref_or_null连接查询。
-
index_subquery
利用索引来关联子查询,不再全表扫描。
- unique_subquery
该联接类型类似于index_subquery。 子查询中的唯一索引
备注:一般来说,得保证查询至少达到range级别,最好能达到ref。
- possible_keys
显示可能应用在这张表中的索引,一个或多个。 查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用.
6)key
实际使用的索引。如果为NULL,则没有使用索引
查询中若使用了覆盖索引,则该索引和查询的select字段重叠
possible_keys理论上用到了,key实际上用到了。
- 理论上用到了,实际上没有用到,索引失效
- 理论上没有用到,实际上用到了,覆盖索引
- 理论上用到了,实际上也用到了
7)key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。 在不损失精确性的情况下,长度越短越好。
key_len显示的值为索引
key_len字段能够帮你检查是否充分的利用上了索引
是如何计算的呢?
1 、先看索引上字段的类型+长度比如 int=4 ; varchar(20) =20 ; char(20) =20
2 、如果是varchar或者char这种字符串字段,视字符集要乘不同的值,比如utf-8 要乘 3,GBK要乘2,
3 、varchar这种动态字符串要加2个字节
4、 允许为空的字段要加1个字节
第一组
key_len=age的字节长度+name的字节长度=4+1 + ( 20*3+2)=5+62=67
第二组
key_len=age的字节长度=4+1=5
8)ref
显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值
9)rows
rows列表示Mysql认为执行查询必须检查的行数。对于InnoDB表,这个数字只是一个估计值,可能并不总是准确的。
越少越好
- extra
包含不适合在其他列中显示但十分重要的额外信息
-
using filesort
出现filesort的情况
优化后,不再出现filesort的情况:
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。 MySQL中无法利用索引完成的排序操作称为“文件排序”
- using temporary
优化前存在 using temporary 和 using filesort
优化前存在 using temporary 和 using filesort 不在,性能发生明显变化:
使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
- using index
表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错! 如果同时出现using where,表明索引被用来执行索引键值的查找; 如果没有同时出现using where,表明索引只是用来读取数据而非利用索引执行查找。
利用索引进行了排序或分组
- using where
表明使用了where过滤
-
using join buffer
使用了连接缓存:
-
impossible where
where子句的值总是false,不能用来获取任何元组
- select tables optimized away
在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者 对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算, 查询执行计划生成的阶段即完成优化。
在Innodb中
在Myisam中