推荐阅读:
存储引擎
不少文章都是直接开始介绍有哪些存储引擎,并无去介绍存储引擎自己。那么究竟什么是存储引擎?不知道你们有没有想过,MySQL是如何存储咱们丢进去的数据的?缓存
其实存储引擎也很简单,我认为就是一种存储解决方案,实现了新增数据、更新数据和创建索引等等功能。安全
有哪些已有的存储引擎可让咱们选择呢?架构
InnoDB、MyISAM、Memory、CSV、Archive、Blackhole、Merge、Federated、Example性能
种类不少,可是经常使用的存储引擎目前就只有InnoDB和MyISAM,我也会着重来介绍这两种存储引擎。学习
InnoDB是目前使用最广的MySQL存储引擎,MySQL从5.5版本开始InnoDB就已是默认的存储引擎了。那你知道为何InnoDB被普遍的使用呢?先把这个问题放一放,咱们先来了解一下InnoDB存储引擎的底层原理。优化
InnoDB的内存架构主要分为三大块,缓冲池(Buffer Pool)、重作缓冲池(Redo Log Buffer)和额外内存池url
缓冲池
InnoDB为了作数据的持久化,会将数据存储到磁盘上。可是面对大量的请求时,CPU的处理速度和磁盘的IO速度之间差距太大,为了提升总体的效率, InnoDB引入了缓冲池。
当有请求来查询数据时,若是缓存池中没有,就会去磁盘中查找,将匹配到的数据放入缓存池中。一样的,若是有请求来修改数据,MySQL并不会直接去修改磁盘,而是会修改已经在缓冲池的页中的数据,而后再将数据刷回磁盘,这就是缓冲池的做用,加速读,加速写,减小与磁盘的IO交互。
缓冲池说白了就是把磁盘中的数据丢到内存,那既然是内存就会存在没有内存空间能够分配的状况。因此缓冲池采用了LRU算法,在缓冲池中没有空闲的页时,来进行页的淘汰。可是采用这种算法会带来一个问题叫作缓冲池污染。
当你在进行批量扫描甚至全表扫描时,可能会将缓冲池中的热点页所有替换出去。这样一来可能会致使MySQL的性能断崖式降低。因此InnoDB对LRU作了一些优化,规避了这个问题。
MySQL采用日志先行,在真正写数据以前,会首先记录一个日志,叫Redo Log,会按期的使用CheckPoint技术将新的Redo Log刷入磁盘,这个后面会讲。
除了数据以外,里面还存储了索引页、Undo页、插入缓冲、自适应哈希索引、InnoDB锁信息和数据字典。下面选几个比较重要的来简单聊一聊。
插入缓冲
插入缓冲针对的操做是更新或者插入,咱们考虑最坏的状况,那就是须要更新的数据都不在缓冲池中。那么此时会有下面两种方案。
- 来一条数据就直接写入磁盘
- 等数据达到某个阈值(例如50条)才批量的写入磁盘
很明显,第二种方案要好一点,减小了与磁盘IO的交互。
两次写
鉴于都聊到了插入缓冲,我就不得不须要提一嘴两次写,由于我认为这两个InnoDB的特性是相辅相成的。
插入缓冲提升了MySQL的性能,而两次写则在此基础上提升了数据的可靠性。咱们知道,当数据还在缓冲池中的时候,当机器宕机了,发生了写失效,有Redo Log来进行恢复。可是若是是在从缓冲池中将数据刷回磁盘的时候宕机了呢?
这种状况叫作部分写失效,此时重作日志就没法解决问题。

在刷网页时,并非直接刷入磁盘,而是copy到内存中的Doublewrite Buffer中,而后再拷贝至磁盘共享表空间(你能够就理解为磁盘)中,每次写入1M,等copy完成后,再将Doublewrite Buffer中的页写入磁盘文件。
有了两次写机制,即便在刷脏页时宕机了,在实例恢复的时候也能够从共享表空间中找到Doublewrite Buffer的页副本,直接将其覆盖原来的数据页便可。
自适应哈希索引
自适应索引就跟JVM在运行过程当中,会动态的把某些热点代码编译成Machine Code同样,InnoDB会监控对全部索引的查询,对热点访问的页创建哈希索引,以此来提高访问速度。
你可能屡次看到了一个关键字页,接下来那咱们就来聊一下页是什么?
页
页,是InnoDB中数据管理的最小单位。当咱们查询数据时,其是以页为单位,将磁盘中的数据加载到缓冲池中的。同理,更新数据也是以页为单位,将咱们对数据的修改刷回磁盘。每页的默认大小为16k,每页中包含了若干行的数据,页的结构以下图所示。

不用太纠结每一个区是干吗的,咱们只须要知道这样设计的好处在哪儿。每一页的数据,能够经过FileHeader中的上一页和下一页的数据,页与页之间能够造成双向链表。由于在实际的物理存储上,数据并非连续存储的。你能够把它理解成G1的Region在内存中的分布。
而一页中所包含的行数据,行与行之间则造成了单向链表。咱们存入的行数据最终会到User Records中,固然最初User Records并不占据任何存储空间。随着咱们存入的数据愈来愈多,User Records会愈来愈大,Free Space的空间会愈来愈小,直到被占用完,就会申请新的数据页。
User Records中的数据,是按照主键id来进行排序的,当咱们按照主键来进行查找时,会沿着这个单向链表一直日后找,
重作日志缓冲
上面聊过,InnoDB中缓冲池中的页数据更新会先于磁盘数据更新的,InnoDB也会采用日志先行(Write Ahead Log)策略来刷新数据,什么意思呢?当事务开始时,会先记录Redo Log到Redo Log Buffer中,而后再更新缓冲池页数据。
Redo Log Buffer中的数据会按照必定的频率写到重作日志中去。被更改过的页就会被标记成脏页,InnoDB会根据CheckPoint机制来将脏页刷到磁盘。
日志
上面提到了Redo log,这一小节就专门来说一讲日志,日志分为以下两个维度。
MySQL层面
InnoDB层面
MySQL日志
MySQL的日志能够分为错误日志、二进制文件、查询日志和满查询日志。
- 错误日志 很好理解,就是服务运行过程当中发生的严重错误日志。当咱们的数据库没法启动时,就能够来这里看看具体不能启动的缘由是什么
- 二进制文件 它有另一个名字你应该熟悉,叫Binlog,其记录了对数据库全部的更改。
- 查询日志 记录了来自客户端的全部语句
- 慢查询日志 这里记录了全部响应时间超过阈值的SQL语句,这个阈值咱们能够本身设置,参数为
long_query_time
,其默认值为10s,且默认是关闭的状态,须要手动的打开。
InnoDB日志
InnoDB日志就只有两种,Redo Log和Undo Log,
Redo Log 重作日志,用于记录事务操做的变化,且记录的是修改以后的值。无论事务是否提交都会记录下来。例如在更新数据时,会先将更新的记录写到Redo Log中,再更新缓存中页中的数据。而后按照设置的更新策略,将内存中的数据刷回磁盘。
Undo Log 记录的是记录的事务开始以前的一个版本,可用于事务失败以后发生的回滚。
Redo Log记录的是具体某个数据页上的修改,只能在当前Server使用,而Binlog能够理解为能够给其余类型的存储引擎使用。这也是Binlog的一个重要做用,那就是主从复制,另一个做用是数据恢复。
上面提到过,Binlog中记录了全部对数据库的修改,其记录日志有三种格式。分别是Statement、Row和MixedLevel。
- Statement 记录全部会修改数据的SQL,其只会记录SQL,并不须要记录下这个SQL影响的全部行,减小了日志量,提升了性能。可是因为只是记录执行语句,不能保证在Slave节点上可以正确执行,因此还须要额外的记录一些上下文信息
- Row 只保存被修改的记录,与Statement只记录执行SQL来比较,Row会产生大量的日志。可是Row不用记录上下文信息了,只须要关注被改为啥样就行。
- MixedLevel 就是Statement和Row混合使用。
具体使用哪一种日志,须要根据实际状况来决定。例如一条UPDATE语句更新了不少的数据,采用Statement会更加节省空间,可是相对的,Row会更加的可靠。
InnoDB和MyISAM的区别
因为MyISAM并不经常使用,我也不打算去深究其底层的一些原理和实现。咱们在这里简单的对比一下这两个存储引擎的区别就好。咱们分点来一点点描述。
- 事务 InnoDB支持事务、回滚、事务安全和崩溃恢复。而MyISAM不支持,但查询的速度要比InnoDB更快
- 主键 InnoDB规定,若是没有设置主键,就自动的生成一个6字节的主键,而MyISAM容许没有任何索引和主键的存在,索引就是行的地址
- 外键 InnoDB支持外键,而MyISAM不支持
- 表锁 InnoDB支持行锁和表锁,而MyISAM只支持表锁
- 全文索引 InnoDB不支持全文索引,可是能够用插件来实现相应的功能,而MyISAM是自己就支持全本索引
- 行数 InnoDB获取行数时,须要扫全表。而MyISAM保存了当前表的总行数,直接读取便可。
因此,简单总结一下,MyISAM只适用于查询大于更新的场景,若是你的系统查询的状况占绝大多数(例如报表系统)就可使用MyISAM来存储,除此以外,都建议使用InnoDB。
End
因为时间的缘由,本文只是简单的聊了聊InnoDB的总体架构,并无很深刻的去聊某些点。例如InnoDB是如何改进来解决缓冲池污染的,其算法具体是什么,checkpoint是如何工做的等等,只是作一个简单的了解,以后若是有时间的话再细聊。
本文同步分享在 博客“Java入门到入坟”(JianShu)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。