数据模型

哈希表

  • 键-值存储的数据结构
  • 哈希冲突
  • 无法实现范围查询,只适合等值查询

    有序数组

  • 数据更新插入成本高

  • 只适合于静态存储引擎

    搜索树

  • 二叉搜索数

    • 每个节点的左儿子小于父节点,父节点又小于右儿子
    • 查询更新时间复杂度O(log(N))
  • N叉数

    • 以InnoDB的一个整数字段索引为例,这个N差不多是1200。这棵树高是4的时候,就可以存 1200的3次方个值,这已经17亿了。考虑到树根的数据块总是在内存中的,一个10亿行的表上一 个整数字段的索引,查找一个值最多只需要访问3次磁盘。其实,树的第二层也有很大概率在内 存中,那么访问磁盘的平均次数就更少了
    • N叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中

      跳表

      LSM树

      InnoDB的索引模型

      B+数

  • image.pngimage.png

  • 索引 - 图3
  • 非叶子节点仅用来索引,数据都保存在叶子节点中
  • 所有叶子节点均有一个链指针指向下一个叶子节点
  • 磁盘读写代价低
  • 查询效率稳定O(logn)
  • 方便进行范围查询
  • MySQL中的InnoDB引擎使用B+Tree结构来存储索引,可以尽量减少数据查询时磁盘IO次数,同时树的高度直接影响了查询的性能,一般树的高度维持在 3~4 层。
  • B+Tree由三部分组成:根root、枝branch以及Leaf叶子,其中root和branch不存储数据,只存储指针地址,数据全部存储在Leaf Node,同时Leaf Node之间用双向链表链接每个Leaf Node是三部分组成的,即前驱指针p_prev,数据data以及后继指针p_next,同时数据data是有序的,默认是升序ASC,分布在B+tree右边的键值总是大于左边的,同时从root到每个Leaf的距离是相等的,也就是访问任何一个Leaf Node需要的IO是一样的,即索引树的高度Level + 1次IO操作

    存储结构

  • 表空间

    • 系统表空间
    • 独占表空间
    • 通用表空间
    • 临时表空间
    • undo表空间
    • 数据段
    • 索引段
    • 回滚段
    • 64个连续的页组成,即1M
    • 默认16KB
    • InnoDB按页为单位来读写数据
    • 页写满了,会发生页分裂
    • 操作系统管理磁盘的最小单位是磁盘块,是操作系统读写磁盘的最小单位
  • 主键索引-聚簇索引

  • 叶子节点存储该行记录

  • 索引维护

    • 页分裂
    • 页合并

      非主键索引-二级索引

  • 叶子节点存储该行主键

  • 回表
    • 覆盖索引——减少回表
  • 最左前缀 原则
    • 联合索引最左N个字段,或者字符串的最左M个字符
  • 索引下推

    • MySQL5.6引入,在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不能满足条件的记录,减少回表

      Change Buffer

      索引 - 图4
  • 更新过程

    • 如果数据页在内存中就直接更新,否则的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中
    • 将对数据页的操作记录到redo log,然后告诉server层更新成功
    • 下次查询访问这个数据页,先读入数据页到内存,然后执行change buffer中的相关操作
  • Change Buffer的Merge
    • 将change buffer中的操作应用到原数据页,得到最新结果的过程称为merge
    • 访问这个数据页会触发Merge
    • 系统定期Merge
    • 数据库正常关闭时候Merge
  • 磁盘上的change buffer在系统表空间,内存中的change buffer在InnoDB的buffer pool中
  • 参数innodb_change_buffer_max_size来动态设置change buffer的大小
    • 这个参数设置为50的时候,表示change buffer的大小最多只能占用buffer pool的50%
  • 在一个数据页merge之前,change buffer记录的变更越多,收益旧越大
  • 脏页:内存数据页和磁盘数据页内容不一致
    • flush:脏页落盘
      • 落盘规则:内存中的数据页落盘,如果数据页不在内存,就从磁盘读取内存,用redo log记录更新内存中的数据页,而后落盘
      • 落盘时机
        • 当redo log写满后,也就是write pos追上了check point,mysql就必须去进行flush操作,将数据落盘
        • mysql空闲时候
        • mysql正常关闭时候
        • 内存不够用的时候
    • InnoDB用buffer pool管理内存
    • 脏页影响
      • 一个查询要淘汰的脏页太多,会拖慢响应时间(系统”抖动“)
      • redo log写满,更新操作全部会堵住,影响系统正常运行
    • InnoDB刷脏页的控制策略
      • InnoDB应该根据机器的IO能力,来控制刷脏页的力度
        • 对应的mysql参数:innodb_io_capacity,建议设置成磁盘的IOPS
          • 可以使用fio工具来测试获取
      • 如果发现脏页旁边的数据页也是脏页,会一起刷掉
        • innodb_flush_neighbors为0,则禁掉此规则,mysql8.0这个参数默认0

前缀索引

  1. alter table sys_user add index1(email(6));
  • 使用前缀索引,定义好长度,就可以做到既节省空间,由不用额外增加太多查询成本
  • 前缀索引用不上覆盖索引

    覆盖索引

    索引失效情况

  • 查询条件中有or

  • like以”%”开头
  • 对查询的列上有运算或者函数的
  • 如果查询中没有用到联合索引的第一个字段,则不会走索引
  • 对于 NOT NULL的字段,使用IS NULL或IS NOT NULL作为查询条件无意义,不会用到索引
  • 查询条件中有OR,如果一个没有用到索引,则都不会用到