mysql表中索引是存储引擎层实现的

InnoDB 的索引模型

InnoDB中,表狗屎根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。
InnoDB使用了B+树索引模型,所以数据都存储再B+树中
索引类型分为主键索引和非主键索引

主键索引

主键索引又称为聚簇索引.如果没有设置主键索引,InnoDB会生成一个rowid作为主键id

非主键索引

非主键索引又称为二级索引,存储的是主键索引的的主键值
普通索引查询要先搜索普通索引的树,得到主键值,再再主键索引查询得到真正的内容。这个过程叫做回表。
普通索引需要多扫描一棵树,所以我没应该尽量使用主键查询。

索引维护

B+树为了维护索引的有序性,在插入新值的时候需要必要的维护。
如果插在中间的话,且数据页已满,需要申请新的数据页。然后挪动部分数据过去,这个过程称为页分裂。
页分裂,性能会受影响,还会影响数据页的利用率。(50%)
相邻两个页由于删数据,利用率很低之后,会将数据页合并。合并过程可以认为是分裂过程的逆过程。

索引的选择

新增

自增id,每次新增都是追加操作,不会涉及到挪动其他记录,也不会触发叶子节点分裂。所以适合做为唯一索引。
主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
只有一个索引,该索引是唯一索引时可以考虑用业务字段作为唯一索引。

查询

普通索引查询的时候会多做一次”查找判断下一条记录”的操作.但是由于查询是以数据页形式加载到内存中的,多出来的操作对cpu来讲几乎是可以忽略不计.

更新

如果数据页已经加载到内存中:

  • 唯一索引,判断冲突,插入值,
  • 普通索引都直接更新值.

数据页未加载到内存中:

  • 唯一索引:先加载内存,判断冲突,插入值
  • 普通索引:记录到change buffer

所以建议优先考虑普通索引
更新之后需要马上要访问这个数据页,会触发merge过程,增加change buffer的维护代价.此时考虑使用唯一索引
业务上需要使用唯一索引

覆盖索引

如果查询的结果为主键,或者是索引,联合索引覆盖了我们的查询需求,称为覆盖索引
覆盖索引,不需要回表,减少了搜索次数,显著提升查询性能

最左前缀原则

B+树这种索引结构,可以利用”最左前缀”,来定位记录.
第一原则:如果通过调整顺序,可以少维护一个索引,那么顺序往往就要优先考虑采用的.
第二原则:空间原则

数据页

当需要读一条记录的时候,并不是将这个记录本身从磁盘读出,而是以页为单位,将其整体读人内存.InnoDB默认内存大小是16K

change buffer

普通索引才有会使用change buffer,存在于内存中
需要更新一个数据的时候,如果这个数据页还没在内存中,在不影响数据一致性的前提下,InnoDB会将这些更新操作缓存在change buffer中.
将数据merge到原数据页:

  • 访问这些数据
  • 系统后台线程定期执行
  • 数据库正常关闭

change buffer 因为减少了随机磁盘访问,所以更新性能会明显提升.

索引基数

mysql的优化器在索引选择的时候,并不能准确的知道满足这个条件的记录有多少条.只能通过采样法来获取索引基数.

索引选择异常的处理

可能的异常索引选择情况:

  1. 由于事务原因,导致扫描行判断错误,优化器认为全表扫描,因为不需要回表.会比走索引快.
  2. 由于order by b limit 1 导致系统认为需要对b排序,而破坏了 以小驱大.

解决办法:

  1. 通过force index指定索引
  2. 修改语句指导索引
  3. 新建索引来提供优化器的选择,或者删除误用的索引

    给字符串加索引

  4. 直接传教索引,缺点:占空间

  5. 前缀索引:缺点增加扫描次数,不能使用覆盖索引
  6. 倒序存储,再建前缀索引(用于字符串前缀区分度不高,比方身份证),每次查询需要reverse函数额外开销
  7. 创建hash字段索引,查询性能稳定,有额外的存储和计算消耗

    数据刷新到磁盘

    脏页

    内存数据页跟磁盘数据页不一致的时候,我们称之为脏页

    干净页

    内存数据页和磁盘的数据一致称为干净页

    刷新脏页(flush)到磁盘

    InnoDB的redo log 满了。系统会停止更新操作,把checkpoint往前推。尽量避免
    系统内存不足,需要新的内存页。如果淘汰的是脏页,这就触发flush。常态,一个查询淘汰脏页多,日志写满。会导致性能问题。
    系统不满的时候就会flush脏页。
    MySql正常关闭,MySql会把所以的脏页都flush到磁盘中。

    Buffer pool

    InnoDB会用缓冲池来管理内存,Buffer pool

    InnoDB刷脏页的控制策略

    innodb_io_capacity设置成磁盘的IOPS
    InnoDB会根据脏页的比例,和redo log的日志数量值,取较大的 设为R%*设置的innodb_io_capacity来控制脏页的速度

    刷脏页的连坐机制

    刷新脏页的时候会把附近的脏页一起flush。会减少随机IO,但是在SSD磁盘IO不再是瓶颈,关闭会减少sql的响应时间。所以建议关闭连坐机制
    InnoDB8.0之前是默认开启的,8.0默认是关闭的
    innoBD_flush_neighbors = 0 关闭 1开启

    order by 原理

    例子:select city,name,age form t where city=’北京’ order by name limit 1000;

    条件字段加索引

    city 单独索引时

  8. 初始化sort_buffer确定放入排序的字段.(内存够的话放入,全部字段,内存不足的时候,只放需要排序的字段和主键id)

  9. 通过索引找到主键id
  10. 回表,根据主键查询数据放入sort_buffer
  11. 重复2,3直到条件不满足
  12. 对sort_buffer数据进行排序//也有可能通过外部排序来完成,通过sort_buffer_size来定义
  13. sort_buffer不是全部字段的话,再次回表
  14. 返回符合条件的数据

    条件字段+order by字段联合索引

    city,name 联合索引

  15. 根据索引获取主键id

  16. 回表获取三个字段返回
  17. 重复1,2直到所有满足条件的数据

    结果,条件,order by字段联合索引(覆盖索引)

    city name age 联合索引

  18. 根据索引获取第一条数据

  19. 获取下一条数据直到获取所有满足条件的数据

    函数操作索引会破坏索引值的有序性

    显示函数操作:mounth(row) = 7
    隐式操作:id+1 = 1000
    隐式字符串编码转换.
    此时优化器会放弃走树搜索