技术总监夸我“索引”用的溜,我飘了......( 二 )

  • Rotational Latency:完成步骤 1 后 , 磁头移动到同一磁道扇区对应的位置所需求时间 。
  • Transfer Time:从磁盘读取信息传入内存时间 。
  • 这其中寻道时间占据了绝大多数的时间(大概占据随机 IO 时间的占 40%) 。
    随机 IO 和顺序 IO 大概相差百倍 (随机 IO:10 ms/ page, 顺序 IO 0.1ms / page) , 可见顺序 IO 性能之高 , 索引带来的性能提升显而易见!
    索引的种类
    索引主要分为以下几类:
    • B+树索引
    • 哈希索引
    ①B+ 树索引
    技术总监夸我“索引”用的溜,我飘了......

    文章插图
     
    B+ 树是以 N 叉树的形式存在的 , 这样有效降低了树的高度 , 查找数据也不需要全表扫描了 。
    顺着根节点层层往下查找能很快地找到我们的目标数据 , 每个节点的大小即一个磁盘块的大小 , 一次 IO 会将一个页(每页包含多个磁盘块)的数据都读入(即磁盘预读 。
    程序局部性原理:读到了某个值 , 很大可能这个值周围的数据也会被用到 , 干脆一起读入内存) , 叶子节点通过指针的相互指向连接 , 能有效减少顺序遍历时的随机 IO 。
    而且我们也可以看到 , 叶子节点都是按索引的顺序排序好的 , 这也意味着根据索引查找或排序都是排序好了的 , 不会再在内存中形成临时表 。
    ②哈希索引
    哈希索引基本散列表实现 , 散列表(也称哈希表)是根据关键码值(Key value)而直接进行访问的数据结构 , 它让码值经过哈希函数的转换映射到散列表对应的位置上 , 查找效率非常高 。
    假设我们对名字建立了哈希索引 , 则查找过程如下图所示:
    技术总监夸我“索引”用的溜,我飘了......

    文章插图
     
    对于每一行数据 , 存储引擎都会对所有的索引列(上图中的 name 列)计算一个哈希码(上图散列表的位置) , 散列表里的每个元素指向数据行的指针 。
    由于索引自身只存储对应的哈希值 , 所以索引的结构十分紧凑 , 这让哈希索引查找速度非常快!
    当然了哈希表的劣势也是比较明显的 , 不支持区间查找 , 不支持排序 , 所以更多的时候哈希表是与 B Tree等一起使用的 。
    在 InnoDB 引擎中就有一种名为「自适应哈希索引」的特殊索引 , 当 innoDB 注意到某些索引值使用非常频繁时 , 就会内存中基于 B-Tree 索引之上再创建哈希索引 。
    这样也就让 B+ 树索引也有了哈希索引的快速查找等优点 , 这是完全自动 , 内部的行为 , 用户无法控制或配置 , 不过如果有必要 , 可以关闭该功能 。
    InnoDB 引擎本身是不支持显式创建哈希索引的 , 我们可以在 B+ 树的基础上创建一个伪哈希索引 , 它与真正的哈希索引不是一回事 , 它是以哈希值而非键本身来进行索引查找的 , 这种伪哈希索引的使用场景是怎样的呢?
    假设我们在 db 某张表中有个 url 字段 , 我们知道每个 url 的长度都很长 , 如果以 url 这个字段创建索引 , 无疑要占用很大的存储空间 。
    如果能通过哈希(比如 CRC32)把此 url 映射成 4 个字节 , 再以此哈希值作索引  , 索引占用无疑大大缩短!
    不过在查询的时候要记得同时带上 url 和 url_crc , 主要是为了避免哈希冲突 , 导致 url_crc 的值可能一样:
    SELECT id FROM url WHERE url = "http://www.baidu.com"  AND url_crc = CRC32("http://www.baidu.com") 这样做把基于 url 的字符串索引改成了基于 url_crc 的整型索引 , 效率更高 , 同时索引占用的空间也大大减少 , 一举两得 , 当然人可能会说需要手动维护索引太麻烦了 , 那可以改进触发器实现 。
    除了上文说的两个索引  , 还有空间索引(R-Tree) , 全文索引等 , 由生产中不是很常用 , 这里不作过多阐述 。
    高性能索引策略
    不同的索引设计选择能对性能产生很大的影响 , 有人可能会发现生产中明明加了索引却不生效 , 有时候加了虽然生效但对搜索性能并没有提升多少 。


    推荐阅读