8、索引

1、索引不是万能

  • 数据行数少的状况下,索引效率没什么做用
  • 当数据重复度大,好比高于 10% 的时候,也不须要对这个字段使用索引

2、索引类型

1.功能逻辑分类

普通索引
  • 基础的索引,没有任何约束,主要用于提升查询效率
惟一索引
  • 在普通索引的基础上增长了数据惟一性的约束
  • 在一张数据表里能够有多个惟一索引
主键索引
  • 在惟一索引的基础上增长了不为空的约束,也就是 NOT NULL+UNIQUE
  • 一张表里最多只有一个主键索引
全文索引
  • 用的很少
  • MySQL 自带的全文索引只支持英文
  • 用专门的全文搜索引擎更好(ElasticSearch和Solr)

 2.物理实现分类

汇集索引
  • 有且只能有一个
  • 表中数据行按索引的排序方式进行存储,影响数据表的物理存储顺序,对查找行颇有效
  • 叶子节点存储的就是咱们的数据记录
  • 只有当表包含汇集索引时,表内的数据行才会按找索引列的值在磁盘上进行物理排序和存储
  • 每个表只能有一个汇集索引,由于数据行自己只能按一个顺序存储
  • 相比非汇集索引查询更高效
非汇集索引
  • 能够没有,也有多个
  • 又称二级索引或者辅助索引
  • 单独的存储空间存放非汇集索引
  • 索引项是按照顺序存储的,索引项指向的内容是随机存储的
  • 每一个非汇集索引保存的数据都会存储主键值
  • 两次查找,第一次先找到索引,第二次找到索引对应的位置取出数据行
  • 非汇集索引不会把索引指向的内容像汇集索引同样直接放到索引的后面,而是维护单独的索引表
  • 只维护索引,不维护索引指向的数据
  • 相比汇集索引,插入,删除,更新等操做效率更高

 3.字段个数分类

单一索引 索引列为一列
联合索引 多个列组合在一块儿建立

4.索引片中包含的匹配列的数量不一样

窄索引 包含索引列数为 1 或 2
宽索引 包含的索引列数大于 2)

3、联合索引的最左原则

联合索引(x,y,z),索引查询,必须出现由左到右的查询条件才有效(不是编写SQL的where顺序,是要查y的时候,必有x做为查询条件才能够),从左到右的使用索引中的字段sql

4、为何用B+树来作索引

名称 树形 局限性
二叉树

  1. 可能深度很是大
  2. 有时候还不靠谱,成了链表
平衡二叉树
  1. 增长约束每一个节点左子树和右子树的高度不能超过1
  2. 当 n 比较大时,深度也是比较高的
  3. 执行增删操做,不知足上述条件就要经过旋转来保持平衡
  4. 旋转是很是耗时的,因此AVL树适合用于查找多的状况
BTree
  1. 分叉从2变成M阶
  2. 下降了深度查询的次数
  3. 非叶子节点也会存储数据,查询效率不稳定
B+Tree
  1. 中间节点并不直接存储数据
  2. 每次只有访问到叶子节点才能找到对应的数据
  3. 查询效率更稳定
  4. 树更矮胖(阶数更大深度更低)、查询效率更高
  5. 叶子节点经过有序链表进行了连接范围查询上大大提升
  • 磁盘的 I/O 操做次数对索引的使用效率相当重要
  • 虽然传统的二叉树数据结构查找数据的效率高,但很容易增长磁盘 I/O 操做的次数,影响索引使用的效率
  • 在构造索引的时候,更倾向于采用“矮胖”的数据结构
  • B 树和 B+ 树均可以做为索引的数据结构,在 MySQL 中采用的是 B+ 树,B+ 树在查询性能上更稳定,在磁盘页大小相同的状况下,树的构造更加矮胖,所须要进行的磁盘 I/O 次数更少
  • 子节点进行了链表连接更适合进行关键字的范围查询 

5、Hash索引

  • Hash 自己是一个函数,又被称为散列函数,它能够帮助咱们大幅提高检索数据的效率
  • 只须要一次交互就能够完成查找,效率很是高
  Hash B+Tree
范围查询
  • 不支持,索引无序
  • 支持,叶子节点是有序链表
联合索引的最左侧原则
  • 联合索引的部分索引没法使用
  • Hash 索引在计算 Hash 值的时候是将索引键合并后再一块儿计算 Hash 值,因此不会针对每一个索引单独计算 Hash 值
  • 支持,联合左向右关联
ORDER BY 排序
  • Hash 索引指向的数据是无序的,所以没法起到排序优化的做用
  • B+ 树索引数据是有序的,能够起到对该字段 ORDER BY 排序优化的做用
模糊查询
  • 不支持,同上
  • 支持,xxx%
等值查询
  • 效率更高
  • 索引列的重复值若是不少,效率就会下降
  • Hash 索引一般不会用到重复值多的列上
  • 效率比不上Hash

MySQL 中的 Memory 存储引擎支持 Hash 存储,其余不支持,如InnoDB数据库

6、Mysql 自适应 Hash 索引

MySQL 的 InnoDB 存储引擎还有个“自适应 Hash 索引”的功能,就是当某个索引值使用很是频繁的时候,它会在 B+ 树索引的基础上再建立一个 Hash 索引,这样让 B+ 树也具有了 Hash 索引的优势,这个功能不须要人工干预数组

  1. 自适应哈希索引只保存热数据(常常被使用到的数据),并不是全表数据。所以数据量并不会很大,可让自适应Hash放到缓冲池中,也就是InnoDB buffer pool,进一步提高查找效率
  2. InnoDB中的自适应Hash至关因而“索引的索引”,采用Hash索引存储的是B+树索引中的页面的地址
  3. 自适应Hash采用Hash函数映射到一个哈希表中,因此对于字典类型的数据查找很是方便哈希表是数组+链表的形式

InnoDB自己不支持Hash,可是提供自适应Hash索引,不须要用户来操做,而是存储引擎自动完成的。自适应Hash也是InnoDB三大关键特性之一数据结构

7、建立索引规律

索引太多了,在更新数据的时候,若是涉及到索引更新,就会形成负担函数

须要建立索引的状况性能

  1. 字段的数值有惟一性的限制
  2. 频繁做为 WHERE 查询条件的字段,尤为在数据表大的状况下
  3. 须要常常 GROUP BY 和 ORDER BY 的列
  4. 多个单列索引在多条件查询时只会生效一个索引(MySQL 会选择其中一个限制最严格的做为索引),多条件联合查询的时候最好建立联合索引
  5. UPDATE、DELETE 的 WHERE 条件列,通常也须要建立索引
  6. DISTINCT 字段须要建立索引

不须要建立索引的状况优化

  1. WHERE 条件(包括 GROUP BY、ORDER BY)里用不到的字段不须要建立索引
  2. 若是表记录太少,好比少于 1000 个,那么是不须要建立索引的
  3. 字段中若是有大量重复数据,也不用建立索引
  4. 频繁更新的字段不必定要建立索引,更新数据的时候,也须要更新索引,若是索引太多,在更新索引的时候也会形成负担,从而影响效率

索引失效的状况搜索引擎

  1. 若是索引进行了表达式计算,则会失效
  2. 若是对索引使用函数,会形成失效
  3. 在 WHERE 子句中,若是在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效,由于 OR 的含义就是两个只要知足一个便可,所以只有一个条件列进行了索引是没有意义的,只要有条件列没有进行索引,就会进行全表扫描,所以索引的条件列也会失效
  4. 当咱们使用 LIKE 进行模糊查询的时候,前面不能是 %,如%xxx
  5. 索引列尽可能设置为 NOT NULL 约束
  6. 联合索引的时候要注意最左原则

8、索引片和过滤因子

  1. SQL 查询语句在执行中须要扫描的一个索引片断
  2. 索引片越宽,须要顺序扫描的索引页就越多
  3. 索引片越窄,就会减小索引访问的开销

经过宽索引避免回表

学生表(学号,姓名,课程ID,课程名),主键(学号)spa

select 学号,姓名,课程ID,课程名 from 学生表 where 课程ID in (1,2,3);

窄索引(课程ID)设计

  1. 经过窄索引,找到主键(定位数据行)
  2. 根据主键(数据行),回表查找相应数据
  3. 取出select须要的数据

宽索引(课程ID,姓名,课程名)

  • 直接经过宽索引找到select须要的数据

优势:经过宽索引将 SELECT 中须要用到的列(主键列能够除外)都设置在宽索引中,这样就避免了回表扫描的状况

缺点:宽索引须要顺序扫描的索引页不少

过滤因子

  • 描述了谓词的选择性。在 WHERE 条件语句中,每一个条件都称为一个谓词,谓词的选择性也等于知足这个条件列的记录数除以总记录数的比例

学生表(学号,姓名,课程ID,性别,年龄

  • 假设重复率:性别50%,姓名14%,课程ID28%,年龄43%
  • 性别,年龄不是好过滤因子
  • 联合过滤因子有更高的过滤能力
  • 须要注意一个条件,那就是条件列的关联性应该尽可能相互独立,不然若是列与列之间具备相关性,联合过滤因子的能力就会降低不少
  • 好比城市名称和电话区号就有强相关性,这两个列组合到一块儿不会增强过滤效果
  • 过滤因子决定了索引片的大小
  • 过滤因子的条件过滤能力越强,知足条件的记录数就越少,SQL 查询须要扫描的索引片也就越小
  • 若是没有选择好索引片中的过滤因子,就会形成索引片中的记录数过多的状况

三星索引

  1. 在 WHERE 条件语句中,找到全部等值谓词中的条件列,将它们做为索引片中的开始列
  2. 将 GROUP BY 和 ORDER BY 中的列加入到索引中
  3. 将 SELECT 字段中剩余的列加入到索引片中
  • 经过索引查找符合条件的记录,就须要将 WHERE 子句中的等值谓词列加入到索引片中,这样索引的过滤能力越强,最终扫描的数据行就越少
  • 要对数据记录分组或者排序,都须要从新扫描数据记录。为了不进行 file sort 排序,能够把 GROUP BY 和 ORDER BY 中涉及到的列加入到索引中
  • 建立了索引就会按照索引的顺序来存储数据,这样再对这些数据按照某个字段进行分组或者排序的时候,就会提高效率

很难存在理想的索引设计

  1. 采用三星索引会让索引片变宽,这样每一个页可以存储的索引数据就会变少,从而增长了页加载的数量
  2. 从另外一个角度来看,若是数据量很大,好比有 1000 万行数据,过多索引所须要的磁盘空间可能会成为一个问题,对缓冲池所需空间的压力也会增长,增长了索引维护的成本
  3. 若是为全部的查询语句都设计理想的三星索引,就会让数据表中的索引个数过多,这样索引维护的成本也会增长
  4. 当添加一条记录的时候,就须要在每个索引上都添加相应的行(存储对应的主键值)
  5. 假设添加一行记录的时间成本是 10ms(磁盘随机读取一个页的时间)
  6. 若是咱们建立了 10 个索引,添加一条记录的时间就可能变成 0.1s,若是是添加 10 条记录呢?就会花费近 1s 的时间
  7. 从索引维护的成原本看消耗仍是很高的
  8. 固然对于数据库来讲,数据的更新不必定立刻回写到磁盘上,但即便不及时将脏页进行回写,也会形成缓冲池中的空间占用过多,脏页过多的状况
相关文章
相关标签/搜索