您的位置:首页 > 数据库 > MySQL

mysql存储引擎InnoDB插入数据的过程详解

2015-08-08 20:18 786 查看
mysql是目前市面上应用非常广泛的关系型数据库.

当插入,更新,删除等sql语句运行后,mysql为何总能高效,快速的执行,而且不管是断电,mysql进程崩溃,还是一些其它非正常因素,mysql总能保持数据完整,

本文将带着这些问题探秘mysql底层默认存储引擎InnoDB(Mysql5.5之后)的执行过程.

问题: InnoDB事务提交后在底层都干了什么?

当提交一个事务时,实际上它干了如下2件事:

一:  InnoDB存储引擎把事务写入日志缓冲(log buffer),日志缓冲把事务刷新到事务日志.

二:  InnoDB存储引擎把事务写入缓冲池(Buffer pool).

做完上面2件事,整个事务提交就完成了.

InnoDB通过事务日志把随机IO变成顺序IO,这大大的提高了InnoDB写入时的性能.

因为把缓冲池的脏页数据刷新到磁盘可能会涉及大量随机IO,这些随机IO会非常慢,通过事务日志,避开随机IO,用顺序IO替代它.

但如果此时机器断电或者意外崩溃,那脏页数据没刷新到磁盘,岂不是数据会丢失? 

答案是否定的, mysql意外崩溃后,重启时.会根据事务日志重做事务,恢复所有buffer pool中丢失的脏页.

上面的过程是在未开启binlog的情况下的执行过程,binlog的基本配置如下: 

#【开启二进制日志】 

log_bin = mysql-bin

server_id = 2 

#【中继日志的位置和命名】 

relay_log = mysql-relay-bin

# 【允许备库将其重放的事件也记录到自身的二进制文件中】

log_slave_updates = 1

#【sync_binlog 实时刷新】

sync_binlog = 1

binlog的相关情况不再本文的介绍范围内,不再展开说明.

让我们来看看InnoDB的缓存和文件关系,如图1:



图1

这里面有几个核心的组件:

1, 缓冲池(buffer pool).

事务提交后,InnoDB首先在缓冲池中找到对应的页,把事务更新到缓冲区中.

当刷新脏页到磁盘时,缓冲区都干了什么?

缓冲区把脏页拷贝到double write buffer,double wirte buffer把脏页刷新到double write磁盘(这也是一次顺序IO),再把脏页刷新到数据文件中.

当然缓冲池中还有其他组件,也非常重要,如插入缓冲,该缓冲区是为了高效维护二级非唯一索引所做的优化,把多次IO转化为一次IO来达到快速更新的目的.这里不再展开说明.

2, 日志缓冲(log buffer)

InnoDB使用日志来减少事务提交时的开销.因为日志记录了事务,就无须在每个事务提交时把缓冲池中的脏块刷新到磁盘.因为刷新缓冲池到磁盘一般是随机IO.

InnoDB的日志缓冲有两个重要的参数需要介绍下:

innodb_log_buffer_size 日志缓冲区大小(5.6 默认8M,一般不需要设置太大,除非有BLOB字段)

innodb_flush_log_at_trx_commit  这是InnoDB刷新事务日志的策略参数,默认为1. 

刷新策略值: 

      0,  一秒钟刷新一次,事务提交时,不做任何操作.(可能丢失1秒钟事务数据)
      1,  每次事务都提交刷新到持久化存储(默认&最安全)
      2,  每次提交时把日志缓冲写到日志文件,但并不刷新.  

1和3的区别是: mysql进程挂了,3不会丢事务. 服务器断电或者挂了, 都丢失事务. 把缓冲写到日志是简单的把数据从INNODB的内存缓冲转移到操作系统缓冲.

3, 事务日志 

这里面有2个重要的配置参数需要说明下.

2.1)  innodb_log_file_size mysql 5.6默认的大小是50M

2.2)  innodb_log_files_in_group  mysql5.6默认是2,如下图:



也就是说,InnoDB默认的事务日志文件大小总和是100M。这对高性能工作来说可能太小了,有时需要几百兆甚至几个G的事务日志空间.

linux可通过/etc/my.cnf 来修改事务日志文件的大小, windows是my.ini配置文件

innodb日志是环行方式写的:当写到日志的尾部,会重新跳转到开头继续写,但不会覆盖还没应用到数据文件的日志记录,因为这样会清理掉已提交事务的唯一持久化记录.

日志文件太小,InnoDB将必须做更多的检查点,导致更多的日志写,在日志没有空间继续写入前,必须等待变更被应用到数据文件,写语句可能会被拖累.

但日志文件太大,在崩溃恢复时InnoDB可能不得不做大量的工作,增加恢复时间. 应该在这之间找到平衡,设置合适的日志大小.

4, 双写缓冲

缓冲池刷新脏页面到磁盘时,首先把它们写到双写缓冲,然后再把它们写到所属的数据区域中.

那岂不是所有的脏页都需要写2遍?对,就是写2遍. 但双写缓冲是顺序的,对写冲击比较小.

有些备库上可以禁止双写缓冲,另外一些文件系统(ZFS)做了同样的事,所以没必要让InnoDB做2次, innodb_doublewirte 来关闭。

InnoDb用双写缓冲来避免页没写完整所导致的数据损坏.

双写缓冲的架构如下图:



     从缓冲池中拷贝页到double_write_buffer,double_write_buffer刷新到double_write(共享表空间),再调fsync()同步磁盘

     总结:

               1, InnoDB提交事务过程如下:

                    1.1):  把事务写入日志缓冲(log buffer),日志缓冲把事务刷新到事务日志.

                    1.2):  把事务写入缓冲池(Buffer pool).

               2,  Innodb存储引擎在事务提交后,是把随机IO转化为顺序IO来达到快速提交事务的目的.

               3,  每次刷新脏页到磁盘,实际上是2次写页到磁盘. 

                    3.1):  刷新脏页到双写缓冲,顺序IO

       
3.2):  调用一次fsync()刷新到磁盘,随机IO

               4,  宕机或者意外崩溃重启mysql时,根据事务日志来重做日志恢复缓冲池未来得及刷新到磁盘的脏页,保证数据完整性.

最后给大家提一个问题:

Q:  如果发生写失效(页16KB数据,只写了8Kb),可以通过重做日志进行恢复,为什么还需要double_write?

<!--  重做日志中记录的是对页的物理操作,如果页本身已经发生了毁坏,再对其重做是没有意义的,会发生数据丢失的情况 -->
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息