业务工具杂谈(二)之Mysql

85 阅读5分钟

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

索引

MYSQL 对索引的官方定义为:索引(index) 是帮助mysql高效获取数据的数据结构,所以说,索引的本质是数据结构

索引的目的在于提高查询效率,就像书本里面的目录一样,目录 = 索引;通过目录上的页码找到指定的value

2.2.1 B、B+树索引

  • 平常说的索引,没有特别说明的话,就是B+树(多路搜索树)结构组织的索引。

    • 二叉查找树:

      • 左节点 < 根节点 < 右节点
      • 结构多变,容易一边超长浪费资源,影响查找
    • 平衡二叉树:

      • 左节点 < 根节点 < 右节点
      • 在二叉查找树的基础上,任何节点的子树高度差 ≤ 1
    • B 树 ( B - 树 ):多路平衡查找树——常用于文件系统和部分数据库索引(MongoDB)

      • 从查找次数和查找次数来说,二叉查找树都是最少的,但是基于现实的问题——磁盘IO
      • 对比磁盘IO的速度来说,内存的比较耗时几乎可以忽略。所以,只要树的结构足够矮壮,IO的次数足够少,就可以提升查找的性能
      • 重点优势:自平衡——插入和删除会使整个B树节点发生连锁反应(参考平衡二叉树),这正是因为这样,B树才能维持多路平衡
    • B+树:

      • 在B树的基础上,进行完善
      • 数据都存放在叶子节点,非叶子节点存放指向下一节点的指针和key值,不再把数据带在每个节点上

索引真的这么好吗?
虽然可以提高数据索引的效率,并且因为其B+树的数据结构,帮助其降低了数据库IO的成本
但是相对来说,会占用内存,也就牺牲了CPU的性能;所以需要合理利用索引

  • 索引的区分

    • 聚集索引(聚簇索引)/主键索引

      • 叶子节点存放的是实际的整行数据记录,通过主键查找到索引节点,找到叶子结点拿到数据
    • 非聚集索引(非聚簇索引) / 辅助索引 / 组合索引

      • 叶子节点存放的是主键Id,再通过主键Id走主键索引,这也就是常说的回表
    • 这个辅助索引倒是有点类似es的倒排索引(仅仅相近而已)

  • 为什么推荐使用整型自增而不是UUID

    • 因为UUID是字符串,需要对每一个字符进行比对,会耗费更多的空间
    • 因为UUID是乱序的,新增的时候有可能需要对整个结构进行重构;整型的话可以直接在叶子结点下新增即可
  • 辅助索引/ 非主键索引存储的是主键?

    • 就类似订单表与订单详情表一样,只需要保留一个外键,不需要把整套订单信息再维护一遍,修改订单后,也不需要对其他信息负责

2.2.2 哈希索引

哈希索引是基于哈希表实现的,只有完全覆盖(精确匹配所有的列值)才会生效

目前来说,只有Memory和NDB引擎支持哈希索引

  • 所谓的哈希索引就是存储引擎会对每一行数据的索引列计算出一个哈希值(hash code)并存储在索引中,同时哈希表保存一个指向数据行的指针
  select * from user
      id    name
      1     abc
      2     qqq
      3     www
      
        //hash函数
      f(`abc`) = 2323
      f(`qqq`) = 8747
      f(`www`) = 7437
      
      //hash索引的存储结构
      2323  指向第一行的指针
      7437  指向第三行指针
      8747  指向第二行指针
  • 如果计算出来哈希冲突的话,会把多个记录指针以链表的形式存放在hash内
  • hash索引并不是按照索引的值顺序去存储的,而是hash值的顺序,所以无法对数据进行排序
  • hash索引不支持部分匹配、模糊等部分查询,因为hash code 是对索引列的全部数据进行计算,
  • 如果哈希冲突很大的话,对性能的影响也是较大的,会导致查询变得很慢。因为查询到哈希相同时,需要遍历对应哈希值的链表的每一行

实际用途:哈希索引只适合特定场景,但是一旦适合使用,性能的提升将会很快

  • 实例:需要存储大量的URL,并且通过URL去搜索查找

    • 正常的URL都是挺长的,如果用B树去存储会导致存储的内容很大

    • 使用hash索引,把URL列设置为索引,性能就会提高很多,因为对URL计算hash code,指针指向相应的行

    •     select * from user where url = "http://www.baidu.com" 
              and url_crc = CRC32("http://www.baidu.com")
      
    • 可以像上述一样,多维护一个hash字段(url_crc),并且用hash函数对目标值进行hash计算

    • 为什么还需要多一个url = "http://www.baidu.com"

      • 是因为上述所说的hash冲突,会导致相同hash值的记录指针存放在hash条目内
      • 这就需要对整个链表遍历并一一比对其中的url值

2.2.3 空间索引

空间索引更多应用于查找空间范围内的目标,也可以称为空间数据查询,是用来提高系统对数据获取的效率

  • MyISAM表支持空间索引,可以用作地理数据存储;
  • 空间索引是空间数据库的三大特性之一,会从所有纬度来索引数据

2.2.4 全文索引

全文索引也是MyISAM的特殊类型索引,一般用于替代效率较低的LIKE模糊查询匹配操作,更类似于搜索引擎做的事情,而不是简单的WHERE条件查询

2.2.5 总结

  • 为什么Mysql 索引用B+树而不用B树

    • 因为B+树的非叶子节点不存放具体的数据,所以内存页可以存放更多的key值,数据更加紧密,相比B树来说更为矮壮,这也就减少了相应的IO;
    • 而且,B+树的叶子节点都是相链的,对整棵树的遍历只需要一次线性遍历叶子节点即可
  • Hash索引和B+树

    • hash索引无法排序,跟数据存储的顺序无关,只与hash值有关
    • hash索引是需要精准匹配所有列值才会生效,否则全表扫描
    • 当数据量大的时候,不可避免会出现hash冲突,hash冲突会以链表形式存放在当前值下,再一一比对;效率低下
  • 高效索引

    • 索引覆盖(Covering Index),也就是说不再需要回表操作(主键索引)
    • 使用explain语句,通过输出的extra值判断是否有命中索引
  • 索引一定是最好的吗

    • 并不一定,只有当索引帮助存储引擎快速找到记录所带来的好处 > 索引的代价时,才是有效索引
    • 相对的,对于数据量非常小的表(几百、几十),简单的全表扫描会更加高效
    • 对于中大型的表,索引就非常有效;
    • 而对于特大型的表,索引的代价就随之增大了,这时要关注的就不再是一条条地匹配数据了,而是区分出需要查询的一组数据(块级别元数据技术取代索引)