MySQL(InnoDB)高级篇(持续更新)

MySQL高级篇(InnoDB)

1.索引

1.InnoDB存储引擎----B+ Tree索引

B+树是mysql索引的数据结构之一(最多使用)。

这里我们可以先说说mysql为什么没有使用二叉树、红黑树来存储索引:

这里强烈推荐一个网站,可以演示各种数据结构:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

二叉树

我们就随便写数据:依次加入5,6,7,8,9这五个数据

image-20211116114021584

可以看到直接二叉树成了链表结构了,这时候要查找9,就得经过5次io,所以如果你设置自增列为索引,那性能就和全表扫描相差无几,并且还需要额外的索引存储空间

红黑树

红黑树是一颗平衡二叉树,要求每个节点的左子树和右子树高度差不超过1,

还是添加56789:

image-20211116132422274

现在我们查找9,只需要三次io。这看起来比二叉树好很多,但是为什么mysql没有使用这种方式呢?

因为,当数据量上去后,红黑树的高度会很高image-20211116132854122

这就会导致io次数多,效率低

B+树

我画了一张图:

image-20211118161540834

B+树是采用分页的方式来存储索引,默认每页16kb。(就是相对二叉树,B+树的每个节点存储一个页的索引,树高度也就降下来了)

叶子节点才存储着数据,并且用指针相连

排好序的

B+树在查询某个索引时,会将那一页的数据加载到内存缓存起来,这样同页的索引会更快。好像最顶层的页是常驻内存的,以减少io次数,加快查询速度。

通过使用冗余数据来实现树结构,比如第一页的首个索引值为12,当这一页满了就在父节点创建一个12,其中的指针指向第一页所在的磁盘地址,50同理。比如现在要查找15,12<15<50,所以15在12所指向的页。然后顺次找到15拿到数据。

2.索引分类

存储引擎是作用于表的,不是数据库

查看索引:

show index from 表

删除索引:

drop index 索引名 on 表名

1.主键索引

当你没有创建主键时,则会使用一个唯一索引作为主键索引,没有unique索引,InnoDB存储引擎会为每一行生成一个6字节的RowID作为主键

2.单列索引(普通索引)

//建表时
key(列名)
//建表后
create index 索引名 on 表名(列名)

3.复合索引(多个列)

//建表时
key(列名,列名...)
//建表后
create index 索引名 on 表名(列名,列名,...)

4.唯一索引

//建表时
unique(列名)
//建表后
create unique index 索引名 on 表名(列名)

3.聚簇索引、非聚簇索引

聚簇索引:就是索引和数据存在一起,B+树就是这样的,行数据存在叶子节点

非聚簇索引(辅助索引):索引和数据分开放,索引结构的叶子节点存储的是主键值,所以非聚簇索引需要二次查找,意思就是先查找到数据的主键,然后去聚簇索引中找到这个主键。(之所以是存主键而不是直接存物理地址,因为增删改时会导致地址发生变化)

InnoDB中除了主键索引(或者唯一索引成为了主键),其它索引都是非聚簇索引,都需要先根据索引找到主键id,再用主键索引找到行数据

4. InnoDB存储引擎—hash索引

hash想必大家都很熟悉了,在InnoDB中的hash和hashmap的结构差不多,数组加链表。

image-20211118101147665

其索引的效率会比B+树高,因为只需要对索引值进行一次hash计算就能找到在hash数组的哪里,然后顺着链表找。

之所以B+树使用最多是因为,Hash索引不支持范围查找,因为hash本就是追求散列,其保存的数据是没有规律的,比如上图中,我们要查询小于50的,hash值为2、23的链表都有,也就是说范围查找对于hash来说,只能全表扫描!而企业中范围查找是常用的,所以B+树自然使用多

5. 使用InnoDB,为什么推荐使用自增的整数类型作为主键?

既然是使用B+树:

1.最好自己要创建主键

因为InnoDB在你没有显示的定义主键时,会为每一行创建一个6字节的RowId作为主键。这本来是我们可以做的事,交给InnoDB去做会增加其工作量,降低工作效率

2.为什么推荐主键要整型?

前面说了,InnoDB是会对索引排序的,整数排序会比字符串排序更快(字符串需要一位一位的比)

3.为什么推荐主键自增

同样也是因为InnoDB的索引排序,如果主键不是自增的,就意味着在B+树上会进行插入,会破坏原有的树结构(因为是平衡二叉树),可能造成节点的分裂,性能消耗就比较大了。而使用自增则永远只会在最后面加,节点变动更自然规律,会比插入好。

6.复合索引最左前缀原则

最左前缀:

比如我们建了一个复合索引(name,age,sex),那么以下哪些能够使用到复合索引呢?

CREATE TABLE `user1` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `sex` int(1) DEFAULT NULL,
  `test` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`name`,`age`,`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • explain select * from user1 where name=‘xx’ and age=18
  • explain select * from user1 where age=18 and sex=1
  • explain select * from user1 where name=‘xx’ and sex=1 and age=18
  • explain select * from user1 where sex=1 and name=‘xx’ and age=18

第一个、第三个、第四个可以。

这里需要说到最左前缀原则,即复合索引最左边的列(name)必须在查询条件的最前面,后面的复合列不论有不有、顺序如何都能够使用到复合索引。

mysql也很贴心,帮我们做了优化,比如第四个句子,name不在第一个,但是也能使用到复合索引。就是因为mysql帮我们调整了顺序

综上,只要是查询条件中有复合索引的最左边的那个列,就能使用到复合索引

ps:这里我做实验时发现了一个东东:

先说一下现象:当一个表的所有列由主键加复合索引组成时,没有其它多余列(或者所有列组成复合索引),比如一个表(id,name,age,sex)id是主键,name+age+sex是复合索引。这种情况最左前缀就不成立了,无论你怎样组合复合索引中的列,age,age+sex这些都能使用到复合索引

我创建了两个表。两个表的字段、索引都相同。一个表列全部组成主键和复合索引,另一个表在第一个表的基础上加上一个普通字段。

然后我将两个表的索引文件用md5(md5加密算法可以用来判断两个文件内容是否一样)算了一下是不同的(因为多了一个字段,b+树叶子节点要存数据,算出来自然不一样,不过也不敢排除其它原因),接着我又调用了一下两个文件输入流的available()方法,发现是一样的。

我猜测是因为没有其它多余的列了,而复合索引本身就已经是一行数据了,没有其它字段要查了。。。貌似也解释不了不满足最左前缀也能使用复合索引。。。。。。

经过老师的解答,是用到了索引覆盖,什么叫索引覆盖?https://www.cnblogs.com/happyflyingpig/p/7662881.html

  • 解释一: 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。

  • 解释二: 索引是高效找到行的一个方法,当能通过检索索引就可以读取想要的数据,那就不需要再到数据表中读取行了。如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫 做覆盖索引。

  • 解释三:是非聚集组合索引的一种形式,它包括在查询里的Select、Join和Where子句用到的所有列(即建立索引的字段正好是覆盖查询语句[select子句]与查询条件[Where子句]中所涉及的字段,也即,索引包含了查询正在查找的所有数据)。

    SQL语句只通过索引,就取到了所需要的数据,这个过程就叫做索引覆盖

所以为什么要满足最左前缀才能使用复合索引?

对于(name,age,sex)作为复合索引,B+树是先对name进行排序,两个name相同的情况下,再比较它两的age进行排序,如果age也相同就使用sex排序。即大体排序是用name,出现name相同,使用复合索引中的列顺序进行比较排序。

所以,你的查询条件name不放在最前面,它怎么去查?没法使用该复合索引了

Guess you like

Origin blog.csdn.net/qq_42682745/article/details/121367136