MySQL是如何利用索引的?

什么是索引?索引是数据库快速找到记录行的一种数据结构 , 类似我们看书时的目录 , 它是良好性能的关键因素 。尤其是表中的数据量越来越大时 , 如果索引使用不当 , 会严重影响性能 。索引也是最常见的数据库优化手段 , 它能轻易的将查询性能提高好几个量级 。
MySQL索引类型?

mysql索引数据是存储在存储引擎中的 , 所以不同存储引擎中索引的工作方式并不一样 。
B-Tree索引:基于B+树(一种多叉搜索数树)来实现的索引类型 , 一般也是使用的最多的索引类型 , 之所以选择B+树而不是其他数据结构 , 是因为B+树在查询时间复杂度可以维持在O(logn)的级别上 , 由于B+的矮胖(从根节点到叶子节点的距离可以维持在较小范围)特性减少磁盘IO次数、数据只存在叶子节点中并且按顺序存储也可以支持快速的范围查询 , 这是其他结构无法满足的!
B+索引中值是按顺序存储的 , 叶子节点到根节点的距离都相同 , 从B+树的根节点开始往下查找 , 节点存储了指向叶子节点的指针 , 通过将要查找的值和每个节点值比较后 , 一层层定位到最终的叶子结点上 , 叶子节点存储的就是行数据、指针或主键 。
假如我们索引列是:
key(lastname(姓),firstname(名),born) , 可以使用B+树索引的查询类型包括:全键值、键值范围、键前缀查找 , 其中键前缀只适用于最左前缀查找:
  • 全值匹配:指的是和索引中所有的列进行匹配 , 如可以找到姓名为:Cuba(名) Allen(姓) 、生于1988-10-04的人 , 如where lastname=‘Allen’ and firstname=‘Cuba’ and born='1988-10-04'
  • 匹配最左前缀:可以查找姓为Allen的人 , 如where lastname=‘Allen’
  • 匹配列前缀:也可以匹配某一列的值的开头部分 , 如where lastname like ‘A%’ 或者where firstname like ‘M%’
  • 匹配范围:可以匹配姓在Allen和Bill之间的人
  • 精确匹配某一列并匹配另外一列:查找所有姓为Allen、并且名字是以M开头的人 , 如where lastname=‘Allen’ and firstname like ‘M%’
  • 访问索引数据:这种查询只需要访问索引本身就行了 , 不需要访问数据行 , 也就是常说的索引覆盖 , 举个例子:如果只需要找到姓为Allen的人的名称 ,  而不需要这个人其他的信息 , 名称就存在与索引中 , 不需要再去数据行中查找数据了 。
这里需要注意的是叶子节点存什么类型数据不同的存储引擎还不一样 , 在MyISAM中叶子节点存储的是数据物理位置(指针) , 而InnoDB使用B+结构存储的是原始数据或主键 , 也就是我们常说的聚簇索引 , 它存储的是原始全量数据、键值 , 聚簇索引指的是一种数据索引组织形式 , 它将数据和索引聚集在一起所以叫聚簇 , 它本身并不是一种索引类型 。
一般InnoDB查找过程为从辅助索引上开始查找到数据主键 , 然后在主键索引中用主键再次查找 , 最后再找到数据 , 虽然多了一次查找过程 , 但更新数据不会导致聚簇索引频繁变化 。而在MyISAM中不需要2次索引查找 , 因为叶子节点存储的是数据的物理地址可以直接定位 , 虽然查询看似简单了 , 但是物理地址会因为数据频繁变更而发生变化 。
假设有以下数据:
MySQL是如何利用索引的?

文章插图
InnoDB(聚簇索引)数据查找过程:
MySQL是如何利用索引的?

文章插图
MyISAM(非聚簇索引结构)数据查找过程:
MySQL是如何利用索引的?

文章插图
哈希索引:基于哈希表来实现的索引类型 , 如果存在哈希冲突 , 索引会使用链表来存放多个记录到一个哈希桶中 。举个例子:如果存在以下索引 key USING HASH(firstname) , 哈希索引会使用哈希函数计算出firstname列的哈希值作为key , 并将行指针作为value存储 , 当使用 =、IN()、<=>操作时 , 先计算出sql语句操作查找值的哈希值 , 并使用其来查找哈希表对应的行指针 , 从而返回数据 。


推荐阅读