Mysql-Mysql高级-索引优化介绍(2)-mysql索引的结构

索引优化介绍

mysql索引的结构
  • BTree结构

    • 在这里插入图片描述

    • BTREE特性,BTree又叫多路平衡搜索树,一颗m叉的BTree特性如下:(上图是一个三叉的多路平衡搜索树)

      • 树中每个节点最多包括m个孩子
      • 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子
      • 若根节点不是叶子节点,则至少有两个孩子
      • 所有的叶子节点都在同一层
      • 每个非叶子结点由n个key与n+1个指针组成,其中ceil(m/2)-1<= n <= m-1。说明一个三叉的BTree,每个非叶子节点至少有1个key,2个指针。每个非叶子节点至多有2个key,3个指针。一个四叉的BTree,每个非叶子节点至少有1个key,2个指正;每个非叶子节点至多有3个key,4个指针。
      • BTREE的新key插入,是向叶子节点做插入的,因为如果插入始终往根节点插入的话,会造成叶子节点的元素始终不变,而根节点如果需要分裂又会分裂成新的叶子节点
    • 以5叉BTree为例,key的数量:公式推导[ceil(m/2)-1] <= n <= m-1,所以2<= n <= 4。当n>4时,中间节点分裂到父节点,两边节点分裂,插入 C N G A H E K Q M F W L T Z D P R X Y S数据为例

      • 演变过程

        • 插入前4个字母C N G A

          • 在这里插入图片描述
        • 插入H,根节点的key的数量n>4,此时中间元素G字母向上分裂到新的节点,作为根节点,G左右两侧数据分裂成新的叶子节点

          • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xHdOEeOY-1624600598569)(pic\MySQL\btree插入过程\Btree-插入过程2.png)]
          • 在这里插入图片描述
        • 插入E,K,Q不需要分裂

          • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9MAaHYM-1624600598571)(pic\MySQL\btree插入过程\Btree-插入过程3.png)]
          • 在这里插入图片描述
        • 插入M需要分裂,此时M恰好是中间元素,中间元素M向上分裂添加到根节点中G元素的后面,将右侧节点H K N Q分裂成左右两个节点

          • 在这里插入图片描述
        • 插入F W L T,不需要分裂

          • 在这里插入图片描述
        • 插入Z时,需要分裂,最右侧节点满了,此时的中间关键字T上移至父节点,注意通过上移中间关键字,树最终还是保持平衡,分裂结果的节点存在2个关键字

          • 在这里插入图片描述
        • 插入D时,导致最左边的叶子节点被分裂,D恰好是中间关键字,上移到父节点。

          • 在这里插入图片描述
        • 随后插入P R X Y不需要分裂

          • 在这里插入图片描述
        • 最后插入S时候,导致右侧第二个节点满了需要分裂,此时把中间关键字Q上移时,发现根节点也满了,需要分裂。根节点中D G Q M T,此时M需要向上分裂。

          • 在这里插入图片描述
    • 说明

      • 一颗b树,浅蓝色的块称之为一个个磁盘块,每个磁盘块包含几个数据项(白色表示)和指针(黄色表示),例如磁盘1包含数据项有17和35,包含指针p1,p2,p3。
      • p1指向小于17的磁盘块,p2指向大于17,小于35的磁盘块,p3指向大于35的磁盘块。
  • B+Tree

    • B+Tree为BTree的变种,B+Tree和BTree的区别:

      • n叉B+Tree最多含有n个Key,而Btree最多还有n-1个key;
      • B+Tree的叶子节点保存所有key的信息,按照key 的大小顺序排列。包含关键字(数据),指向数据的指针,没有指向下一个磁盘块的指针。
      • 所有的非叶子节点都可以看做是key的索引部分。非叶子节点包含的是,关键字(数据),向下的指针(指向下一个磁盘块的指针),没有指向数据的指针
      • B-树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,B+树的记录只放在叶子节点。
      • 在B-Tree中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而B+Tree每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。从这个角度看B-Tree性能好像要比B+Tree好,而在实际应用中B+Tree性能要好些。因为B+Tree的非叶子节点不存放实际数据,这样每个节点可容纳的元素个数比B-Tree多,树的高度比B-Tree小,这样带来的好处是减少磁盘访问次数。尽管B+Tree找到一个记录“所需的比较次数”要比B-Tree多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际B+Tree的性能可能还会好些。而且B+Tree的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有文件,一个表中的所有记录等)这也是很多数据库和系统使用B+Tree的原因。
      • 在这里插入图片描述
    • 为什么说B+Tree比B-Tree更适合实际应用中操作系统的文件索引和数据库索引?

      • B+Tree的磁盘读写代价更低
        • B+Tree的内部节点并没有指向关键字具体信息的指针。因此其内部节点相对BTree数更小,如果把所有同一内部节点的关键字存放在同一个盘块中,那么盘块所能容纳的关键字数量也越多。一次性读取到内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了
      • B+Tree的查询效率更加稳定
        • 由于非叶子节点不是最终指向文件内容的节点,而是叶子节点中关键字的索引,所以任何关键字的查找都必须走一条从根节点到叶子节点的路。所有关键字的路径长度相同,导致每个数据的查询效率相当。
  • Mysql B+Tree索引

    • Mysql索引数据结构对经典B+Tree进行了优化,在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提供了区间访问的性能。(双向链表)
    • 在这里插入图片描述
  • 聚簇索引与非聚簇索引

    • 聚簇索引并非一种单独的索引类型,而是一种数据存储方式。聚簇索引本身也是B+tree索引

    • 非聚簇索引:将数据存储于索引分开的结构,索引结构的叶子节点指向数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key_buffer命中时,速度慢的原因

    • 在innodb中,在聚簇索引之上创建的索引称之为辅助索引(二级索引),辅助索引访问数据总是需要二次查找(需要回表,需要根据查询到的主键id(聚簇索引的id)再查询聚簇索引获得数据),非聚簇索引都是辅助索引,像复合索引,前缀索引,唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值。

    • 何时使用聚簇索引和非聚簇索引

      • 对于经常分组排序的列,可以使用聚簇索引,也可以使用非聚簇索引(两者都是有序的,都是b+tree结构)

      • 对于查询返回某范围内的数据,建议使用聚簇索引,不建议使用非聚簇索引(非聚簇索引,叶子节点存储的是主键值,需要回表)

      • 列中只有一个值或极少的不同值,不建议使用索引

      • 小数目的不同值使用聚簇索引,大数目的不同值使用非聚簇索引(这里有疑问?需要思考)

      • 频繁更新的列不建议使用聚簇索引,建议使用非聚簇索引(因为,频繁更新会造成聚簇索引的分裂,

      • 动作使用聚簇索引使用非聚簇索引
        经常被分组排序
        返回某范围内的数据×
        一个或极少不同值××
        小数目的不同值×
        大数目的不同值×
        频繁更新的列×
        外键列
        主键列
        频繁修改的列×
    • 术语’聚簇‘表示数据行和相邻的键值聚簇存储在一起。

    • 在这里插入图片描述

    • 聚簇索引的好处

      • 按照聚簇索引排序顺序,查询显示一定范围数据的时候,由于数据都是紧密相连的。数据库不用从多个数据块中提取数据,所以节省了大量io操作。
    • 聚簇索引的限制

      • 对于mysql数据库目前只有innodb数据引擎支持聚簇索引,而Myisam并不支持聚簇索引(因为Myisam只存储索引值,不存储数据信息)。
      • 由于数据物理存储排序方式只能有一种,所以每个mysql表只能有一个聚簇索引(也就是主键索引)。一般情况下就是该表的主键
      • 为了充分利用聚簇索引的聚簇特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议使用无序的id,比如uuid这种。(选用uuid会导致聚簇索引失去意义)
    • 一个误区:把主键自动设为聚簇索引

      • 聚簇索引默认是主键:如果表中没有定义主键,innodb会选择一个唯一的非空索引代替。如果表中没有主键也没有唯一的非空索引,InnoDB会隐式地定义一个主键来作为聚簇索引。InnoDB只聚集在同一个页面中的记录,包含相邻键值的页面可能相距甚远。如果要设置非主键的聚簇索引,必须先删除主键,然后添加想要聚簇的聚簇索引,最后恢复主键设置即可。
      • 此时其他索引只能被定义为非聚簇索引。这是个最大的误区。有的主键还是无意义的自动增量字段,这样的话Clustered index对效率的帮助就被完全浪费了。
      • 聚簇索引的性能最好而且具有唯一性,有序性,所以非常珍贵,需要慎重设置。一般根据表最常用的SQL查询方式进行选择,某个字段作为聚簇索引,或组合聚簇索引,这个要看实际情况。
      • 最终的目的就是在相同结果集的情况下,尽量减少磁盘IO
mysql索引分类
  • 单值索引

    • 即一个索引只包含单个列,一个表可以有多个单列索引

    • 语法

      • -- 随表一起创建单值索引
        create table customer(
        	id int(10) unsigned AUTO_INCREMENT,
            customer_no varchar(200),
            customer_name varchar(200),
            PRIMARY KEY(id),
            KEY(customer_name)
        );
        
        -- 单独创建单值索引
        create index idx_customer_name on customer(customer_name);
        
        -- 删除单值索引
        drop index idx_customer_name on customer;
        
  • 唯一索引

    • 索引列的值必须唯一,但允许为空值

    • 语法

      • -- 随表一起创建索引
        CREATE TABLE customer(
        	id int(32) UNSIGNED AUTO_INCREMENT,
        	customer_no varchar(200),
        	customer_name varchar(200),
        	Primary key(id),
            key(customer_name),
        	unique(customer_no)
        )
        -- 单独创建唯一索引
        create unique index idx_customer_no on customer(customer_no);
        
        -- 删除唯一索引
        drop index idx_customer_no on customer;
        
  • 主键索引

    • 设定为主键后数据库会自动创建索引,innodb为聚簇索引

    • 语法

      • create table customer(
            id int(32) unsigned auto_increment,
            customer_no varchar(200),
            customer_name varchar(200),
            primary key(id),
        )
        
        -- 单独创建主键索引
        alter table customer add primary key customer(customer_no);
        
        -- 删除主键索引
        alter table customer drop primary key;
        
        -- 修改主键索引:必须先删除掉原主键索引,再新建新的主键索引
        
  • 复合索引

    • 一个索引包括多个列

    • 语法

      • -- 随表一起创建复合索引
        create table customer(
        	id int(32) unsigned auto_increment,
            customer_no varchar(200),
            customer_name varchar(200),
            primary key(id),
            key(customer_name),
            unique(customer_no),
            key(customer_name,customer_no)
        )	
        -- 单独创建复合索引
        create index idx_no_name on customer(customer_no,customer_name);
        
        -- 删除索引
        drop index idx_no_name on customer;
        
  • 基本语法

    • 创建
      • CREATE [UNIQUE] INDEX indexName ON myTable(columnname(length));
      • ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnname(length));
    • 删除
      • DROP INDEX [indexname] ON mytable;
    • 查看
      • SHOW INDEX FROM table_name\G
    • 使用ALTER命令
      • ALTER TABLE tbl_name ADD PRIMARY_KEY(column_list);该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL;
      • ALTER TABLE tbl_name ADD UNIQUE index_name(column_list);这条语句创建的索引值必须是唯一的(除了NULL外,NULL可能会出现多次)
      • ALTER TABLE tbl_name ADD INDEX index_name(column_list);添加普通索引,索引值可出现多次。
      • ALTER TABLE tbl_name ADD FULL TEXT index_name(column_list);该语句指定了索引为FULLTEXT,用于全文索引。
  • 索引的结构

    • BTree索引
    • Hash索引
    • full-text索引
    • R-Tree索引
需要建索引的情况
  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 查询中与其他表关联的字段,外键关系建立索引
  • 频繁更新的字段不适合创建索引
    • 因为每次更新不单单是更新了记录还会更新索引
  • where条件用到的字段可以创建索引
  • 单值索引和复合索引的选择,尽量选择复合索引
  • 查询中排序的字段可以建索引,排序字段若通过索引去访问将大大提高排序速度
  • 查询中统计或者分组字段需要创建索引
不需要建索引的情况
  • 表记录太少的情况
  • 经常增删改的表
    • 因为索引提高了查询的速度,同时会降低表更新的速度,如果对表进行INSERT,UPDATE和DELETE。因为更新表时,mysql不仅要保存数据,还要保存索引文件。
  • 数据重复且分布平均的表字段不适合间索引,因为如果某个数据列包含许多重复的内容,对这个列建索引没有太大的意义,因为查询到的任然是大量数据,因此应该只为经常查询和经常排序的数据列建立索引
    • 假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值分布概率大约为50%,那么对这种表A字段建立索引一般不会提高数据库的查询速度。
    • 索引的选择性是指索引列中不同值的数目和表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,呢么这个索引的选择性就是1980/2000 = 0.99。一个索引的选择性越接近于1,这个索引的效率就越高。
  • where条件里用不到的字段不创建索引
  • Mysql数据100万到300万左右,性能开始下降。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值