索引是帮助MySql高效获取数据的排好序的数据结构
索引的数据结构
二叉树,红黑树,Hash表,B-Tree
创建索引
ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。
ALTER TABLE table_name ADD INDEX index_name (column_list)
ALTER TABLE table_name ADD UNIQUE (column_list)
ALTER TABLE table_name ADD PRIMARY KEY (column_list)
CREATE INDEX可对表增加普通索引或UNIQUE索引。
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
其中table_name是要增加索引的表名,column_list指出对哪些列进行索引;另外,不能用CREATE INDEX语句创建PRIMARY KEY索引。
删除索引
可利用ALTER TABLE或DROP INDEX语句来删除索引。类似于CREATE INDEX语句,DROP INDEX可以在ALTER TABLE内部作为一条语句处理,语法如下。
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY
索引存储方式
- B+Tree(MySql默认索引存储数据结构)
- Hash
对索引的key进行一次hash计算就可以定位出数据存储的位置
很多时候Hash索引要比B+Tree索引高效
仅满足“=”,“IN”,不支持范围查询
hash冲突问题
如图Hash的算法:value % 13 = index
- 扩展B树(注意和B+Tree的区别)
MyISAM和InnoDB实现BTree索引方式的区别
MyISAM存储引擎索引实现
B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
MyISAM索引文件和数据文件是分离的 ;如图:myisam表文件
InnoDB存储引擎索引实现
其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”
InnoDB表数据文件本身就是主索引 如图:innodb表文件
聚集索引的优点
聚集索引查找的速度非常的快,因为整个B+树本身就是的多叉平衡树,叶子节点是有序的,所以找到了索引就等于是找到了相应的数据
聚集索引的缺点
以主键作为B+树索引,成为聚集索引,一张表才一个;
依赖于有序的数据。B+树本身就是多叉平衡树,如果索引的数据不是有序的,那么就需要在插入的时候进行排序,如果数据是整刑还比较好操作,如果是字符串或者是uuid这种又长又难比较的数据,插入和查找的速度肯定就很慢了
更新代价比较大。如果索引列的数据被修改,那么相应的索引也要修改,加上聚集索引的叶子节点上还存放了数据行,修改的代价就更大了。所以对于主键索引来说,一般主键是不允许修改的。
非聚集索引
以主键以外的列值作为键值构建的 B+ 树索引,我们称之为非聚集索引
- 非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表;
- 聚集索引包含非聚集索引键值,并且每个键值项都有指向包含该键值的数据行的指针。
优化索引:减少回表次数,可以使用覆盖索引,为需要查询的字段建立索引,但是不适合select *(字段太多)导致索引文件大,查询性能下降;
为什么建议InnoDB的表要建议设置主键,并且推荐使用整型的自增主键? 如果没有设置主键,InnoDB会选择一个不包含null值的唯一索引作为主键索引;如果没有选到唯一值的索引列,mysql会帮忙建立一个隐藏列,维护一个唯一id,以此来组织索引,那么为了避免 mysql选择索引列和建立隐藏列的性能损耗,建议手动建立一个主键。
索引分类
- 单值(普通)索引:即一个索引只包含单个列,一个表可以有多个单值索引;NORMAL
- 唯一索引:索引列的值必须唯一,单允许空值;UNIQUE
- 全文索引:表示全文收索,在检索长文本的时候,效果最好;FULLTEXT
- 空间索引:MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建;
建议需要创建索引
- 主键自动建立唯一索引(设置表的主键就自动创建索引)
- 频繁作为查询条件的字段应该创建索引
- 查询中与其它表关联的字段,外键关系建立索引
- 频繁更新的字段不适合创建索引,因为每次更新记录还会更新索引
- where条件里用不到的字段不创建索引
- 单键/组合索引的选择(高并发下倾向创建组合索引)
- 查询中排序的字段,排序字段若通过索引访问将提高排序速度
- 查询中统计或者分组字段(orderby 分组前要排序)
不建议创建索引
- 表记录太少
- 经常增删改的表
- 某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果
性能分析
用EXPLAIN关键字,来查看索引是否正在被使用,并且输出其使用的索引信息
**id:**为SELECT的识别符。这是SELECT的查询序列号,如果是子查询,id序号会递增,id的值越大优先级越高。
**select_type:**表示使用SELECT的查询类型,SIMPLE表示为简单的SELECT,不适用于UNION或子查询,就是简单的SELECT。也就是说该SELECT查询时会使用索引。其他取值:**PRIMARY:**最外面的SELECT,在有子查询时,就会出现两个以上的SELECT。
**UNION:**union(两张表连接)中的第二个或后面的select语句。
**SUBQUERY:**在子查询中,第二个SELECT。
**table:**数据表的名字。按照被读取的先后顺序排列,这里只查询一张表,所以只显示book。
**type:**显示的是访问类型,是较为重要的指标,结果值从好到坏(一般大于range就差不多了)system(系统表)>const>eq_ref>ref>range>index>All。
Type | 访问类型 system(系统表)>const>eq_ref>ref>range>index>All |
---|---|
const | 表示通过索引一次就找到了,const用于比较primary key或者unique索引 |
eq_ref | 唯一索引扫描,表中一条记录与之匹配 |
ref | 非唯一索引扫描,返回匹配某个单独值的所有行 |
range | 只检索给定范围的行,使用一个索引来选择行,比index强 |
index | index和All都是读全表,但是index是从索引中读取的,而All是从硬盘中读取的 |
建议: | 最好达到range以上,可做sql优化达到 |
**possible_keys:**显示可能应用在这张表中的索引,一个或者多个。
**key:**实际选用的索引,如果为null则没有使用索引。
key_len:显示了mysql使用索引的长度(也就是使用的索引个数),长度越短越好。注意,key_len的值可以告诉你在联合索引中mysql会真正使用了哪些索引。
ref:用于连接程序使用键的最左前缀或者是该键不是 primary key 或 unique索引;可能的取值有 system、const、eq_ref、index和All。
**rows:**根据表统计信息及索引引用情况,大致估算出找到所需的记录所需要读取的行数。
**extra:**提供了与关联操作有关的信息,没有则什么都不写。
extra | 提供了与关联操作有关的信息 |
---|---|
Using filesort | 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行,Mysql无法利用索引完成的排序操作称为“文件排序” |
Using temporary | 使用了临时表保存中间结果,Mysql在对查询结果排序时使用临时表,常见于排序orderby和分组查询groupby,union等; |
Using index | 表示相应的select操作使用了覆盖索引(Covering Index),避免访问表的数据行,如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有出现using where,表明索引用来读取数据而非执行查找动作; |
Using where | 表示使用了where查询 |
using join buffer | 使用了连接缓存 (使用join多了,配置文件的join buffer可以调大) |
impossible where | where子句的值总是false,不能用来获取元组 |
覆盖索引(Covering Index):索引是可以高效找到行的方法,但是一般数据库也能使用索引找到一个列的数据,因此不必读取整个行;毕竟索引叶子节点(BTree)存储了要索引的数据,当能通过读取索引就可以拿到数据,就不需要读取行了,一个索引包含(覆盖)了满足查询结果的数据就叫做覆盖索引;可以减少回表查询;
案例分析
使用Explain 和 show profile
可以直接使用show profile来查看上一条SQL语句的开销信息
show profile memory for query 2; 如下为MEMORY部分的开销
IN和Exists优化
SQL编写
使用索引
<、>、BETWEEN
走范围索引range。
!=、<>
不走索引
like
模糊查询 前模糊或者 全模糊不走索引
or
条件字段(字段加索引则走索引)
in
走索引,但是当in的查询范围比较大的时候,索引可能会失效,走全表扫描
not in
不走索引
SQL 怎么优化执行效率更高
SQL 优化的原则是:将一次操作需要读取的 BLOCK 数减到最低,即在最短的时间达到最大的数据吞吐量。
调整不良 SQL 通常可以从以下几点切入:
- 检查不良的 SQL,考虑其写法是否还有可优化内容
检查子查询 考虑 SQL 子查询是否可以用简单连接的方式进行重新书写
检查优化索引的使用
考虑数据库的优化器 - 避免出现 SELECT * FROM table 语句,要明确查出的字段。
- 在一个 SQL 语句中,如果一个 where 条件过滤的数据库记录越多,定位越准确,则该 where 条件越应该前移。
- 查询时尽可能使用索引覆盖。即对 SELECT 的字段建立复合索引,这样查询时只进行索引扫描,不读取数据块。
- 在判断有无符合条件的记录时建议不要用 SELECT COUNT (*)和 select top 1 语句。
- 使用内层限定原则,在拼写 SQL 语句时,将查询条件分解、分类,并尽量在 SQL 语句的最里层进行限定,以减少数据的处理量。
- 应绝对避免在 order by 子句中使用表达式。
- 如果需要从关联表读数据,关联的表一般不要超过 7 个。
- 小心使用 IN 和 OR,需要注意 In 集合中的数据量。建议集合中的数据不超过 200个。
- <> 用 < 、 > 代替,>用>=代替,<用<=代替,这样可以有效的利用索引。
- 在查询时尽量减少对多余数据的读取包括多余的列与多余的行。
- 对于复合索引要注意,例如在建立复合索引时列的顺序是 F1,F2,F3,则在 where
或 order by 子句中这些字段出现的顺序要与建立索引时的字段顺序一致,且必须包含第
一列。只能是 F1 或 F1,F2 或 F1,F2,F3。否则不会用到该索引。