赞
踩
目录
索引 ( index)是帮助MysqL 高效获取数据 的 数据结构 (有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
| |
注意:上述二叉树索引结构的只是一个示意图,并不是真实的索引结构。
优势 | 劣势 |
|
|
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
索引结构 | 描述 |
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引。 |
Hash索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询。 |
R-tree (空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 |
Full-text (全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES。 |
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。
① Hash索引特点:
② 存储引擎支持:在MsaL中,支持hash索引的是Memory引擎,而innoD8中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的。
思考:为什么InnoDB存储引擎选择使用B+Tree索引结构?(面试题)
- 相对于二叉树,层级更少,搜索效率越高。
- 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低。
- 相对Hash索引,B+Tree支持范围匹配以及排序操作。
分类 | 含义 | 特点 | 关键字 |
主键索引 | 针对于表中主键创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 | 含义 | 特点 |
聚集索引 (Clustered lndex) | 将数据存储与索引放到一块,索引结构的叶子节点保存了行数据 | 必须有而且只有一个 |
二级索引(Secondary lndex) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应主键 | 可以存在多个 |
聚集索引选取规则:
回表查询:先根据二级索引拿到主键值,再根据主键值走聚集索引拿到这一行的数据。 |
思考:以下SQL语句中,哪个执行效率高?为什么?
解答:第一个执行效率高。根据 id 直接找聚集(主键)索引构造的B+Tree,直接找到行数据返回;根据 name字段 需要先到二级索引的B+Tree上查找name对应的 primary key 的值,然后再回表查询去聚集索引的B+Tree上查找对应的行数据。
示例练习:根据下列的需求,完成索引的创建
- name 字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
- phone 手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。
- 为 profession、age、status 创建联合索引。
- 为 email 建立合适的索引来提升查询效率。
show index from tb_user;
- -- 1. name 字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
- create index idx_user_name on tb_user (name);
-
- -- 2. phone 手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。
- create unique index idx_user_phone on tb_user(phone);
-
- -- 3. 为 profession、age、status 创建联合索引。
- create index idx_user_pro_age_stu on tb_user(profession,age,status);
-
- -- 4. 为 email 建立合适的索引来提升查询效率。
- create index idx_user_email on tb_user(email);
MySQL客户端连接成功后,通过show [ session|global ] status 命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次: SHOW GLOBAL STATUS LIKE ' Com_ _ _ _ _ ';(一个下划线一个字符)
通过这种方式查看SQL执行频率,为SQL优化提供支撑。
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认100秒)的所有SQL语句的日志。MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置信息:
配置完毕之后,通过以下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息 /var/lib/mysqL/localhost-slow.log。
EXPLAIN 或者 DESC 命令获取MySQL 如何执行 SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序。语法:
EXPLAIN执行计划各字段含义:
> 多对多的多表关联:id相同,执行顺序从上到下
> 子查询( 查询选修了"MySQL"课程的学生 ):id不同,值越大,越先执行
我们发现执行一条数据的查询用时20.78秒,效率极低,原因在于:因为表中 id 为主键,默认主键索引,而 sn字段没有索引,所以效率低。
PS:以上证明了索引对于查询效率的提升。
示例:联合索引idx_user_pro_age_sta
- explain select * from tb_user
- where profession = '软件工程' and age = 31 and status = '0' ;
- explain select * from tb_user
- where profession = '软件工程';
explain select * from tb_user where age = 31 and status = '10';
- explain select * from tb_user
- where age = 31 and status = '0' and profession = '软件工程';
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
- explain select * from tb_user
- where profession = '软件工程'and age > 30 and status = '0';
- explain select * from tb_user
- where profession = '软件工程'and age >= 30 and status = '0';
explain select * from tb_user where substring(phone,10,2) = '15';
- explain select * from tb_user
- where profession='软件工程' and age = 31 and status = 0;
explain select * from tb_user where profession like '软件%';
explain select * from tb_user where profession like '%工程';
explain select * from tb_user where id = 10 or age = 23;
由于age没有索引,即使id有索引,索引也会失效,索引需要针对于age也要建立索引。
通过小例子了解SQL提示:
已知profession查询会用到复合索引,那么如果我们再创建单列索引。
create index idx_user _pro on tb_user (profession);
那么当我们再次查询时,那么将会选择用复合索引还是单列索引呢?
因此:SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
之前我们在基础篇中提到,尽量不要使用select * ,一方面不直观,可读性差,另一方面效率低下。所以尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到) 。
- explain select id,pofession,age,status from tb_user
- where profession = '软件工程' and age = 31 and status = '0';
解释:id,pofession,age之间存在联合索引,属于二级索引,可以拿到我们想要找到的数据直接返回,不需要再查找聚集索引。
- explain select id,profession,age,status, name from tb_user
- where profession = '软件工程' and age = 31 and status = '0';
解释:id,profession,age,status通过二级索引都可以查询到,但是name字段不可以,需要通过id再到聚集索引中进行查找name字段即回表查询。
知识小贴纸:
- using index condition ;查找使用了索引,但是需要回表查询数据。
- using where; using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据。
当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘lO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
select count(distinct ermail) / count(*) from tb_user ; | |
select coint(distinct substring(email,1,5)) / count(*) from tb_user ; |
- -- 创建前缀索引 长度为5
- create index idx_email_5 on tb_user (email(5));
-
- explain select * from tb_user where email = 'daqiao666@sina.com' ;
-
单列索引情况:
- explain select id, phone, name from tb_user
- where phone = '123456789' and name = '张三';
多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询。
而我们想要使用我们创建联合索引,不使用单列索引:
- create unqiue index idx_user_phone_name on tb_user (phone, name) ;
- explain select id,phone, name from tb_user use index(idx_user_phone_name)
- where phone = '123456789' and name = '张三';
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。