MySQL基础(六)-索引(index)详解
目录
三、在MySQL中,主键、unique字段上会自动添加索引。
一、什么是索引?
索引实在数据库表的字段上添加的,是为了提高查询效率存在的一种数据结构。
一张表的一个字段可以添加一个索引,当然,多个字段也可以联合起来添加索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
比如我们通过字典查找某个汉字的时候,有两种方法:
第一种方法:
一页一页找,知道找到我们需要的内容为止。这种查找方式属于全字典扫描,效率比较低。
第二种方法:
先通过目录(索引)去定位一个大概的位置,然后直接定位到这个位置,做局域性扫描,缩小扫描的范围,快速的查找。这种查找方式属于通过索引检索,效率较高。
如:
因为添加了查询条件"name=jack",所以我们会去name字段上扫描。
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描。会将name字段上的每一个值都比对一遍,效率较低。
MySQL在查询方面主要就是两种方式:
第一种方式:全表扫描
第二种方式:根据索引检索
注意:
在实际中,字典的目录是排序的,按照 a b c ....进行排序。因为只有排序了才有区间查找的概念。缩小扫描范围实际就是扫描某个区间。
在MySQL数据库中索引也是需要索引的,并且这个索引的排序和TreeSet数据结构相同。在MySQL中索引是一个B-Tree数据结构。
遵循左小右大原则存放,采用中序遍历方式遍历取数据。
二、索引的实现原理
注意1:在任何数据库当中,主键上都会自动添加索引对象。另外在MySQL中,一个字段上如果有unique约束的话,也会自动创建按索引对象。
注意2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
注意3:在MySQL中,索引是一个单独的对象。不同的存储引擎以不同的形式存在。但不过关索引存储在哪里,索引在MySQL中都是以一个树的形式存在(自平衡二叉树:B-Tree)
二叉树数据结构的弊端:
当极端情况下,数据递增插入时,会一直向右插入,形成链表,查询效率会降低。
MySQL中常用的索引数据结构有:
- BTree索引(Myisam普通索引)
- B+Tree索引(InnoDB普通索引)
- Hash索引(memory存储引擎)等等
储存引擎 | 存储位置 |
MyISAM | .MYI |
InnoDB | tablespace |
MEMORY | 内存 |
索引的实现原理:缩小扫描范围,避免全表扫描。
索引的优势:
提高数据检索的效率,降低数据库的IO成本。
通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
索引的劣势:
索引实际上也是一张表,保存了主键和索引的字段,并且指向实体表的记录,所以索引也是需要占用空间的。
再索引大大提高查询速度的同时,却会降低表的更新速度,在对表进行数据增删改的同时,MySQL不仅要更新数据,还需要保存一下索引文件。每次更新添加了索引列的字段,都会区调整因为更新带来的简直变化后的索引的信息。
三、索引的使用场景
在MySQL中,主键、unique字段上会自动添加索引。
在什么条件下,我们会考虑给字段添加索引呢?
条件1:数据量庞大
条件2:该字段经常出现在where的后面,以条件的形式存在。即这个字段总是被扫描。
条件3:该字段很少的DML操作。因为DML之后索引需要重新排序
建议不要随意添加索引,因为索引也是需要维护的。太多的话反而会降低系统的性能。
建议通过主键查询,通过unique约束的字段进行查询,效率较高。
不推荐建立索引:
1、表记录太少
2、经常增删改的表
3、where条件里用不到的字段不建立索引
四、索引的创建与删除
创建索引:
给emp表的ename字段添加索引,起名:emp_ename_index
create index emp_ename_index on emp(ename);
alter table emp add index emp_ename_index(ename);
删除索引:
将emp表上的emp_ename_index索引对象删除
drop index emp_ename_index on emp;
五、查看SQL语句是否使用了索引 - explain
使用explain关键字可以用来分析查询语句或者表的结构的性能瓶颈。其作用:
- 表的读取顺序
- 哪些索引可以使用
- 数据读取操作的操作类型
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
mysql> explain select * from test where id=1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | test | NULL | ALL | NULL | NULL | NULL | NULL | 7 | 14.29 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
# 给id字段添加索引
mysql> create index id_index on test(id);
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from test where id=1;
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test | NULL | ref | id_index | id_index | 5 | const | 4 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
扫描7条记录,说明没有使用索引。
type=ALL:未使用索引
type=ref:使用索引
explain重要字段名
id:
select查询的序列号,表示查询中执行select子句或操作表的顺序。
- id相同时,执行顺序由上至下。
- id不同,如果是子查询,id序号会递增,id值越大优先级越高,则越先被执行。
- id相同和不同都存在时,id相同的可以理解为一组,从上往下顺序执行。所有组中,id值越大,优先级越高越先执行。
select_type:
查询的类型,常见值有如下,
- simple:简单的select查询,查询中不包括子查询或者union。
- primary:查询中若包含任何复杂的子部分,最外层查询则被标记为primary
- derived:在from列表中包含的子查询被标记为derived(衍生),MySQL会递归执行这些子查询,把结果放在临时表里。
- subquery:在select或where列表中包含了子查询。
table:
显示这一行的数据是关于哪张表的。
type:
访问类型排序。最好到最差依次是system>const>eq_ref>ref>range>index>all
- System:表只有一行记录(等于系统表)是const的特例,可以忽略不计。
- Const:通过索引一次就找到了,用于比较primary key或unique索引。因为只匹配一行数据,所以很快。
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
- Ref:非唯一性索引扫描,返回匹配某个单独值的所有行。
- range:只检索给定范围的行,使用一个索引来选择行。
- index:full index scan。index与all的区别:index类型只遍历索引树。通常比all快,因为索引文件通常比数据文件小。也就是说,虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读取的。
- all:full table scan。将遍历全表一找到匹配的行。
possible——keys:
显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上如果存在索引,则该索引将会被列出来,但不一定会被查询实际使用上。
key:
查询中实际使用的索引,如果为null,则没有使用索引。
ref:
显示索引的哪一列被使用了。哪些列或常量被用于查找索引列上的值。
rows:
显示MySQL认为它执行查询时必须检查的行数。一般越少越好。
extra:
- using filesort:MySQL无法利用索引完成的排序操作称为“文件排序”
- using temporary:MySQL在对查询结果排序时使用品是表,常见于排序order by和分组查询group by
- using index:表示索引被用来执行索引键值的查找,避免访问了表的数据行,效率不错。
- using where:表示使用了where过滤。
六、索引的失效
失效的第一种情况:模糊匹配当中以“%”开头
select * from emp where ename like '%T';
在上述代码段中,ename上即使添加了索引,也不会进行索引。
原因是模糊匹配当中以“%”开头。所以尽量避免在模糊查询的时候以“%”开头,作为一种优化的手段/策略。
失效的第二种情况:使用or
如果使用or,那么要求or两边的条件字段都要有索引,才会进行索引。如果其中一边有一个字段有索引,另一个字段没有索引,那么另一个字段上的索引也会失效。
所以这就是不建议使用or的原因。
失效的第三种情况:使用复合索引时,没有使用左侧的列查找
复合索引的概念:
两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
如:
create index emp_job_sal_index on emp(job,sal);
失效的第四种情况:在where当中索引列列添加了运算
explain select * from emp where sal+1=800;
失效的第五种情况:在where中,索引列使用了函数
explain select * from emp where lower(ename)='smith';
七、索引的分类
索引是各种数据库进行优化的重要手段。优化的时候优先考虑的因素就是索引。
索引的分类:
单一索引:一个字段上添加索引。
复合索引:两个字段或者更多的字段上添加索引。
主键索引:主键上添加索引。
唯一性索引:具有unique约束的字段上添加索引。
......
注意:唯一性比较弱的字段上,添加索引用处不大。
八、排序优化
- 尽量避免使用using filesort方式排序。
- order by语句使用索引最左前列或使用where子句与order by自己条件组合满足索引最做前列。
- where子句中如果出现索引范围查询会导致order by索引失效。