java

关于MySQL索引知识与小妙招 — 学到了!

2020-12-19  本文已影响0人  牧小农

一、索引基本知识

1.1 索引的优点

  1. 大大减少了服务器需要扫描的数据量,加快数据库的检索速度
  2. 帮助服务器避免排序和临时表
  3. 将随机io变成顺序io

1.2 索引的用处

  1. 速查找匹配WHERE子句的行
  2. 从consideration中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引
  3. 如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行
  4. 当有表连接的时候,从其他表检索行数据
  5. 查找特定索引列的min或max值
  6. 如果排序或分组时在可用索引的最左前缀上完成的,则对表进行排序和分组
  7. 在某些情况下,可以优化查询以检索值而无需查询数据行

1.3 索引的分类

数据库会默认创建索引,但是并不是给主键建立索引,而是给唯一键建里索引的,因为主键的特性是唯一且非空

1.4 面试技术名词

1.5 索引采用的数据结构

1.5.1 哈希表

缺点︰

1、利用hash存储的话需要将所有的数据文件添加到内存,比较耗费内存空间
2、如果所有的查询都是等值查询,那么hash确实很快,但是在企业或者实际工作环境中范围查找的数据更多,而不是等值查询,因此hash就不太适合了

1.5.2 二叉树

缺点∶

无论是二叉树还是红黑树,都会因为树的深度过深而造成io次数变多,影响数据读取的效率

1.5.3 B+树

B树特点:
1、所有键值分布在整颗树中
2、搜索有可能在非叶子结点结束,在关键字全集内做一次查找,性能逼近二分查找
3、每个节点最多拥有m个子树
4、根节点至少有2个子树
5、分支节点至少拥有m/2颗子树(除根节点和叶子节点外都是分支节点)
6、所有叶子节点都在同一层、每个节点最多可以有m-1个key,并且以升序排列

实例图说明∶
每个节点占用一个磁盘块,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为16和34,P1指针指向的子树的数据范围为小于16,P2指针指向的子树的数据范围为16~34 ,P3指针指向的子树的数据范围为大于34。

查找关键字过程:

  1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
  2. 比较关键字28在区间(16,34 ),找到磁盘块1的指针P2。
  3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
  4. 比较关健字28在区间(25,31 ),找到磁盘块3的指针P2。
  5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O 操作第3次】
  6. 在磁盘块8中的关健宁列表中找到关健字28。

缺点:

1.6 索引匹配方式

全值匹配: 全值匹配指的是和索引中的所有列进行匹配

explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';

匹配最左前缀: 只匹配前面的几列

explain select * from staffs where name = 'July' and age = '23';explain select * from staffs where name = 'July';

匹配列前缀: 可以匹配某一列的值的开头部分

explain select * from staffs where name like 'J%';explain select * from staffs where name like '%y';

匹配范围值: 可以查找某一个范围的数据

explain select * from staffs where name > 'Mary';

精确匹配某一列并范围匹配另外一列:可以查询第一列的全部和第二列的部分

explain select * from staffs where name = 'July' and age > 25;

只访问索引的查询: 查询的时候只需要访问索引,不需要访问数据行,本质上就是覆盖索引

explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';

二、哈希索引

2.1 哈希索引的限制

  1. 哈希索引只包含哈希值和行指针,而不存储字段值,索引不能使用索引中的值来避免读取行
  2. 哈希索引数据并不是按照索引值顺序存储的,所以无法进行排序
  3. 哈希索引不支持部分列匹配查找,哈希索引是使用索引列的全部内容来计算哈希值
  4. 哈希索引支持等值比较查询,也不支持任何范围查询
  5. 访问哈希索引的数据非常快,除非有很多哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中的所有行指针,逐行进行比较,直到找到所有符合条件的行
  6. 哈希冲突比较多的话,维护的代价也会很高

2.2 案例

当需要存储大量的URL,并且根据URL进行搜索查找,如果使用B+树,存储的内容就会很大:select id from url where url=""

也可以利用将url使用CRC32做哈希,可以使用以下查询方式:select id fom url where url="" and url_crc=CRC32("")

此查询性能较高原因是使用体积很小的索引来完成查找

三、组合索引

当包含多个列作为索引,需要注意的是正确的顺序依赖于该索引的查询,同时需要考虑如何更好的满足排序和分组的需要

案例: 建立组合索引 a,b,c ,不同SQL语句使用索引情况

语句 索引是否发挥作用
where a=3 是,只使用了a
where a=3 and b=5 是,使用了a,b
where a =3 and b = 5 and c= 4 是,使用了a,b,c
where a = 3 or c = 4
where a = 3 and c= 4 是,仅使用了a
where a = 3 and b > 10 and c = 7 是,使用了a,b
where a = 3 and b like '%mxn%' and c=7 使用了a

四、聚簇索引与非聚簇索引

4.1 聚簇索引

4.2 非聚簇索引

五、覆盖索引

5.1 基本介绍

  1. 如果一个索引包含所有需要查询的字段的值,我们称之为覆盖索引
  2. 不是所有类型的索引都可以称为覆盖索引,覆盖索引必须要存储索引列的值
  3. 不同的存储实现覆盖索引的方式不同,不是所有的引擎都支持覆盖索引,memory不支持覆盖索引

5.2 优势

1、索引条目通常远小于数据行大小,如果只需要读取索引,那么mysql就会极大的较少数据访问量
2、因为索引是按照列值顺序存储的,所以对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少的多
3、一些存储引擎如MYISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用,这可能会导致严重的性能问题
4、由于INNODB的聚簇索引,覆盖索引对INNODB表特别有用

5.3 案例演示

1、当发起一个被索引覆盖的查询时,在explain的extra列可以看到using index的信息,此时就使用了覆盖索引
2、在大多数存储引擎中,覆盖索引只能覆盖那些只访问索引中部分列的查询。不过,可以进一步的进行优化,可以使用innodb的二级索引来覆盖查询。

例如:actor使用innodb存储引擎,并在last_name字段又二级索引,虽然该索引的列不包括主键actor_id,但也能够用于对actor_id做覆盖查询

六、优化小细节

--创建数据表create table citydemo(city varchar(50) not null);insert into citydemo(city) select city from city;--重复执行5次下面的sql语句insert into citydemo(city) select city from citydemo;--更新城市表的名称update citydemo set city=(select city from city order by rand() limit 1);--查找最常见的城市列表,发现每个值都出现45-65次,select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10;--查找最频繁出现的城市前缀,先从3个前缀字母开始,发现比原来出现的次数更多,可以分别截取多个字符查看城市出现的次数select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10;select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10;--此时前缀的选择性接近于完整列的选择性--还可以通过另外一种方式来计算完整列的选择性,可以看到当前缀长度到达7之后,再增加前缀长度,选择性提升的幅度已经很小了select count(distinct left(city,3))/count(*) as sel3,count(distinct left(city,4))/count(*) as sel4,count(distinct left(city,5))/count(*) as sel5,count(distinct left(city,6))/count(*) as sel6,count(distinct left(city,7))/count(*) as sel7,count(distinct left(city,8))/count(*) as sel8 from citydemo;--计算完成之后可以创建前缀索引alter table citydemo add key(city(7));--注意:前缀索引是一种能使索引更小更快的有效方法,但是也包含缺点:mysql无法使用前缀索引做order by 和 group by。 
create table user(id int,name varchar(10),phone varchar(11));alter table user add index idx_1(phone);explain select * from user where phone=13800001234;(不会触发索引)explain select * from user where phone='13800001234';(触发索引)
上一篇下一篇

猜你喜欢

热点阅读