根据存储事务的工作流程原理,来了解如何保证事务的ACID特性,利用了MySQL数据库的哪些工作机制;

事务工作流程名字解释:

  • 名词解释一:redo log-Disk

表示重做日志,当出现异常情况,内存中数据直接写入磁盘失败时,可以通过重启数据库服务,读取此文件修复数据信息;

文件存储表项为:ib_logfile0~N 默认48M,轮询使用

  • 名词解释二:redo log buffer-mem

表示重做日志生成缓冲区,相当于redo log的内存区域。redo log文件与redo log buffer是有IO关系的;

事务修改提交后:redo log buffer -> redo log,表示写入数据到redo log;

事务操作恢复时:redo log -> redo log buffer,表示读取数据从redo log;

  • 名词解释三:tablespace file-disk

表示存储表数据行和索引等信息的文件,含有表空间所有数据文件;ibd

  • 名词解释四:Innodb buffer pool-mem

表示数据缓冲区,主要用于缓冲事务要处理的数据和索引信息,tablespace文件与buffer pool是有IO关系的;

  • 名词解释五:LSN

表示日志序列号,在buffer pool中有数据页信息的变化就会记录到redo log buffer中,主要记录变化了多少字节量;

利用LSN记录相应数据页的变化量(LSN+变化字节量),也可以理解为记录的是日志量的变化;

MySQL每次数据库启动,都会比较磁盘数据页和redolog的LSN,必须要求两者一致,数据库才能正常启动;

  • 名词解释六:WAL(Write Ahead Log)

表示redo日志生成记录优先于数据页写入到磁盘的过程,并且是支持预写入机制(group commit)的;

  • 名词解释七:Dirty page

表示在内存进行修改的数据页,在redo buffer中会记录数据页的数据量的变化,此时在数据页还未最终写入到磁盘中时;

就称之为脏页,所以一般所谓的脏读就是读取脏页的数据页信息;

  • 名词解释八:CheckPoint

表示为检查点,就是将脏页刷写到磁盘的动作;

  • 名词解释九:DB_TRX_ID(6字节)

表示为事务ID号,InnoDB会为每一个事务生成一个事务号(由事务管理器管理TM),伴随着整个事务生命周期

其中事务ID号码信息,在redo和undo日志文件中都会有相应的标识;

  • 名词解释十:DB_ROLL_PTR(7字节)

表示回滚指针,在rollback时会使用undo日志回滚已修改的数据,DB_ROLL_PTR会指向此次事务的回滚业务点;

从而找到undo上的相应的日志信息;

数据库名词解释官方参考:https://dev.mysql.com/doc/refman/8.0/en/glossary.html

事务工作流程具体解读:

简单事务情况举例:

mysql> begin;
mysql> update t1 set A=2 where A=1;
mysql> commit;

事务工作流程一:redo log 重做日志如何应用

  • 用户发起update操作事务语句,将磁盘数据页(page100,A=1,LSN=1000)加载到内存(buffer_pool)缓冲区;

  • 将在内存中发生数据页修改操作(A=1改为A=2),形成数据页脏页,更改中数据页的变化会记录到redo buffer中;

加入1000个字节日志信息,LSN=1000+1000=2000;

  • 当执行事务提交操作的时候,基于WAL机制,等到redo buffer中的日志完全落盘到ib_logfileN-redo log中,即commit正式完成;

  • 此时ib_logfileN中记录了一条日志,内容为:page100数据页变化+LSN=2000

简单理解:记录内存数据页变化日志+undo(DB_TRX_ID,DB_ROLL_PTR),通过LSN和数据页建立关系

特殊情景分析:当此时,redo落盘了,数据页没有落盘,出现宕机情况了;

  • MySQL CR(自动故障恢复)工作模式,启动数据库时,自动检查redo的LSN和数据页LSN;
  • 如果发现redo LSN > 数据页的LSN,加载原始数据页+变化redo指定内存,使用redo重构脏页(前滚);
  • 如果确认此次事务已经提交(commit标签),立即触发CKPT(checkpoint)动作,将脏页刷写到磁盘上;

知识点补充:

MySQL有一种机制,批量刷写redo的机制:会在A事务commit时,顺便将redo buffer中的未提交的redo日志也一并刷到磁盘;

为了区分不同状态的redo,日志记录时会标记是否commit;

redo保证了ACID哪些特性:

主要保证了D的特性,另外A C也有间接关联;

Redo Log日志文件生成流程:

Day008-数据库服务事务相关知识-图1

Redo Log日志文件应用流程:

Day008-数据库服务事务相关知识-图2

说明:利用redo Log重做日志功能可以保证事务的D特性,基于可以丢内存数据,但是不可以丢操作事务日志的原则;

存储引擎读写磁盘数据页IO信息:

存储引擎读写磁盘数据页IO信息:

mysql> select @@innodb_read_io_threads;
+------------------------------------+
| @@innodb_read_io_threads |
+------------------------------------+
|                                                4 |
+------------------------------------+
1 row in set (0.00 sec)
-- 接收SQL层处理信息传达到存储引擎层的读IO配置信息;

mysql> select @@innodb_write_io_threads;
+------------------------------------+
| @@innodb_write_io_threads |
+------------------------------------+
|                                                 4 |
+------------------------------------+
1 row in set (0.00 sec)
-- 接收SQL层处理信息传导到存储引擎层的写IO配置信息

存储引擎序号号码信息查看:

mysql> show engine innodb status\G
Log sequence number                   105377511
-- redo buffer中的SN号码信息
Log flushed up to                           105377511
-- redo buffer刷新到磁盘上的SN号码信息
Last checkpoint at                         105377511
-- 磁盘数据页的SN号码信息

存储引擎redo buffer落盘的机制策略:****

mysql> select @@innodb_flush_log_at_trx_commit;
+-----------------------------------------------+
| @@innodb_flush_log_at_trx_commit |
+-----------------------------------------------+
|                                                               1 |
+-----------------------------------------------+
1 row in set (0.00 sec)
-- 表示数据库配置与安全有关的两个双一配置
-- 当数值为1:表示每次事务提交就立刻进行redo buffer刷新落盘,若落盘不成功,则commit命令操作也不会成功;默认
-- 当数值为0:表示日志缓存信息写入磁盘是按照每秒种进行一次操作,未刷新日志的事务可能会在崩溃中丢失;不安全
-- 当数值为2:表示在事务提交后先生成日志缓存信息,然后再按照每秒钟进行一次写入磁盘操作;不安全
-- 参考官方链接:https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html

事务工作流程二:undo log 回滚日志如何应用

  • 事务发生数据页修改之前,会申请一个undo事务操作,保存了事务回滚日志(逆向操作的逻辑日志)
  • undo写完之后,事务修改数据页头部(会记录DB_TRX_ID+DB+ROLL_PTR),这个信息也会被记录在redo Log中

简单理解:记录数据修改的前镜像(逆向操作),数据页和undo通过DB_TRX_ID,DB_ROLL_PTR建立关系

特殊情景分析01:

当执行rollback命令时,根据数据页的DB_TRX_ID+DB+ROLL_PTR信息,找到undo日志并进行回滚;

特殊情景分析02:

mysql> begin;
mysql> update t1 set A=2 where A=1;
-- 此时宕机了

假设:undo 有;redo 没有

  • 启动数据库时,检查redo和数据页的LSN号码,发现是一致的;
  • 所以不需要进行redo的前滚,此时也不需要回滚。undo信息直接被标记为可覆盖状态;

假设:undo 有;redo 也有(没有commit标签)

  • MySQL CR(自动故障恢复)工作模式,启动数据库时,自动检查redo的LSN和数据页LSN;
  • 如果发现redo LSN>数据页的LSN。随即加载原始数据页+变化redo Log日志信息到相应内存位置,使用redo重构脏页(前滚);
  • 如果确认此次事务没有commit标记,立即触发回滚操作,根据DB_TRX_ID+DB_ROLL_PTR信息,找到undo回滚日志,实现回滚;

以上流程被称之为InnoDB的核心特性:自动故障恢复(CR),会先前滚再回滚,先应用redo再应用undo;

undo保证了ACID哪些特性:

主要保证事务的A的特性,同时C和I的特性也有关系;

undo Log日志文件生成流程:

Day008-数据库服务事务相关知识-图3

undo Log日志文件应用流程:

Day008-数据库服务事务相关知识-图4

说明:利用undo Log重做日志功能可以保证事务的A特性,基于先进行数据页前滚操作恢复脏页,在进行回滚操作恢复操作前事务;

事务工作流程三:事务中的C特性如何保证

InnoDB crash recovery:数据库意外宕机时刻,通过redo前滚+undo回滚保证数据的最终一致;

InnoDB doubewrite buffer:默认存储在ibdataN中,解决数据页写入不完整;DWB一共2M,分两次。每次1M写入;

redo日志只能恢复好的数据页的内容,但是不能恢复已经有异常的数据页内容;

可以参考官方资料:https://dev.mysql.com/doc/refman/8.0/en/mysql-acid.html

DWB文件信息生成流程:

Day008-数据库服务事务相关知识-图5

DWB文件信息应用流程:

Day008-数据库服务事务相关知识-图6

事务工作流程四:事务中的I特性如何保证

主要对数据库服务并发访问资源的保护,在并发事务工作期间,防止事务与事务之间的资源争抢(相互影响);

  • 保证读隔离性

方式一:利用隔离级别保证

序号 隔离级别 简单回顾
01 RU 有可能会出现脏读、不可重复读、幻读
02 RC 有可能会出现不可重复读,幻读
03 RR 有可能会出现幻读(99.9%的读异常问题配合锁机制都可以处理)
04 SR(SE) 采用事务串行工作机制

方式二:利用MVCC机制隔离(只能保证读的隔离)

MVCC(multi-version-concurrent-control)即多版本并发控制,是一种并发控制的方法;

可以类别成Git进行并发处理的机制,其实就是每个事务在发生更新的过程中,维护发生更新事务的各个版本;

各个事务版本通过undo的日志(前镜像)实现快照的技术(read view),从而可以保存多个事务版本;

对于隔离级别而言,只有RC和RR级别可以使用到MVCC机制的,实现一种快照读机制,而RU和SR级别是不会使用到MVCC机制的;

  • RC:应用MVCC的快照读机制,是基于语句级别的;(不可重复读 ture)

在事务期间,执行每个查询语句的时候,都会检查MVCC版本(快照列表),获取最新的已提交事务的快照;

  • RR:应用MVCC的快照读机制,是基于事务级别的;(不可重复读 false)

在事务期间,执行首条查询语句的时候,就会生成MVCC版本(相应快照),将会一直读取此快照数据信息,直到事务生命周期结束;

以上的RR隔离级别利用MVCC的快照读机制,又称为一致性快照读;

==================================================================================================

MVCC进行多版本控制时,会应用的两种锁机制:乐观锁/悲观锁

每个事务操作都要经历两个阶段:

  • 读阶段--应用乐观锁:

MVCC利用乐观锁机制,实现非锁定读取,借助快照技术(read view)

# 进行操作事务处理过程(trx1
> begin;
> DML01 语句
-- 在做第一次事务操作的时候,当前事务获取系统最新的 rv1 版本快照
> DML02 语句
-- 在做下一次事务操作的时候,生成新的事务系统查询的 rv2 版本快照
> select
-- 此时查询的是 rv2快照数据信息
> commit
-- rv2 快照数据被提交,成为系统最新的快照

RC隔离级别快照应用:

trx-01rv1  -> rv2  -> commit;
trx-02rv1  -> rv1  -> rv2

RR隔离级别快照应用:

trx-01:第一个查询时,生成global consitence snapshot RV-CS110:00),一直伴随着事务生命周期结束
trx-02:第一个查询时,生成global consitence snapshot RV-CS210:01),一直伴随着事务生命周期结束
  • 写阶段--应用悲观锁:

即对于写操作,是不能进行并发操作的;

MVCC技术总结:

01 mvcc采用乐观锁机制,实现非锁定读取;

02 在RC级别下,事务中可以立即读取到其它事务提交过的readview数据快照信息;

03 在RR级别下,事务中从第一次查询开始,生成一个一致性readview,直到事务结束

==================================================================================================

  • 保证写隔离性

方式一:利用隔离级别保证

在应用不同隔离级别时也会有不同的锁机制

  • RC:具有记录锁机制;
  • RR:具有间隙锁机制+下一键锁机制(next lock) 表锁

方式二:利用锁进制隔离(保护并发访问资源)

类型 锁机制 简述说明
内存资源锁 latch(闩锁) 主要是保护内存资源;rwlock(读写锁)、mutex(只读锁)
避免不同程序争用相同地址区域内存资源)
元数据锁 MDL 主要是保护元数据资源,限制DDL操作;metadata lock
表级别锁 table_lock 主要是保护整个数据表资源;
命令方式锁表 lock table t1 read;
工具方式锁表 利用mysqldump、XBK(PBK)进行备份非InnoDB数据时,将触发FTWRL全局锁表;
行锁升级为表锁 比如做数据更新操作时,没有设置索引条件信息,就会出现全表扫描,出现表锁;
行级别锁 row_lock InnoDB默认锁粒度,加锁方式都是在索引上加锁的;
record lock 记录锁,在聚簇索引锁定,在RC级别只有record lock
gap lock 间隙锁,在辅助索引间隙加锁,在RR级别存在,防止幻读;
next look 下一键锁,即GAP+Record,在RR级别存在,防止幻读;

从功能应用方面进行锁分类:了解

  • IS:表示意向读锁或查询锁,可以在表上进行加锁做提示(select * from t1 lock in shared mode);
  • S:表示读锁或查询锁,现在基本上没有自动设置了,除非手工进行设置锁定(lock table t1 read);
  • IX:表示意向写锁或排他锁,可以在表上进行加锁做提示(select * from t1 for update)
  • X:表示写锁或排他锁,限制其他人的指定操作行为;

官方参考资料链接:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html