MySQL性能优化---索引
一、什么是索引
索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存。如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍。
二、索引的分类
1、主键索引
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( […], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。
创建主键索引:
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。
alter table 表名 add primary key (列名);
删除主键索引:
alter table articles drop primary key;
2、查询索引
desc 表名; 不能显示索引名称
show index from 表名
show keys from 表名
3、全文索引
正确用法:
select * from articles where match(title,body) against ( ‘database’)
说明:
在mysql中fulltext 索引只针对 myisam生效
mysql自己提供的fulltext针对英文生效->sphinx (coreseek) 技术处理中文
使用方法是 match(字段名..) against(‘关键字’)
全文索引:停止词, 因为在一个文本中,创建索引是一个无穷大的数,因此,对一些常用词和字符,就不会创建,这些词,称为停止词.比如(a,b,mysql,the)
mysql> select match(title,body) against (‘database’) from articles;(输出的是每行和database的匹配度)
4、唯一索引
这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:
创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
创建表的时候指定索引,例如CREATE TABLE tablename ( […], UNIQUE [索引的名字] (列的列表) );
创建表结构:
create table ddd(id int primary key auto_increment , name varchar(32) unique);
注意:
unique字段可以为NULL,并可以有多NULL, 但是如果是具体内容,则不能重复,
但是不能存有重复的空字符串’’
5、普通索引
普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn)中的数据列创建索引。只要有可能,就应该选择一个数据最整齐、最紧凑的数据列(如一个整数类型的数据列)来创建索引。
6、空间索引
空间索引介于空间操作算法和空间对象之间,它通过筛选作用,大量与特定空间操作无关的空间对象被排除,从而提高空间操作的速度和效率。
三、索引的代价
1.占用磁盘空间;
2.对DML语句的效率影响;
3.增删会对索引影响,因为索引要重新整理;
四、索引的优点
1.通过创建索引,可以在查询数据的过程中,提高系统的性能;
2.通过创建唯一索引,可以保证数据表中每一行数据的唯一性;
3.在使用分组和排序子句进行数据检索时,可以减少查询中分组和排序的时间;
五、索引的缺点
1.创建索引和维护要耗费时间,而且时间随着数据量的增加而增大;
2.索引需要占用物理空间,如果要建立聚簇索引,所需要的空间会更大;
3.在对表中的数据进行增加删除和修改时需要耗费较多的时间,因为索引也要动态维护;
六、在哪些列上可以创建索引
1.经常需要搜索的列;
2.作为主键的列上;
3.经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
4.经常需要根据范围进行搜索的列上;
5.经常需要排序的列上;
6.经常使用在where子句上面的列上;
七、不应该在哪些列上创建索引
1.查询中很少用到的列;
2.对于那些具有特定数据的列,比如性别;
3.对于那些数据量相当大的列;
4.当对修改性能的要求远远大于搜索性能时,因为当增加索引时,会提高搜索性能,但是会降低修改性能;
八、索引的实现原理
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据表中的数据。索引的实现通常使用B树机器变种B+树。
在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
上图展示了一种可能的索引方式。左边是数据表,一共有两列七条数据,最左边的数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在0(log2n)的复杂度内获取到相应数据。
1)B 树
B 树中每个节点包含了键值和键值对于的数据对象存放地址指针,所以成功搜索一个对象可以不用到达树的叶节点。
成功搜索包括节点内搜索和沿某一路径的搜索,成功搜索时间取决于关键码所在的层次以及节点内关键码的数量。
在 B 树中查找给定关键字的方法是:首先把根结点取来,在根结点所包含的关键字 K1,…,kj 查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查的关键字在某个 Ki 或 Ki+1 之间,于是取 Pi 所指的下一层索引节点块继续查找,直到找到,或指针 Pi 为空时查找失败。
2)B+ 树
B+ 树非叶节点中存放的关键码并不指示数据对象的地址指针,非也节点只是索引部分。所有的叶节点在同一层上,包含了全部关键码和相应数据对象的存放地址指针,且叶节点按关键码从小到大顺序链接。如果实际数据对象按加入的顺序存储而不是按关键码次数存储的话,叶节点的索引必须是稠密索引,若实际数据存储按关键码次序存放的话,叶节点索引时稀疏索引。
B+ 树有 2 个头指针,一个是树的根节点,一个是最小关键码的叶节点。
所以 B+ 树有两种搜索方法:
一种是按叶节点自己拉起的链表顺序搜索。
一种是从根节点开始搜索,和 B 树类似,不过如果非叶节点的关键码等于给定值,搜索并不停止,而是继续沿右指针,一直查到叶节点上的关键码。所以无论搜索是否成功,都将走完树的所有层。
B+ 树中,数据对象的插入和删除仅在叶节点上进行。
这两种处理索引的数据结构的不同之处:
a:B 树中同一键值不会出现多次,并且它有可能出现在叶结点,也有可能出现在非叶结点中。而 B+ 树的键一定会出现在叶结点中,并且有可能在非叶结点中也有可能重复出现,以维持 B+ 树的平衡。
b:因为 B 树键位置不定,且在整个树结构中只出现一次,虽然可以节省存储空间,但使得在插入、删除操作复杂度明显增加。B+ 树相比来说是一种较好的折中。
c:B 树的查询效率与键在树中的位置有关,最大时间复杂度与 B+ 树相同(在叶结点的时候),最小时间复杂度为 1(在根结点的时候)。而 B+ 树的时候复杂度对某建成的树是固定的。可以扫描2的次方。
九、创建索引
1、创建普通索引
【create index 索引名称 on 表名(列名)】
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。
CREATE INDEX test_grade ON salgrade(grade)
2、创建唯一索引
【create UNIQUE index 索引名称 on 表名(列名)】
CREATE UNIQUE INDEX test_grade ON salgrade(grade)
3、创建索引实现提高查询速度
3.1 创建一张表emp(数据要多)
3.2 在没有添加索引的情况下查询数据
SELECT * FROM emp WHERE empno='100005'
3.3 添加索引
ALTER TABLE emp ADD INDEX (empno)
3.4 添加索引后再次查询数据
SELECT * FROM emp WHERE empno='100005'
4、为什么索引可以提高查询效率
官方定义,索引就是一种数据结构,可以加快查询速度,因为底层采用B+树算法,常见的查找算法:二分查找,二叉树,哈希散列算法等等
Myisam存储引擎数据文件和索引文件分离,树节点存储数据地址
InnoDB存储引擎数据和索引放在一个文件当中,树节点下的叶子带数据,所以说InnoDB效率上要比Myisam高,减少磁盘IO
建议数据不要过长 从左到右
十、数据查询缓存和缓冲区
1、默认query_cahce是开启的,如果查询缓存开启了,第一次查询数据的时候,读取的是数据文件,第二次会执行查询缓存,所以第二次查询会很快
如果数据更新,需要重新再缓存
SHOW GLOBAL VARIABLES LIKE '%query_cache%'
2、如果表使用的是innodb,第一次查询会走数据文件,第二次会走buffer_pool,也比直接查询要快
SHOW VARIABLES LIKE '%storage_engine%'
3、存储引擎不同,索引生成的文件也不同
①Myisam生成3个文件
frm为表结构文件
MYD是数据文件
MYI是索引文件
②InnoDB生成2个文件
frm为表结构文件
ibd为索引+数据
十一、MySQL explain执行计划解读
1、查询一个没有创建索引的列
EXPLAIN SELECT * FROM emp WHERE ename='sxkHFd'
2、查询一个创建索引的列
EXPLAIN SELECT * FROM emp WHERE empno='100002'
3、EXPLAIN列的解释
①、table
显示这一行的数据是关于哪张表的
②、type
这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
说明:不同连接类型的解释(按照效率高低的顺序排序)
system:表只有一行:system表。这是const连接类型的特殊情况。
const :表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待。
eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用。
ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。
range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况。
index:这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)。
ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免。
③、possible_keys
显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句
④、key
实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引
⑤、key_len
使用的索引的长度。在不损失精确性的情况下,长度越短越好
⑥、ref
显示索引的哪一列被使用了,如果可能的话,是一个常数
⑦、rows
MYSQL认为必须检查的用来返回请求数据的行数
⑧、Extra
关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
说明:extra列返回的描述的意义
Distinct :一旦mysql找到了与行相联合匹配的行,就不再搜索了。
Not exists :mysql优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了。
Range checked for each Record(index map:#) :没有找到理想的索引,因此对从前面表中来的每一个行组合,mysql检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。
Using filesort :看到这个的时候,查询就需要优化了。mysql需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。
Using index :列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。
Using temporary :看到这个的时候,查询需要优化了。这里,mysql需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上。
Where used :使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题。
因此,弄明白了explain语法返回的每一项结果,我们就能知道查询大致的运行时间了,如果查询里没有用到索引、或者需要扫描的行过多,那么可以感到明显的延迟。因此需要改变查询方式或者新建索引。mysql中的explain语法可以帮助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。当然,在大规模数据量时,索引的建立和维护的代价也是很高的,往往需要较长的时间和较大的空间,如果在不同的列组合上建立索引,空间的开销会更大。因此索引最好设置在需要经常查询的字段中。