InnoDB实现事务的原理

本文详细介绍了InnoDB存储引擎实现事务的原理,包括原子性、一致性、隔离性和持久性的概念及其在InnoDB中的实现方式。InnoDB通过Redo Log保证事务的持久性,使用Undo Log实现事务回滚和MVCC,保证并发事务的隔离性。文章还讨论了后台线程的作用,如Master Thread、IO Thread、Purge Thread等,以及缓冲池、表空间等InnoDB的逻辑存储结构。
摘要由CSDN通过智能技术生成

目录

MySQL的逻辑架构

后台线程

缓冲池

InnoDB逻辑存储结构

Redo Log

Undo Log

InnoDB事务特性

Atomicity(原子性)

Consistency(一致性)

Isolation(隔离性)

Durability(持久性)

事务特性实现原理

原子性实现原理

持久性实现原理

隔离性实现原理

行锁与表锁

并发事务读操作可能存在的三类问题

事务隔离级别

MVCC

ReadView

一致性实现原理


由于InnoDB在MySQL数据库中占有十分重要的地位,并且其实现事务的过程也十分复杂,涉及的面比较广,如果没有一定的知识背景很难理解它的设计思路。因此要想理清InnoDB事务实现的原理,我们很有必要了解一下MySQL的相关知识。

MySQL的逻辑架构

 

从图1可以看出MySQL有以下几部分组成

连接池组件

管理服务和工具组件

SQL接口组件

查询分析器组件

优化器组件

缓冲组件

插件式组件

物理文件

MySQL数据库区别于其他数据库的最重要的一个特点就是它的插件式存储引擎。MySQL插件式的存储引擎提供了一系列标准的管理和服务支持,这些标准与存储引擎本身无关。需要特别注意的是,存储引擎是基于表的,而不是数据库。

InnoDB存储引擎支持事务,其设计目标主要面向在线事务处理(OLTP)的应用。它的特点是行锁设计,支持外键,并支持非锁定读,即默认读取操作不会产生锁。从MySQL数据库5.5.8版本开始,InnoDB存储引擎是MySQL默认的存储引擎。

InnoDB存储引擎将数据放在一个逻辑的表空间中,这个表空间由InnoDB存储引擎自身进行管理。它可以将每个InnoDB存储引擎的表单独存放到一个独立的ibd文件中,因此它也支持裸设备用来建立它的表空间。

InnoDB通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了SQL标准的4种隔离级别,默认是Repeatable Read级别。同时,使用一种被称为next-key locking的策略来避免幻读(phantom)现象的产生。除此之外,InnoDB存储引擎还提供了插入缓存(insert buffer),二次写(double wrote),自适应哈希索引(adaptive hash index),预读(read ahead)等高性能高可用的功能。

对于表中数据的存储,InnoDB存储引擎采用了聚集(clustered)的方式,因此每张表的存储引擎会为每一行生成6字节的ROWID,并以此作为主键。

InnoDB体系架构

从图2中可以简单看出InnoDB存储引擎的体系结构,InnoDB有多个内存块,可以认为这些内存块组成了一个内存池,负责如下工作:

维护所有进程/线程需要访问的多个内部数据结构。

缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存。

重做日志(redo log)缓冲

... ...

后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存是最近的数据,此外将已经修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态。

后台线程

InnoDB存储引擎是多线程模型,因此其后台有多个不同的线程,负责处理不同的任务。

Master Thread:是一个非常核心的后台线程,主要负责将缓冲池中异步数据刷新到磁盘,保证数据一致性,包括脏页的刷新,合并插入缓存(insert buffer),undo页的回收。它具有最高的线程优先级别,其内部有多个循环(loop)组成:主循环(loop),后台循环(background loop),刷新循环(flsuh loop),暂停循环(suspend loop)。Master Thread会根据数据库运行状态在这几个循环进行切换。

Loop被称为主循环,因为大多数操作是在这个循环中,其中有两个操作,伪代码如下:

void master_thread{
    goto loop;
  loop;  
    for(int i = 0; i < 10; i++){
        thread_sleep(1)
        do log buffer flush to disk
        if(last_one_second_ios < 5)//前一秒IO次数<5
             do merge at most 5 insert buffer
         //当前缓冲池中脏页的比例是否超过了配置文件中的阈值,默认90%
        if(buf_get_modified_ratio_pct > innodb_max_dirty_page_pct)
            do buffer pool flush 100 dirty page//将100个脏页写入磁盘
        if(no user activity)
            goto background loop
    }
    if(last_ten_seconds_ios < 200)//判断过去10秒的IO操作是否小于200
        do buffer pool flush 100 dirty page//小于200InnoDB认为当前有足够的磁盘IO操作能力
    do merge at most 5 insert buffer
    do log buffer flush to disk
    do full purge//删除无用的undo页
    if(buf_get_modify_ratio_pct > 70%)//如果有超过70%的脏页比例
        do buffer pool flush 100 dirty page //刷新100个脏页到磁盘
    else
        buffer pool flush 10 dirty page//小于70%,刷新10个脏页到磁盘
    goto loop:
    background loop:
    do full purge//删除无用的undo页
    do merge 20 insert buffer
    if not idle:
    goto loop:
    else:
        goto flush loop
    flush loop:
    do buffer pool flush 100 dirty page
    if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
        goto flush loop
     goto suspend loop
     suspend loop:
     suspend_thread()
     waiting event
     goto loop;
}

从代码中可以看到,loop循环是通过thread sleep来实现的,这意味着所谓每秒一次或每10秒一次的操作是不精确的。在负载很大的情况会有延迟,只能说大概在这个频率。

每秒一次的操作包括:

日志缓冲刷新到磁盘,即使这个事务还没有提交(总是);

合并插入缓存(可能);

之多刷新100个InnoDB的缓冲池中的脏页到磁盘;

如果当前没有用户活动,则切换到background loop(可能)。

每10秒的操作包括:

合并至多5个插入缓存(总是)

将日志缓存刷新到磁盘(总是)

删除无用的Undo页(总是)

刷新100个或者10个脏页到磁盘(总是)

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值