04 Mysql实战45讲:索引

192 阅读7分钟

索引的出现其实是为了提高数据库查询的效率,就像书的目录一样

1. 索引的常见类型

  • 哈希表【Mysql的Memory引擎显式支持】
    • 哈希表是一种KV存储结构,使用hash函数定位具体的key在哈希表中的位置(可参考Java中HashMap数据结构),其解决哈希冲突的方式就是链地址法
    • 使用场景:适用于等值查询,因为key存储的顺序是按照hash值(无序)存储的,所以不适合区间查询;
    • 如图要找到name04,先根据name04做hash计算得到索引为2,然后遍历链表比对key和name04的值是否相等
    • 哈希索引的限制条件
      • 哈希索引只包含哈希值和行指针,不存储字段值,所以不能使用索引避免读取行(哈希访问内存行的速度很快,所以读取行对性能影响不是很明显)
      • 哈希索引数据并不是按照索引值顺序存储,所以无法使用排序
      • 哈希索引不支持部分索引列匹配查找,因为哈希索引是使用索引列的全部内容计算哈希 image.png
  • 有序数组
    • 有序数组是按照某一字段做排序后的存储方式,精确查询时:可根据二分查找定位,当需要范围查询时,二分查找加上线性查找
    • 使用场景:有序数组在等值和范围查询场景中的性能都非常优秀,缺点是更新数据时:如果往中间插入记录,就必须挪动后面所有的记录,成本太高;只适用于静态存储引擎:比如某一年的统计数据 image.png
  • 搜索树
    • 二叉搜索树的特点是:每个节点的左叶子结点小于父节点,父节点小于右叶子节点。查询的时间复杂度是O(log(N));为了维持O(log(N))的查询复杂度,更新时需要保持搜索树是平衡二叉树,所以更新的时间复杂度也是O(log(N)),二叉树的搜索效率是最高的
    • 不使用二叉搜索树的原因是:索引不止存在内存,还要写到磁盘,比如存储了100w数据的节点的平衡二叉树,树高20,一次查询可能要访问20个数据块,从磁盘随机读需要10ms左右的寻址时间,如果要二叉树来存储,单独访问一个行需要20*10=300ms,难以接受
    • 【补充】平衡二叉树的结构特点:
      • 平衡二叉树是其每隔节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1) image.png
  • 为了让查询少读磁盘,必须少随机访问数据块,所以使用N叉树
  • B树:叶子节点和非叶子节点都存数据,每一层存储的数据比B+树少,且区间查询还需回到父节点层比较。 image.png
  • B+树:非叶子节点只保存索引,叶子节点才存储具体的值(非叶子节点层存储的数据量多),叶子节点存储了指向下一叶子节点的指针
    • 比如以InnoDB的一个整数字段索引为例子,N叉树的N大概为1200,当树高为4的时候,就可以存1200的三次方,大概为17亿。考虑到树根的数据块总是在内存中(因为经常被访问,所以属于热数据)一个10亿行的表上一个整数字段的索引大概需要访问3次磁盘,第二层也有很大概率在内存中,访问磁盘的平均次数就更少。 image.png
  • 数据底层存储的核心是基于数据模型,遇到新的数据库时可以先关注数据模型,才能从理论上分析出适用场景。

2. Innodb的索引模型

  • 在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表,InnoDB使用了B+树索引模型,所以数据都是存储在B+树中的
  • 每一个索引在InnoDB里面对应一棵B+树
  • 根据叶子节点的内容,索引分为主键索引和非主键索引
    • 主键索引的叶子节点存放的整行数据。又被称为聚簇索引
    • 非主键索引的叶子节点内容是主键的值(而不是指向行的物理位置的指针),称为二级索引
  • 基于主键索引的查询和普通索引的查询区别
    • 基于主键索引的查询,只需要查询聚簇索引对应的B+树
    • 基于普通索引,则需要查询普通索引B+树得到id,然后再去主键索引树回表查询
  • 按照Mysql使用的B-Tree索引有以下优点:
    • 索引大大减少了服务器所需要扫描的数据量
    • 索引可以帮助服务器避免排序和临时表
    • 索引可以将随机IO变为顺序IO

3. 索引维护

  • 新增数据如果索引字段比较离散,则会发生页分裂。除了性能,还影响存储利用率
    • 如果主键是单调增加的话,符合递增插入的场景,每次都是追加操作,不涉及挪动记录,不会触发页分裂
  • 主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也越小
  • 删除数据,如果相邻两个页删除了数据,利用率很定,会将数据页合并
  • 顺序主键一些缺点
    • 对于高并发的工作负载,在InnoDB中按主键顺序插入可能会造成明显的争用,主键的上界会成为热点,因为所有的插入都发生在这里,所以并发插入可能导致间隙锁竞争
    • 另一个热点可能是AUTO_INCREMENT锁机制,也可修改innodb_autoinc_lock_mode

4. 覆盖索引

  • 由于覆盖索引可以减少树的搜索次数,显著提升查询新能,所以使用覆盖索引是一个常用的性能优化手段

5. 最左前缀原则

  • B+树索引结构,可以利用索引的最左前缀来定位记录
  • 在建立联合索引时,如何安排索引内的字段排序
    • 如果通过调整顺序可以少维护一个索引,那么这个顺序就需要优先考虑采用,优化存储空间
  • 可以使用索引的查询类型
    • 全值匹配
    • 匹配最左前缀
    • 匹配最左边的列前缀
    • 匹配范围值
    • 精确匹配最左一列并范围匹配另外一列
    • 只访问索引的查询
  • B-Tree的索引限制
    • 如果不是按照索引的最左列开始查找,则无法使用索引
    • 不能跳过索引中的列
    • 如果查询有某个列的范围查询,则其右边的所有列都无法使用索引优化查找

6. 索引下推

  • MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数

其他

  • 三星索引
    • 索引将相关的记录放在一起获得一星
    • 如果索引中的数据顺序和查找中的排列顺序一致获得二星
    • 如果索引中的列包含了查询中需要的全部列则获得三星