innodb存储引擎中一条sql写入的详细流程

 

这里主要讲解存储引擎的部分,server层可以看看这篇文章

第1步:会先去看缓冲区有没有这条数据,如果有就不进行缓存,直接进入第三步。

第2步:会将要修改的那一行数据所在的一整页加载到缓冲池Buffer Pool中

第3步:将旧值写入undo日志中,便于回滚以及mvcc机制的运作

第4步:将Buffer Pool中的数据更新为新的数据。

第5步:写入redo日志缓冲池,redo日志的内容实际和binlog差不多,但是作用不同(写入redo日志是磁盘顺序写,mysql会先预留一块区域给redo日志使用,速度会比随机读要更快)

第6步:准备提交事务前,将redo日志写入磁盘

第7步:准备提交日志前,将binlog日志写入磁盘

第8步:将commit标记写到redo日志中,事务提交完成。该操作时为了保证事务提交后redo和binlog数据一致性

第八步:将缓冲区的数据写入磁盘,注意这里的写入不是及时写入的,而是随机的。

说一下Buffer pool中写入磁盘的过程

这里并不是直接一次性将缓冲区中的数据写入磁盘的,而是分两步:两次写给 InnoDB 带来的是可靠性,主要用来解决 部分写失败(partial page write)。doublewrite 有两部分组成,一部分是内存中的 doublewrite buffer ,大小为 2M,另外一部分就是物理磁盘上的共享表空间中 连续的 128 个页,即两个区,大小同样为 2M。当缓冲池的作业刷新时,并不 直接写硬盘,而是通过 memcpy 函数将脏页先拷贝到内存中的 doublewrite buffer,之后通过 doublewrite buffer 再分两次写,每次写入 1M 到共享表空间 的物理磁盘上,然后马上调用 fsync 函数,同步磁盘

doublewrite buffer 和 redo日志起的是不同的作用,redo只适用于数据完全没有更新,例如还没有开始写入磁盘的时候就断电了。而doublewrite 则适用于写入了一半之后断电了。两个都是为了保证数据的可靠性

为什么innodb不选择直接将数据更新到磁盘,而是先写入缓冲区并且引入一系列操作呢?

如果一条sql进来就直接写入磁盘,那这种写入就数据随机读写磁盘,效率是非常低的,而通过缓冲区一次读取一页数据,接下来的sql操作的行如果也在缓冲区中,那就先修改缓冲区,而修改缓冲区的速度是非常非常快的,然后到了某个时机在将所有的sql进行写入磁盘,这无形减少了磁盘io。当然,在写入之前肯定会在缓冲区里对数据进行了处理。而引入一系列操作是为了保证数据库的高可用性,undo日志用于事务的回滚以及mvcc机制,这是innodb的特性。而redo日志则保证了出现断电等极端现象重启数据库时数据能够恢复等

什么是两阶段提交?为什么要有两阶段提交?

两阶段提交:将redo日志的提交分为prepare和commit两个阶段,称之为两阶段提交。第五第六步为第一阶段,第七步标记commit为第二阶段

没有两阶段提交的情况

1.当只有redo log,binlog失效时,会导致主库可以通过redo log来重做,而从库因为没有及时获取到binlog而不能进行回放,导致主从数据不一致。这样会出现 redo log 写入到磁盘了,但是 binlog 还没写入磁盘,于是当发生 crash recovery 时,恢复后,主库会应用redo log,恢复数据,但是由于没有 binlog,从库就不会同步这些数据,主库比从库“新”,造成主从不一致

2.跟上一种情况类似,很容易知道,这样会反过来,造成从库比主库“新”,也会造成主从不一致。

两阶段提交,解决crash recovery 问题

如果 redo log 已经 commit,那毫不犹豫的,把事务提交
如果 redo log 处于 prepare,则去判断事务对应的 binlog 是不是完整的

  • 如果是,则把事务提交
  • 如果不是,则事务回滚

两阶段提交,其实是为了保证 redo log 和 binlog 的逻辑一致性。

redo日志

位置:

位于data目录下的ib_logfile0 

可以通过sql查询 show variables like '%innodb_log_group_home_dir%';

作用:

用于数据库宕机的时候,重启数据库时恢复宕机期间已经写入了redo日志但来不及写入磁盘idb文件的数据

redo log 写入磁盘的过程分析:

redo log 从头开始写,写完一个文件继续写另一个文件,写到最后一个文件末尾就又回到第一个文件开头循环写,如下面这个图所示。

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。

checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件里。

write pos  checkpoint 之间的部分就是空着的可写部分,可以用来记录新的操作。如果 write pos 追上checkpoint,表示redo log写满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下,再擦除掉之前,如果当前的内容还没有写入磁盘idb文件,那么myslq就会强制将修改的数据内容写入磁盘idb文件。

redo log 的写入策略

事实上第一张主导图中的第五步,redo日志写入磁盘文件之间还有一步是先将数据写入os缓存(os page cache)中,然后再写入redo log磁盘文件。

innodb_flush_log_at_trx_commit :这个参数控制 redo log 的写入策略,它有三种可能取值:
设置为0:表示每次事务提交时都只是把 redo log 留在 redo log buffer 中,数据库宕机可能会丢失数据。
设置为1(默认值):表示每次事务提交时都将 redo log 直接持久化到磁盘,数据最安全,不会因为数据库宕机丢失数据,但是效率稍微差一点,线上系统推荐这个设置。
设置为2:表示每次事务提交时都只是把 redo log 写到操作系统的缓存page cache里,这种情况如果数据库宕机是不会丢失数据的,但是操作系统如果宕机了,page cache里的数据还没来得及写入磁盘文件的话就会丢失数据。
InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 操作系统函数 write 写到文件系统的page cache,然后调用操作系统函数 fsync 持久化到磁盘文件。
redo log写入策略参看下图(该图只针对redolog日志的变化操作):

binlog日志

作用

binlog二进制日志记录保存了所有执行过的修改操作语句,不保存查询操作。如果 MySQL 服务意外停止,可通过二进制日志文件排查,用户操作或表结构操作,从而来恢复数据库数据。
启动binlog记录功能,会影响服务器性能,但如果需要恢复数据或主从复制功能,则好处则大于对服务器的影响。
MySQL5.7 版本中,binlog默认是关闭的,8.0版本默认是打开的。上图中log_bin的值是OFF就代表binlog是关闭状
态,打开binlog功能,需要修改配置文件my.ini(windows)或my.cnf(linux),然后重启数据库。

可以通过sql查看是否开启show variables like '%log_bin%';

配置binlog日志

# log‐bin设置binlog的存放位置,可以是绝对路径,也可以是相对路径,这里写的相对路径,则binlog文件默认会放在 data数据目录下 
log‐bin=mysql‐binlog 
# Server Id是数据库服务器id,随便写一个数都可以,这个id用来在mysql集群环境中标记唯一mysql服务器,集群环 境中每台mysql服务器的id不能一样,不加启动会报错
server‐id=1 
# 其他配置
binlog_format = row # 日志文件格式,下面会详细解释 
expire_logs_days = 15 # 执行自动删除binlog日志文件的天数, 默认为0, 表示不自动删除 
max_binlog_size = 200M # 单个binlog日志文件的大小限制,默认为 1GB
重启数据库后我们再去看data数据目录会多出两个文件,第一个就是binlog日志文件,第二个是binlog文件的索引文件,这个文件管理了所有的binlog文件的目录。

查看binlog相关参数信息

show variables like '%log_bin%' ;

1 log_bin binlog 日志是否打开状态
2 log_bin_basename :是 binlog 日志的基本文件名,后面会追加标识来表示每一个文件, binlog 日志文件会滚动增加
3 log_bin_index :指定的是 binlog 文件的索引文件,这个文件管理了所有的 binlog 文件的目录。
4 sql_log_bin sql 语句是否写入 binlog 文件, ON 代表需要写入, OFF 代表不需要写入。如果想在主库上执行一些操作,但不复制到slave 库上,可以通过修改参数 sql_log_bin 来实现。比如说,模拟主从同步复制异常。

binlog 的日志格式

用参数 binlog_format 可以设置binlog日志的记录格式,mysql支持三种格式类型:

  • STATEMENT:基于SQL语句的复制,每一条会修改数据的sql都会记录到master机器的bin-log中,这种方式日志量小,节约IO开销,提高性能,但是对于一些执行过程中才能确定结果的函数,比如UUID()、 SYSDATE()等函数如果随sql同步到slave机器去执行,则结果跟master机器执行的不一样。
  • ROW:基于行的复制,日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改记录下每一行数据修改的细节,可以解决函数、存储过程等在slave机器的复制问题,但这种方式日志量较大,性能不如Statement。举个例子,假设update语句更新10行数据,Statement方式就记录这条update语句,Row方式会记录被修改的10行数据。
  • MIXED:混合模式复制,实际就是前两种模式的结合,在Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种,如果sql里有函数或一些在执行时才知道结果的情况,会选择Row,其它情况选择Statement,推荐使用这一种。

重新生成binlog日志条件:

  • 服务器启动或重新启动
  • 服务器刷新日志,执行命令flush logs
  • 日志文件大小达到 max_binlog_size 值,默认值为 1GB
binlog写入磁盘机制
binlog写入磁盘机制主要通过 sync_binlog 参数控制,默认值是 0。
为0的时候,表示每次提交事务都只 write 到page cache,由系统自行判断什么时候执行 fsync 写入磁盘。虽然性能得到提升,但是机器宕机,page cache里面的 binlog 会丢失。
也可以设置为1,表示每次提交事务都会执行 fsync 写入磁盘,这种方式最安全。
还有一种折中方式,可以设置为N(N>1),表示每次提交事务都write 到page cache,但累积N个事务后才 fsync 写入磁盘,这种如果机器宕机会丢失N个事务的binlog。
发生以下任何事件时, binlog日志文件会重新生成:

redo日志和binlog日志的区别

这两种日志都是用于数据恢复的,redo日志主要是用于系统突然宕机的时候,进行数据恢复,而binlog日志是用于正常情况下数据不小心被误删,需要数据恢复的情况

undo log日志什么时候删除

新增类型的,在事务提交之后就可以清除掉了。
修改类型的,事务提交之后不能立即清除掉,这些日志会用于mvcc。只有当没有事务用到该版本信息时才可以清 除。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值