表妹问我innodb的引擎是怎么设计的

大鱼仙人

共 4654字,需浏览 10分钟

 · 2021-11-18


hello大家好 我是大家的学习成长小伙伴Captain



面试官:平时用的数据库有哪些呢


表妹:亲爱的面试官你好,我平时用的最多的数据库就是mysql了,除此之外,还用过oracle、MongoDB、hbase,缓存redis以及数据仓库hive


面试官:不错,会的不少,今天我们只聊聊你最常用的mysql,应该也是你最熟悉的吧


表妹:理论上来说是这个样子,你先问个问题试试,我看看答不答的上来啦


面试官也很豪爽:可以,那你给我说一下innodb引擎是怎么设计的吧


表妹:... ... 那个面试官,我突然想起来今天早上出门的时候,家里的煮蛋器上还煮着好几个鸡蛋呢,我得赶紧捞出来去,不然我怕鸡蛋的皮给煮没了


面试官:好的,赶紧去吧,下次来面试的时候记得给我带一个鸡蛋


玩归玩,闹归闹,别拿面试官开玩笑


开玩笑,不要紧,沮丧出大门待通知


表妹回到家泪汪汪,直接冲到我的屋子来质问我,为什么你上次给我说没有mysql的知识点的时候,没有告诉我面面试官会问这么霸道的问题呢


表妹:我把索引啊、B+树啊啥的都准备了,结果她问我innodb怎么设计的,这我怎么知道的,他怎么不去问mysql的设计师去啊


我:没关系,真正的勇士敢于面对惨烈的人生,我来带着你分析分析,下次保证你能和面试官愉快的沟通,不用再编理由回家来捞鸡蛋了


呜呜呜呜...



开题


首先呢,我们先普及一下,mysql是一个关系型数据库,而数据库的重点就是存储数据,这个引擎的作用就是如何来把这个数据存储到数据库服务器的硬盘上,存储的什么结构,靠什么策略存储


如果每次都存储直接保存到硬盘,那是不是会导致速度很慢,我们可以先把数据保存到服务器的内存中,再通过一定的持久化规则存储到硬盘上,那么问题来了,到底是多少条数据保存一次呢,还是几分钟一次呢,这些都是属于策略问题


mysql结构



我们可以看到mysql的结构,来简单说一下流程


客户端连接器:这个就是和各个编程语言进行交互连接的,JDBC应该是大家最熟悉的了吧


系统管理和控制工具:进行mysql中的元数据、备份、还有配置信息这种的系统管理和控制工具


连接池:管理用户的连接、线程处理这些需要缓存的数据


SQL接口:接受SQL语句,返回查询结果


解析器:进行SQL的解析,分解SQL语句,验证SQL合法性


查询优化器:解析之后进行查询优化,分析出最优解的执行计划,注意,这里是最优执行计划,但是并不一定代表是速度最快的执行计划


缓存:查询缓存,查询的时候先去查询缓存看是否命中缓存,如果命中则直接取数据,属于一个优化


可插拔存储引擎:这个就是属于我们这一篇要关注的,多种存储引擎,对应着不同的存储机制,不同的事务和锁机制等,innodb是其中的一个引擎,也是用的最多的引擎


文件和日志:存储到硬盘中的数据文件和日志文件,日志就是典型的那些binlog、redo log、undo log这些日志文件


innodb的结构



接着我们看innodb的引擎的设计


innodb主要可以分为两大块:In-Memory Structures内存结构和On-Disk Structures磁盘结构


innodb使用的属于内存和日志先行的一种策略,就是会把修改的数据先存储到内存中,然后将事务记录保存到redo log日志文件中,这个日志文件就是用来保证数据库事务中的持久化这个特性的


先写到内存和日志,再通过一定的策略将数据进行持久化,只要将数据修改在内存中完成和记录在日志之后,对应的事务就会返回给用户,表示该事务已经完成了。但是实际上这个数据是还没刷到硬盘中的


也就是实际上可能会存在部分数据丢失,innodb通过redo log日志来保证数据的一致性,如果保证所有的重做日志,也就可以在系统崩溃的时候通过日志来进行数据的恢复


innodb还引入了检查点机制,即定期检查,保证检查点之前的日志都已经写到了磁盘中,则下次恢复只需要从检查点开始即可


In-Memory Structures内存结构


主要包含Buffer Pool、Change Buffer、Adaptive Hash Index和Log Buffer这四部分组成,然后从内存结构也可以看出来,Change Buffer和Adaptive Hash Index占用的内存也是属于Buffer Pool中的,而Log Buffer占用的内存和Buffer Pool的内存是相互独立的



Buffer Pool

The buffer pool is an area in main memory where InnoDB caches table and index data as it is accessed.

我们在上面也说过,mysql为了加速数据的查询是做了很多的优化的,这个就是其中一点,mysql更新数据的时候并不会直接去修改磁盘中的数据,这样做是很影响效率的,每次都往硬盘中写数据肯定是不适合现在这种互联网时代


mysql先修改内存,再记录到redo log中,然后再根据相应的策略将数据更新到硬盘中


而这些数据的存放地方就是Buffer Pool


大家在平时的开发中肯定也使用过缓存,甭管是Redis还是Memcached,都是我们用于系统中的加速策略,查询一个数据先去缓存中看一圈是否有,有的话直接把数据返回即可,没有的话我们才会去数据库中进行查询


在现在这种系统中,我们都会把系统的一些热点数据或者是访问量比较高的数据存储到redis中,防止每次都查数据库造成数据库的压力剧增


在使用缓存的过程中,我们也要注意缓存血崩、缓存穿透和击穿这些问题,其实只要我们做好了防护,这些问题还是都比较好避免的,不太清楚的小伙伴可以去看我的redis篇


哦对了,我的后续的文章都会及时更新到我的GitHub地址上 https://github.com/DayuMM2021/Java


mysql是以页为单位从磁盘中读取数据的,而Buffer Pool也是这样的,Buffer Pool的数据是一个以页为单位的链表


那Buffer Pool自然不是无限大的,自然就需要相应的淘汰策略,来进行数据的清楚


缓存池的使用情况可以通过show engine innodb status命令来查看,其中的一些主要信息


----------------------BUFFER POOL AND MEMORY----------------------Total large memory allocated 137428992 # 分配给InnoDB缓存池的内存(字节)Dictionary memory allocated 102398  # 分配给InnoDB数据字典的内存(字节)Buffer pool size   8191 # 缓存池的页数目Free buffers       7893 # 缓存池空闲链表的页数目Database pages     298  # 缓存池LRU链表的页数目Modified db pages  0    # 修改过的页数目......


Change Buffer


上面我们说了Buffer Pool主要是用于优化查询速度的,那么如果我们要修改数据呢,要是每次更新都直接更新到磁盘中,这样肯定是很慢的


甚至如果更新的数据在磁盘中的页是无规则的,那每次都需要把数据更新到不同的页中,会大大的影响速度


Change Buffer就是用于这个的,mysql如果发现你要修改的数据不在Buffer Pool中,就把你要修改的数据先记录到Change Pool中,同时记录到redo log日志中,然后再把这一页的数据load到内存中


load到内存之后,会把Change Buffer里面的记录进行修改,应用到内存Change Buffer中,这个动作叫做merge


接着把内存数据刷到磁盘中,这个动作叫做purge




查看Change Buffer 信息也可以通过 show engine innodb status 命令。


merge:Change Buffer - Buffer Pool


purge:Buffer Pool - 磁盘


Adaptive Hash Index


哈希索引我们知道是查询速度很快的,缺点是不擅长进行范围查询,我们一般使用的索引都是B+树,而B+树的查询速度则取决于树的高度了


不论数据在磁盘中,还是load到内存中的数据,存储结构都是B+树,我数据都已经放到Buffer Pool中了,还得通过B+树的多次IO才能查找到数据,有些繁琐


于是自适应哈希索引就来了,对于那些经常被访问的数据,不需要每次来了之后都走B+树的多次查询了,直接把数据的指针位置记录下来即可


自适应,我们是无法干预的,mysql自动评估是否适合使用这个自适应哈希索引,如果合适,则会建立数据的自适应哈希索引


Log Buffer


重做日志redo log中的缓冲区,大小默认16M,由innodb_log_buffer_size定义,一个比较大的log buffer可以让大的事务在提交前不必将日志中途刷到磁盘中,也可以提高效率


如果系统中涉及到很多的大事务,可以适当的加大该值


On-Disk Structures磁盘结构


这个属于操作系统提升性能,在磁盘前面加的一层高速缓存


表空间分为系统表空间、临时表空间、常规表空间、Undo表空间和file-per-table表空间


系统表空间又包括了InnoDB数据字典、双写缓冲区Doublewrite Buffer、修改缓存和Undo日志等


Redo日志存储的就是Log Buffer刷到磁盘里的数据


系统表空间


包含内容有数据字典,双写缓冲,修改缓冲以及undo日志,以及在系统表空间创建的表的数据和索引。


常规表空间


类似系统表空间,也是一种共享的表空间,可以通过 CREATE TABLESPACE 创建常规表空间,多个表可共享一个常规表空间,也可以修改表的表空间。


注意:必须删除常规表空间中的表后才能删除常规表空间。


File-Per-Table表空间


MySQL InnoDB新版本提供了 innodb_file_per_table 选项,每个表可以有单独的表空间数据文件(.ibd),而不是全部放到系统表空间数据文件 ibdata1 中。


其他表空间


其他表空间中Undo表空间存储的是Undo日志。


除了存储在系统表空间外,Undo日志也可以存储在单独的Undo表空间中。


临时表空间则是非压缩的临时表的存储空间,默认是数据目录的 ibtmp1 文件,所有临时表共享,压缩的临时表用的是 File-Per-Table 表空间


  结束语


Captain希望有一天能够靠写作养活自己,现在还在磨练,这个时间可能会持续很久,但是,请看我漂亮的坚持


感谢大家能够做我最初的读者和传播者,请大家相信,只要你给我一份爱,我终究会还你们一页情的。


Captain会持续更新技术文章,和生活中的暴躁文章,欢迎大家关注【Java贼船】,成为船长的学习小伙伴,和船长一起乘千里风、破万里浪


哦对了,后续所有的文章都会更新到这里


https://github.com/DayuMM2021/Java








浏览 20
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报