看完了MySQL小册,我为何强烈推荐?

9,271 阅读14分钟

本文有福利相送,作为我鸽了这么久的歉意。

朋友们我又回来了,距离上次发文应该过了一个月了,期间有很多个人的琐事,导致我的文章不能正常的更新,给大家说声抱歉。

从上周开始吧,我开始看我在掘金很久以前买的一本小册:《从根上理解Mysql是怎么运行的》,本着买了会一半的原则,它一直在我的掘金里面吃灰。

直到最近,看到一些Java交流群里MVCC这个名词在我的时间线上反复出现,我知道这是提示我去好好梳理一下Mysql相关的知识了。

于是我花了一周的时间把这本小册从头到尾的好好看了一遍,有些章节可能反复看了四五遍,自认这本小册里的知识我搞懂了1/3,真的很惭愧,居然还不到一半,因为这个号称从根上理解Mysql的小册涉及到了很多概念,短短一周真的很难全部梳理清楚。

总体上来说,是一本干货满满的性价比好书,从行文内容上也可以看出作者的功底,值得二刷三刷。(这本书貌似准备在机工出版了,已经说了半年了,估计就一个月之内了~)

我不是来写软文,也不是搞推荐,很纯粹的分享一下最近看这本书的感受,这本书很适合会CRUD但对Mysq不甚了解的同学。

简单画一个导图给大家看一下这本书都讲了什么:

我将小册讲诉的所有内容分为七部分,接下来我将根据导图的各个部分说说我的看法。

InnoDB

大家如果查看过这本小册的目录,可以发现其前四章都是关于一些基础内容的,比如启动Mysql-Server,使用Mysql-Client进行连接和更改字符集什么的,所以这部分我并没有标在导图上,有一个大概了解就可以。

在这些基础之后,就开始了小册第一个硬菜:InnoDB原理的探究

InnoDB作为我们最常用的Mysql存储引擎,作者对它的笔墨也是最多的,因为只有它支持事务,支持锁,所以在这部分讲述的很多内容都和后面的部分相呼应。

作者从一条记录说起,给我们娓娓道来了InnoDB内部的数据管理方式:

一条数据库表记录会以一种叫记录格式的方式存储,以5.7为例列举了MySQL中四种行格式的不同组成结构及其特性。

记录格式说完之后,又要说说记录格式放在哪了?记录格式放在一个名为的数据结构上,MySQL中有很多不同的页用以不同的用处。

每个页是16KB大小,MySQL在读取数据或者持久化数据的时候都是以页为基本单位,也就是说一次性最小会读取或持久化16KB数据。

每次需要写入数据时,都会向对应的数据页申请内存,直至填满之后会申请下一个页,如果是删除数据则会标记为删除,但是数据被删除并不会移除此页,而是被删除的数据组成一个垃圾链表,等待新的数据来将其覆盖

那么页说完了,页又从属于谁呢?这时又引入了表空间的概念。

一个表对应一个表空间文件(表名.ibd),这个表空间文件里就是实际存放了数据的文件,数据记录被页管理,页又被表空间管理,这是一个层层递进的从属关系。

但是因为用表空间直接管理页可以跨度太大(一个表空间对应所有表数据,但是一个页才16KB,数据稍微大一点,就是一个表空间对应上万甚至几十万的页),所以表空间里面又进行分层:区和组

64个页对应一个区(64*16=1MB),256个区又对应一个组(256MB),这样划分之后一个逻辑从属关系就变成了:记录->数据页->区->组->表空间

同时为了加快加载速度,几乎所有系统都会用到的缓存就被引入了-缓存池。

缓存池的引入大大提高了数据页查询的效率,毕竟磁盘IO太太太慢了,缓存池会在MySQL启动时申请一块内存作为数据页的缓存池(数据库优化的时候可以谈谈这个思路),同时根据算法来较好的控制缓存池里的数据(最常访问啊什么的,这块蛮复杂的)。

至此,InnoDB的基础原理部分基本就是结束了,这部分我看下来最复杂的就是表空间部分,因为涉及到诸如索引之类的东西,可以说表空间这节是整本小册最难的,我看小册评论区也都是如此想法。

索引

看小册的时候本来就是抱着看索引的想法去的,但是没想到索引的篇幅很短,只有两章:索引原理和索引用法。

其实看小册的时候,我一般都是对照着看的,看完作者写的再去网上看看别人写的,两者结合起来去理解。

索引这块也是如此,但是作者写的真是蛮通俗易懂的,索引就是在行记录的基础上进行主键排序,因为主键是有序的所以用主键作为节点构成一颗B+树,这颗B+树的叶子节点就是所有的记录,非叶子节点是一种名叫目录树的东西,可以理解为对叶子节点进行分组用的,因为是B+树,所以使用二分查找可以很快的查找到对应节点。

这种非叶子节点直接是行记录数据的索引被称为聚簇索引,这种索引会在InnoDB中自动生成,且因为它叶子节点是所有的记录,所以它也是很占空间的东西。

我们手动进行生成的索引叫做二级索引/辅助索引,这种索引生成后也会构成一个B+树,但是此树叶子节点并不记录行数据,而是记录对应行数据的主键,当你用二级索引拿到主键后,还要用主键重新搜索一次数据,这个过程叫做回表

如果创建二级索引时指定了多个列,这种索引叫做覆盖索引

除了这些作者还顺带讲了MyISAM的索引,它没有聚簇索引都是二级索引,但二级索引和InnoDB的又有些不同,MyISAM的索引中记录的不是主键而是行号,可以通过对行号直接找到对应数据,这样就少了一次回表过程。


懂了索引之后,再去看索引用法的注意事项就简单多了~,比如:

  1. 索引列的类型尽量小(减少建立索引耗费的空间)
  2. 删除表中的重复和冗余索引(索引列只会用到一个索引)
  3. 数据库主键自增(避免主键无序,这样会频繁打乱B+树的节点,因为B+树依靠主键排序)
  4. where 条件尽量索引字段的顺序和创建联合索引时字段顺序一致(因为覆盖索引创建是按照多个索引字段值从左到右进行排序的)
  5. 模糊匹配指定左前缀可以用到索引(因为索引值排序时是按照从左到右,所以如果左侧值指定了也可以用到索引缩小范围)

还有很多其他的注意事项,这块网上基本说的都够了,我这里不再赘述,避嫌。

单表的访问方式

单表的访问方式,咋一听很难理解的一个名词,其实是MySQL的设计者给不同的查询设立了不同的叫法,比如用主键查询速度非常快,这种叫const,意为常量级查询,复杂度可以忽略掉。

这些都是一些知道了解的东西,因为算是一种规定,同时这部分是Explain的铺垫,Explain会用到这里面的知识。

Explain

Explain的知识网上博客讲的也很清楚了,因为都是一些概念性的知识,我以前学MySQL的时候看的周阳的课,里面也有讲到Explain相关知识。

它主要是列出了一条查询语句都走了哪些索引,影响了多少行数,个人觉得其实效用不大,因为一条SQL交给执行器去执行之后,会有优化器进行优化,优化过的SQL可能已经和原来你编写的不一样了,需要了解优化器优化过程才能看懂这个。

这种情况你可以使用optimizer_trace去查看SQL的执行过程来知道优化器都做了哪些事。

也可以用其他方式比如:show status like '%last_query_cost%'

这条语句可以查询上条查询语句使用了多少成本,在SQL优化器对一个语句进行优化时会尝试计算不同方案花费了多少成本,最终使用最少成本的那个优化方案,我觉得这种方式可以比较直观的对比自己编写的两个SQL的性能差距。

子查询优化

在优化器优化这块,小册主要讲了优化器进行优化的一些方案,不过主要讲的还是子查询的优化方案。

事务

InnoDB基础,索引,事务和锁,可以说是看完这本小册必须要了解的四大块了。

事务里的概念很多,ACID就不说了,既然是讲原理的书,那作者的笔墨就主要用在MySQL用了什么方案来保证事务和事务的回滚

MySQL使用redo日志来记录事务执行中的语句,用undo日志记录语句执行前记录的值,同时每个事务都有一个全局ID。

redo日志可以保证系统哪怕崩溃了,重启回来之后还能根据里面的日志恢复到原样,undo则可以保证需要回滚时有对应的记录,undo还有一个作用,就是用来做MVCC。

MVCC(Mutil-Version Concurrency Control)-多版本并发控制,它是MySQL中用来解决并发读写的问题的一种方案。

在高并发的程序中,数据库往往会同时开启多个事务,执行过程中多个事务交替执行,这就可能会出现事务并发问题:脏写,脏读,不可重复读和幻读。

这里我们再来复习一下这四个概念:

  1. 脏写:一个事务修改了另一个未提交事务修改过的数据
  2. 脏读:一个事务读到了另一个事务未提交修改过的数据
  3. 不可重复读:一个事务能读到此事务开启后其他事务提交修改的值
  4. 幻读:一个事务中两条同样的查询条件进行查询时,后读到的数据比前一次多

这四个问题都可以通过锁来解决,但是这样的话性能必然是会下降的,所以在MySQL中选择这样做:

脏写一般是由锁来完成的,也就是一个时间点只有一个事务对一条记录进行写,避免脏写。

脏读,不可重复读,幻读则可以由MVCC来控制,增强MySQL的并发性能,其原理是通过undo日志维护了一个版本链,版本链每个版本上面都有一个事务id来标识这个版本是由哪个事务修改的,当前事务进行查询时会根据当前事务id进行计算可以看到这个版本链上的哪些记录,从而去避免脏读,不可重复读和幻读。

网上有些解读MVCC的时候说这是版本链+乐观锁,看了小册之后我在网上翻到了MySQL中关于这块的源码,和小册上面讲诉的一样,是通过计算(或者说判断)来决定能看到哪个版本,乐观锁一般是比较新值和旧值,实际情况和乐观锁不太相像。

事务这块看完之后,发现数据库常问的MVCC也没有那么神秘,可以说是一句话就能说清的事。而且后来我忽然想起来seata这个分布式事务的AT模式,它的原理实现其实就和MySQL里面的事务原理几乎是一样的。

有了前面的铺垫之后,看锁这一章会简单许多,但是小册上面的锁这一章仅仅是介绍了锁的分类,也就是说平常MySQL在用锁的时候一般会用到哪些锁,他们分别有什么特性,并没有去深入的去分析两个事务同时修改一条记录的情况下锁是怎么做的。

但是并不是说作者没写,而是写在公众号里面了,,,关注公众号之后可以去看有三篇加锁实战分析的文章。

锁其实可以通过两个维度来划分:粒度和特性。(当然这是我自己的理解)

  • 粒度:根据影响的范围可以分为行级和表级。
  • 特性:根据锁的特性可以分为独占或共享。

两个维度组合一下一般就是四种:行级独占锁、行级共享锁、表级独占锁和表级共享锁。

除了这几种锁的具体实现,还介绍了锁在内存中的结构,虽然我前面说了这章很重要,但我自己也没有太过于细看,因为都是概念性需要记忆的东西。

其实这章结合公众号的加锁实战分析的文章是比较好的,这章就像是锁实战的先行知识,不然你直接去看锁实战,各种概念一上来恐怕给人搞得头都大了。

小结

这篇文章给我写的非常尴尬,因为有很多想多写出来的知识又不能写(文章内容最多只能由30%知识来源于小册),所以我本来是想从逻辑上详细给大家讲讲这本小册的内容,但是又得避嫌(侵权啊,抄袭啊),最后就感觉有点虎头蛇尾了(极力推荐查看我的导图)。

不过像高频率的一些知识点,我还是写到的了,比如索引原理和事务原理(不过有点简化),大部分开发应该是都没有这个知识深度,如果能好好记忆下里面的内容,面试的时候其实可以往深了说,能把一点说深总比你说一堆浅显易懂的知识点来的有用。

总体来说这本小册还是比较推荐的,是一本深挖原理的书,有些知识点比如bin log这种偏应用层的没有讲到,其他的基本上都是很详细了,用一下小册优惠码应该不到20块钱,性价比很高。

文章开头也说了,本文有一点点福利,给大家抽三个小册六折码(非赞助,都是我之前参加征文活动攒的),鉴于上次抽奖的教训(因为没有私信,迟迟联系不上中奖者),这次打算转移到公号去抽奖。

  • 只需关注我的同名公众号(和耳朵),回复抽奖就有专属的抽奖连接,十月一日中午十二点开奖。
  • 还有我的MySQL思维导图文件,回复MySQL关键字就能获取脑图。

好了,今天的文章就先到这,欢迎大家点赞支持,我是和耳朵,知识输出,共同成长。