Mysql Innodb 事务实现原理

ACID

  1. 原子性:要么都成功,要么都失败
  2. 一致性:事务前后状态一致,比如某张表的id是唯一约束,不能重复。则无论事务成功失败,都不能出现重复的id。
  3. 隔离性:A事务不应该访问到B事务没有提交的数据
  4. 持久性:事务一旦提交,结果是永久的。

事务分类

1. 扁平事务

最简单也最频繁的事务。就是begin开始,中间一个或几个编辑操作,然后一起提交。整体要么都成功要么都失败。
缺点:只能全部回滚,不能部分回滚。在某些大事务中,代价太大

2. 带保存点的扁平事务

对上诉问题的优化处理,可以设置保存点,回滚时选择回滚到哪个保存点。
缺点:如果是大事务,所有持有锁是不会释放的。

select * from a;
set autocommit =0;
start transaction ;
insert into a(id) values (12);
savepoint x;
insert into a(id) values (13);
savepoint m;
insert into a(id) values (14);
rollback to x;
select * from a;

3. 链事务

保存点模式的一个变种。由多个小事务组成,上一个事务的结果对下一个事务可见,只能回滚当前节点的事务。进行下一个事务时,会释放上一个事务的持有锁。
Mysql Innodb 事务实现原理_第1张图片

4. 嵌套事务

5. 分布式事务

事务的实现

  1. 事务的隔离性由锁机制实现
  2. 原子性、持久性由数据库的redo log实现。
  3. 一致性由数据库的undo log实现。

undo并不是redo的逆操作。

  1. undo log和redo log都可视为是一种恢复操作。redo恢复的是提交事务修改的操作,而undo回滚行记录到某个特定版本。
  2. redo是物理日志,记录的是页的物理修改操作日志。undo 是逻辑日志,根据每行进行记录。

redo log

  1. redo log由两部分组成,一是内存中的redo log buffer,其是易失的;二是redo log file,是持久的;
  2. Innodb是事务的存储引擎,事务的提交必须是数据写入文件之后的commit操作才算完成。这就提到了之前介绍的提前写日志机制double write(详见Mysql之Innodb引擎)
  3. redo log是顺序写入的,在数据库运行时不需要对redo log的文件进行读取操作。
  4. redo log允许用户设置非持久性的情况,这样可显著提高数据库性能,但是如果宕机,会丢失最近一段时间的数据。
    innodb_flush_log_at_trx_commit 默认1,表示事务提交时,必须执行一次fsync。0表示提交时不进行写入redo log的操作。2表示提交时写入redo log file,但不执行fsync。此时数据并没有真正写入文件,而是在操作系统的缓存中,此时如果数据库重启,数据不会丢失,但如果系统重启,则可能丢失。
    注:mysql还有一种二进制日志binlog其作用是PIONT-IN-TIME恢复和主从复制环境的建立。从本质上,两者有很大的不同。
redo log binlog
出处 是Innodb引擎 mysql 数据库
内容形式 物理格式日志,记录的是对每个页的修改 逻辑日志,记录的是对应的sql
写入时间 事务进行中不断写入 只在提交时写入一次

redo log 结构

Mysql Innodb 事务实现原理_第2张图片

log group
  1. log group指重做日志组,有多个重做日志组成。
  2. 是逻辑概念。
1 redo log file 前2kb
  1. 对于log group的第一个redo log file 前2kb不存储log block(第二个也要预留2kb空间),而是保存4个512b的块,其存放内容如图所示。
  2. 由于这部分数据的存在,redo log file的写入并不是完全顺序的。
  3. 这部分数据对于Innodb存储引擎的恢复操作非常重要和关键。
  4. log file header后面的checkpoint的值是交替写入的。这样避免了因介质失败而导致无法找到可用checkpoint的情况。
log block

redo log是由log block组成,每个log block为512字节。由于重做日志块的大小和磁盘扇区大小一样都为512字节,因此重做日志的写入可以保证原子性,不需要double write;
log block的结构
由三部分组成,大小如图所示:

  1. log block header
    LOG_BLOCK_HDR_NO: 把log buffer看作一个数组,该值表示在数组中的位置。递增并且循环使用
    LOG_BLOCK_HDR_DATA_LEN:表示log block所占用的大小。
    LOG_BLOCK_FIRST_REC_GROUP:表示log block中第一个日志所在的偏移量
    LOG_BLOCK_CHECKPOINT_NO:该log block最后被写入时检查点第4字节的值
    Mysql Innodb 事务实现原理_第3张图片

undo log

  1. 用作事务回滚和MVCC功能。
  2. undo log是逻辑日志。回滚时只是将数据逻辑的恢复到原来的样子。因为在多用户并发系统中,可能会用多个事务对同一个页的不同记录进行修改,因此不能将一个页回滚到事务开始的样子。(Innodb存储引擎回滚时,它实际做的是一个与原来相反的操作。对于insert,则会delete;对于delete,则会insert;update会执行相反的一个update)
  3. undo log也会产生redo log,因为undo log也需要持久性的保护(事务在undo log segment分配页并写入undo log的过程同样是需要写入redo log)。
  4. undo log是需要进行随机读写的,为了提高磁盘利用率。

undo log 存储管理

Innodb存储引擎对undo采用段的方式进行管理。首先Innodb存储引擎有rollback segment。每个rollback segment有1024个 undo segment,在每个undo segment中申请undo段。共享表空间偏移量5的页记录了所有rollback segment header所在的页。

show variables like 'innodb_undo_%'
字段 说明
innodb_undo_directory . 用于设置rollback segment文件所在路径,“.”表示当前Innodb存储引擎的目录
innodb_undo_logs 128 用于设置rollback segment的个数,表示可支持128 * 1024 个事务
innodb_undo_tablespaces 3 用于设置rollback segment文件的数量,这样rollback segment可以较为平均的分布在多个文件中
事务提交时,Innodb存储引擎需要做两件事
  1. 将undo log放入列表,以供之后的purge操作;
  2. 判断undo log所在的页是否可以重用,可以则分配给下一个事务。

事务提交时,并不能立即删除undo log,因为可能还有其他事务需要根据undo log来获取之前版本的记录。故将undo log放入一个链表,是否可以删除由purge线程判断。

show engine innodb status;
-- 结果 --
-- history list length 12 :代表redo log的数量 --
  1. undo log的格式
    Mysql Innodb 事务实现原理_第4张图片
  2. 查看undo log信息:可以通过两张表来查询:INNODB_TRX_ROLLBACK_SEGMENT、INNODB_TRX_UNDO。

purge

  1. purge用于完成最终的delete和update操作。这是因为支持MVCC,所有记录不能在事务提交时立即进行处理。若改行没有被任何事务引用,那么才执行真正的操作。
  2. history list根据事务提交顺序,将undo log进行连接,先提交的事务总是在尾端。
  3. 在purge过程中,首先innodb存储引擎会根据history list找到第一个需要被清除的记录,清楚之后会在该undo log所在的页查找是否有其他可以被清除的记录。如有则清除,没有则再去history list中查找。这种设计避免了大量的随机IO,从而提高效率。

group commit

若不是只读事务,则每次事务提交时都需要进行一次fsync操作,确保redo log刷到磁盘。但是fsync性能有限,为了提高效率,一次fsync会确保多个事务日志刷新到磁盘。

Innodb存储引擎事务提交时会进行两个操作

1)修改内存中事务对应的信息,将日志写入redo log buffer.
2)调用fsync将确保日志从redo log buffer写入到redo log。
对于步骤1)来说,步骤2)时一个缓慢的过程,在进行步骤2)时,其他事务可以进行步骤1)的操作,当正在提交的事务完成提交操作后,再次进行步骤2)时,可以将多个事务的redo log一次fsync刷到磁盘。

BLGC(binary log group commit)

为了弥补Innodb1.2之前,在开启二进制日志后,group commit失效问题。

BLGC事务提交过程

Mysql Innodb 事务实现原理_第5张图片
在 MYSQL数据库上层进行提交时首先按顺序将其放入一个队列中,队列中的第一个事务称为 leader,其他事务称为 follower,leader控制着 follower的行为。BLGC的步骤分为以下三个阶段:

  1. Flush阶段,将每个事务的二进制日志写入内存中。
  2. Sync阶段,将内存中的二进制日志刷新到磁盘,若队列中有多个事务,那么仅一次 fsync操作就完成了二进制日志的写人,这就是BLGC。
  3. Commit阶段,leader根据顺序调用存储引擎层事务的提交,INNODB存储引擎本就支持 group commit,因此修复了原先由于锁 prepare_ commit mutex导致group commit失效的问题。

当有一组事务在进行 Commit阶段时,其他新事务可以进行 Flush阶段,从而不会使group commit失效。当然 group commit 的效果由队列中事务的数量决定,若每次队列中仅有一个事务,那么可能效果和之前差不多,甚至会更差。但当提交的事务越多时,group commit的效果越明显,数椐库性能的提升也就越大。

你可能感兴趣的:(mysql,数据库,mysql)