1.一条update语句

1.先通过引擎找到对应的行数据,并加锁

2.对行数据进行修改并调用引擎接口修改这条数据,然后释放锁(此时并没有把数据在磁盘上做出修改)

3.redo log在内存中生成这条update的日志,通过innodb_flush_log_trx_commit 参数判断是否flush(持久化到磁盘),并告知mysql执行器完成操作

4.执行器生成binlog并持久化到磁盘

5.执行器调用引擎提交事务接口,redo log状态变为commit状态完成整个更新操作。
6.InnoDB使用一个后台线程智能地刷新这些变更到数据文件。这个线程可以批量组合写入, 使得数据写入更顺序,以提高效率。(数据真正落盘,)
 

2.redo log

Mysql分配给redoLog固定大小区域记录日志,一般为4个文件,该日志采用循环写从头写到尾然后在从头,同时还有一个记录擦除日志位置的标记checkpoint。当写入点和checkpoint在同一个位置,则停止写日志。又Innodb引擎实现
首先确定redo log在写入的时经过redo log buffer、FSPage cache、hard disk
redo log buffer:物理上是Mysql进程内存
FSPage cache:写入磁盘但没有持久化,物理上是在文件系统page cache中
hard disk 持久化到磁盘
 

3.刷脏页

Mysql在记录redo log的时候会先将数据写入到内存中,然后通过flush将内存中的数据写入磁盘中。在此期间会产生脏数据页导致内存和磁盘的数据不一致。

这时候mysql就需要刷脏数据页。即是第一点的第六小点的动作:使用一个后台线程智能地刷新这些变更到数据文件

1.当记录redo log的内存满了,会停止写入redo log操作,然后进行刷脏页工作
2.写入日志太多,发现分配的内存不够,这时候需要淘汰一部分数据页,坐刷脏页工作
3.mysql空闲时会进行刷脏页工作

需要注意的是:

在flush过程中如果存在读入的数据页没有内存的时候,需要到缓冲池中申请数据页。当数据页不足首先将最久不使用的数据从内存中淘汰,如果是脏数据页还得先进行刷盘才能复用。

InnoDB刷盘影响因素:脏页的比重(InnoDB_max_dirty_pages_pct控制)、redo log写盘速度 以及 INNODB_IO_CAPACITY参数控制mysql 的IOPS
 
 

4. checkpoint (跟第二点的刷脏页对应)

1.缩短数据库的恢复时间;

2.缓冲池不够用时,将脏页刷新到磁盘;

3.重做日志不可用时,刷新脏页。

  • 当数据库发生宕机时,数据库不需要重做所有的日志,因为Checkpoint之前的页都已经刷新回磁盘。数据库只需对Checkpoint后的重做日志进行恢复,这样就大大缩短了恢复的时间。

  • 当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。

  • 写入日志太多,发现分配的内存不够,这时候需要停止redo log写入然后淘汰一部分数据页,做刷脏页操作,然后移动checkpoint 位置

checkpoint 分为两类:

  • Sharp Checkpoint

Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数innodb_fast_shutdown=1

但是若数据库在运行时也使用Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在InnoDB存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。

  • Fuzzy Checkpoint

1、Master Thread Checkpoint

以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘,这个过程是异步的,此时InnoDB存储引擎可以进行其他的操作,用户查询线程不会阻塞。

2、FLUSH_LRU_LIST Checkpoint

因为InnoDB存储引擎需要保证LRU列表中需要有差不多100个空闲页可供使用。在InnoDB1.1.x版本之前,需要检查LRU列表中是否有足够的可用空间操作发生在用户查询线程中,显然这会阻塞用户的查询操作。倘若没有100个可用空闲页,那么InnoDB存储引擎会将LRU列表尾端的页移除。如果这些页中有脏页,那么需要进行Checkpoint,而这些页是来自LRU列表的,因此称为FLUSH_LRU_LIST Checkpoint。

而从MySQL 5.6版本,也就是InnoDB1.2.x版本开始,这个检查被放在了一个单独的Page Cleaner线程中进行,并且用户可以通过参数innodb_lru_scan_depth控制LRU列表中可用页的数量,该值默认为1024,如:

+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024 |

+-----------------------+-------+

Fuzzy Checkpoint生命周期如下:Innodb每次取最老的modified page(last checkpoint)对应的LSN,再将此脏页的LSN作为Checkpoint点记录到日志文件,意思就是“此LSN之前的LSN对应的日志和数据都已经flush到redo log。

当mysql crash的时候,Innodb扫描redo log,从last checkpoint开始apply redo log到磁盘,直到last checkpoint对应的LSN等于Log flushed up to对应的LSN,则恢复完成

  • Log sequence number(LSN1):当前系统LSN最大值,新的事务日志LSN将在此基础上生成(LSN1+新日志的大小);

  • Log flushed up to(LSN2):当前已经写入日志文件的LSN;

  • Oldest modified data log(LSN3):当前最旧的脏页数据对应的LSN,写Checkpoint的时候直接将此LSN写入到日志文件;

  • Last checkpoint at(LSN4):当前已经写入Checkpoint的LSN;

如上图所示,Innodb的一条事务日志共经历4个阶段:

  • 创建阶段:事务创建一条日志;

  • 日志刷盘:日志写入到磁盘上的日志文件;

  • 数据刷盘:日志对应的脏页数据写入到磁盘上的数据文件;

  • 写CKP:日志被当作Checkpoint写入日志文件

3、Async/Sync Flush Checkpoint

指的是重做日志文件不可用的情况,这时需要强制将一些页刷新回磁盘,而此时脏页是从脏页列表中选取的。若将已经写入到重做日志的LSN记为redo_lsn,将已经刷新回磁盘最新页的LSN记为checkpoint_lsn,则可定义:

checkpoint_age = redo_lsn - checkpoint_lsn

再定义以下的变量:

async_water_mark = 75% * total_redo_log_file_size

sync_water_mark = 90% * total_redo_log_file_size

若每个重做日志文件的大小为1GB,并且定义了两个重做日志文件,则重做日志文件的总大小为2GB。那么async_water_mark=1.5GB,sync_water_mark=1.8GB。则:

当checkpoint_age<async_water_mark时,不需要刷新任何脏页到磁盘;

当async_water_mark<checkpoint_age<sync_water_mark时触发Async Flush,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age<async_water_mark;

checkpoint_age>sync_water_mark这种情况一般很少发生,除非设置的重做日志文件太小,并且在进行类似LOAD DATA的BULK INSERT操作。此时触发Sync Flush操作,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age<async_water_mark

可见,Async/Sync Flush Checkpoint是为了保证重做日志的循环使用的可用性。在InnoDB 1.2.x版本之前,Async Flush Checkpoint会阻塞发现问题的用户查询线程,而Sync Flush Checkpoint会阻塞所有的用户查询线程,并且等待脏页刷新完成。从InnoDB 1.2.x版本开始——也就是MySQL 5.6版本,这部分的刷新操作同样放入到了单独的Page Cleaner Thread中,故不会阻塞用户查询线程

 

5.WAL

WAL机制(Write-Ahead-Logging),主要讲了数据在操作的时候先进行写日志,然后再将数据写入磁盘的过程

6.binlog 

称为归档日志,是追加写不会覆盖之前的日志。binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用
执行事务过程,先将日志写入binlog cache,事务提交时再将cache写入到binlog中。对于cache系统会分配一片内存,每个线程一个,参数binlog_cache_size用于控制单个线程内binlog cache所占内存的大小。超过这个参数则暂存在磁盘中。

事务提交时,执行器将binlog cache完整事务写入binlog中,并清空binlog cache。

整个写入过程分为两步 write和fsync

write:指把日志写入到文件系统page cache,并没有写入磁盘中,速度快

fsync:将数据持久化到磁盘,该过程占用磁盘IOPS。
 

7.事务两阶段提交

1.Storage Engine(InnoDB) transaction prepare阶段:存储引擎的准备阶段,写redo-buffer然后根据参数罗盘redo log
2.Binary log日志提交:写binlog并落盘.
3.Storage Engine(InnoDB)内部提交把事务状态标记为已提交

8.Crash recovery

如果数据库异常后宕机重启后会通过Crash recovery来恢复

sync_binlog=1 和  innoDB_flush_log_at_trx_commit = 1 称之为双1

双1模式可以保证提交后的事务不会丢失,即crash-safe。

在做Crash recovery时:分为以下3种情况

  1. binlog有记录,redolog状态commit:正常完成的事务,不需要恢复;
  2. binlog有记录,redolog状态prepare:在binlog写完提交事务之前的crash,恢复操作:提交事务。(因为之前没有提交)
  3. binlog无记录,redolog状态prepare:在binlog写完之前的crash,恢复操作:回滚事务(因为crash时并没有成功写入数据库)

9.组提交

mysql 5.7开始默认开启组提交。

1. 要保证顺序必须是提交加入到队列的顺序(binlog_order_commits保证)。

2. 如果有新的事务提交,此时队列为空,则可以加入到FLUSH队列中。不过,因为此时FLUSH临界区正在被占用,所以新事务组必须要等待。

3. 给每个事务分配sequence_number,如果是第一个事务,则将这个组的last_committed设置为sequence_number-1.

4. 将带着last_committed与sequence_number的GTID事件FLUSH到Binlog文件中。

5. 将当前事务所产生的Binlog内容FLUSH到Binlog文件中。

组提交之redo log

多个同时处于prepare阶段的事务生成各自的redo log 会一起刷盘。假设2WTPS,并不需要4W次刷盘。也会在binlog中生成相同的last_commited(表示事务提交的时候,上次事务提交的编号).这些事务称之为1个事务组。

其实每一个组的last_committed值,都是上一个组中事务的sequence_number最大值,也是本组中事务sequence_number最小值减1。同时这两个值的有效作用域都在文件内,只要换一个文件(flush binary logs),这两个值就都会从0开始计数。上述的last_committed和sequence_number代表的就是所谓的LOGICAL_CLOCK

组提交之binlog

为了增加一组事务中的事务数量,提高刷盘收益,MySQL使用两个参数控制获取队列事务组的时机:

binlog_group_commit_sync_delay=N:在等待N μs后,开始事务刷盘(图中Sync binlog)

binlog_group_commit_sync_no_delay_count=N:如果队列中的事务数达到N个,就忽视binlog_group_commit_sync_delay的设置,直接开始刷盘(图中Sync binlog)

组提交可以有效减低iops。 尽管上面两个参数都关闭,也会开启组提交。因为redo log组提交不由这两个参数控制。

10. 并行复制

MySQL 5.7的并行复制基于一个前提,即所有已经处于prepare阶段的事务,都是可以并行提交的。这些当然也可以在从库中并行提交,因为处理这个阶段的事务,都是没有冲突的,该获取的资源都已经获取了。反过来说,如果有冲突,则后来的会等已经获取资源的事务完成之后才能继续,故而不会进入prepare阶段。

一个组提交(group commit)的事务都是可以并行回放,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。

  • binlog_order_commits会影响提交行为。如果设置为ON,那么此时提交就变为串行操作了,就以队列的顺序为提交顺序。而这也是LOGICAL_CLOCK并行复制的基础。因为order commit使得所有的事务分了组,并且有了序列号,从库拿到这些信息之后,就可以根据序号放心大胆地做分发了。
  • slave_parallel_workers  从库并行复制得线程数
  • slave_parallel_type: logical_clock 表示以事务组提交的方式并行复制。database 表示以不同database的方式并行复制

MYSQL 那些事的更多相关文章

  1. mysql那些事之索引篇

    mysql那些事之索引篇 上一篇博客已经简单从广的方面介绍了一下mysql整体架构以及物理结构的内容. 本篇博客的内容是mysql的索引,索引无论是在面试还是我们日常工作中都是非常的重要一环. 索引是 ...

  2. mysql那些事

    ---恢复内容开始--- 登录 mysql登录 -u+用户 -p 密码 显示数据库 show databases; 使用某个数据库 use xxx; 显示数据库表 show tables 显示表结构 ...

  3. mysql那些事(6) WHERE条件 字符串的引号

    前言:所谓的坑,两个意思,一个是软件本身的bug,一个是使用者常犯的错误. phper在日常开发中经常和mysql打交道.特别是在没有分层的中小应用中,phper开发要关注sql语句的实现. 入正题, ...

  4. mysql那些事(5)建表存储引擎的选择

    在mysql见表的时候,会遇到选择存储引擎:MyISAM和InnoDB.究竟用哪种存储引擎好呢? 1.MyISAM:表锁:支持全文索引:读并发性能较好. 2.InnoDB:行锁:支持事务,支持外键:写 ...

  5. mysql那些事(4)建库建表编码的选择

    mysql建数据库或者建表的时候会遇到选择编码的问题,以前我们都是习惯性的选择utf8,但是在mysql在5.5.3版本后加了utf8mb4的编码,utf8mb4可以存4个字节Unicode,mb4就 ...

  6. mysql那些事(3)小数如何存储

    创建mysql数据表的时候,经常会遇到存储小数(浮点数)的情况,如:价格,重量,身高等. 目前大的公司流行三种存储方案: 1.将数据扩大10的倍数达到使用整数类型存储目的. 比如价格,我们经常以分为单 ...

  7. mysql那些事(2)时间类型数据如何存储

    几乎每次数据库建模的时候,都会遇到时间类型数据存储的问题. mysql存储时间通常选择这四种类型:datetime.timestamp.int和bigint四种方式,到底使用什么类型,需要看具体的业务 ...

  8. mysql那些事(1)手机号与座机号码如何存储

    创建mysql数据表的时候,经常会遇到手机号码和座机号码数据的存储问题. 先说手机号码:很多人喜欢使用数字来进行存储,手机号不涉及到运算,并且有时候要带括号,加号之类的字符,有时候还要以0开头.所以, ...

  9. PHP基础之与MySQL那些事

    前言 这篇文章会对PHP的MySQL扩展库,MySQLI的扩展库,SQL批量执行,事务控制等等进行一些简单的讲解. MySQL扩展 PHP中MySQL扩展,虽然因为安全的原因,在PHP5.6及往上不在 ...

随机推荐

  1. 盘点MySQL中比较实用的函数

    之前小编在工作中遇到一些处理数据的问题,例如完成日期格式化,获取几天后的时间,生成指定格式的编码等问题,这时候小编经常会写一大堆逻辑代码来进行处理,还自己感觉自己很流弊的样子,后来却发现同事遇到相同的 ...

  2. MySQL存储引擎入门介绍

    什么是MySQL? MySQL 是一种关系型数据库,在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展.阿里巴巴数据库系统也大量用到了 MySQL,因此它的稳定性是有保障的. ...

  3. 【PowerQuery】做了一万遍的工资条

    前面已经了解了Excel.VBA.Python实现工资条,今天尝试用PQ做一遍 做之前迷惑了很久,如何能自定义长度 Table有Repeat函数,但是List没有.看来另外想办法 一步步接近目标  请 ...

  4. 剑指offer-递归和循环

    1. 斐波那契数列 解: 没啥好说的了,直接上高效的滚动迭代解法.矩阵解法和特征根解法这里不讨论了. class Solution: def Fibonacci(self, n): # write c ...

  5. 再玩树莓派(二)Jexus&.NetCore

    接上一篇,操作系统弄好之后,轮到开发运行环境的搭建. 先说说目标,也就是我到底想搞什么飞机.先说说小目标吧. 现有一个手机App客户端,以答题小游戏作为其内容(例如:口算题,24点,科学百科等) 树莓 ...

  6. c中_tmain()和main()区别

    来源参考:https://www.cnblogs.com/lucyjiayou/archive/2011/05/07/2039621.html tchar.h>可以找到,如#define _tm ...

  7. 2-K8S常用命令

    kubectl 命令行管理工具 类型 命令 描述 基础命令 create 通过文件名或标准输入创建资源 expose 为Deployment,Pod创建service run 在集群中运行一个特定的镜 ...

  8. SpringBoot使用activiti自定义流程demo解析

    环境搭建[这里直接讲解自定义流程] 集成 Activiti Modeler 下载源码 我这里选用的是 Activiti 5.23.0 版本的页面,下载 zip,解压 Activiti 5.23.0 源 ...

  9. 干货分享:用一百行代码做一个C/C++表白小程序,程序员的浪漫!

    前言:很多时候,当别人听到你是程序员的时候.第一印象就是,格子衫.不浪漫.直男.但是程序员一旦浪漫起来,真的没其他人什么事了.什么纪念日,生日,情人节,礼物怎么送? 做一个浪漫的程序给她,放上你们照片 ...

  10. vue任意关系组件通信与跨组件监听状态 vue-communication

    大家好!我是木瓜太香! 众所周知,组件式开发方式给我们带来了方便,不过也引入了新的问题,组件之间的数据就像被一道无形的墙隔开,如果我们希望临时让两个组件直接通信,vuex 太巨,而 $emit 又不好 ...