面试官:MySQL 是如何实现 ACID 的? 您所在的位置:网站首页 mysql怎么保证acid 面试官:MySQL 是如何实现 ACID 的?

面试官:MySQL 是如何实现 ACID 的?

#面试官:MySQL 是如何实现 ACID 的?| 来源: 网络整理| 查看: 265

今天,本文主要探讨MySQL InnoDB 引擎下ACID的实现原理,对于诸如什么是事务,隔离级别的含义等基础知识不做过多阐述。

ACID

MySQL 作为一个关系型数据库,以最常见的 InnoDB 引擎来说,是如何保证 ACID 的。

(Atomicity)原子性: 事务是最小的执行单位,不允许分割。原子性确保动作要么全部完成,要么完全不起作用;

(Consistency)一致性: 执行事务前后,数据保持一致;

(Isolation)隔离性: 并发访问数据库时,一个事务不被其他事务所干扰。

(Durability)持久性: 一个事务被提交之后。对数据库中数据的改变是持久的,即使数据库发生故障。

隔离性

先说说隔离性,首先是四种隔离级别。

不同的隔离级别是为了解决不同的问题。也就是脏读、幻读、不可重复读。

 

那么不同的隔离级别,隔离性是如何实现的,为什么不同事物间能够互不干扰?答案是 锁 和 MVCC。

先来说说锁, MySQL 有多少锁。

粒度

从粒度上来说就是表锁、页锁、行锁。表锁有意向共享锁、意向排他锁、自增锁等。行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。

行锁的种类

在 InnoDB 事务中,行锁通过给索引上的索引项加锁来实现。这意味着只有通过索引条件检索数据,InnoDB才使用行级锁,否则将使用表锁。行级锁定同样分为两种类型:共享锁和排他锁,以及加锁前需要先获得的意向共享锁和意向排他锁。

共享锁:读锁,允许其他事务再加S锁,不允许其他事务再加X锁,即其他事务只读不可写。select...lock in share mode 加锁。

排它锁:写锁,不允许其他事务再加S锁或者X锁。insert、update、delete、for update加锁。

行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

行锁的实现算法

Record Lock

单个行记录上的锁,总是会去锁住索引记录。

Gap Lock

间隙锁,想一下幻读的原因,其实就是行锁只能锁住行,但新插入记录这个动作,要更新的是记录之间的“间隙”。所以加入间隙锁来解决幻读。

Next-Key Lock

Gap Lock + Record Lock, 左开又闭。

锁之于隔离性

大致介绍了下锁,可以看到。有了锁,当某事务正在写数据时,其他事务获取不到写锁,就无法写数据,一定程度上保证了事务间的隔离。但前面说,加了写锁,为什么其他事务也能读数据呢,不是获取不到读锁吗?

MVCC

前面说到,有了锁,当前事务没有写锁就不能修改数据,但还是能读的,而且读的时候,即使该行数据其他事务已修改且提交,还是可以重复读到同样的值。这就是MVCC,多版本的并发控制,Multi-Version Concurrency Control。

版本链

Innodb 中行记录的存储格式,有一些额外的字段:DATA_TRX_ID和DATA_ROLL_PTR。

DATA_TRX_ID:数据行版本号。用来标识最近对本行记录做修改的事务 id。

DATA_ROLL_PTR:指向该行回滚段的指针。该行记录上所有旧版本,在 undo log 中都通过链表的形式组织。

undo log : 记录数据被修改之前的日志,后面会详细说。

 

ReadView

在每一条 SQL 开始的时候被创建,有几个重要属性:

trx_ids: 当前系统活跃(未提交)事务版本号集合。

low_limit_id: 创建当前 read view 时“当前系统最大事务版本号+1”。

up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号”

creator_trx_id: 创建当前read view的事务版本号;

开始查询

现在开始查询,一个 select 过来了,找到了一行数据。

DATA_TRX_ID = low_limit_id:

说明该数据是在当前read view 创建后才产生的,数据不显示。

不显示怎么办,根据 DATA_ROLL_PTR 从 undo log 中找到历史版本,找不到就空。

up_limit_id



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有