原子性(
Atomicity
):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
•
一致性(
Consistency
):事务完成时,必须使所有的数据都保持一致状态。
•
隔离性(
Isolation
):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环
境下运行。
•
持久性(
Durability
):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
那实际上,我们研究事务的原理,就是研究
MySQL
的
InnoDB
引擎是如何保证事务的这四大特性的。
而对于这四大特性,实际上分为两个部分。 其中的原子性、一致性、持久化,实际上是由
InnoDB
中的两份日志来保证的,一份是
redo log
日志,一份是
undo log
日志。 而持久性是通过数据库的锁,
加上
MVCC
来保证的。
我们在讲解事务原理的时候,主要就是来研究一下redolog,undolog以及MVCC。
3.2 redo log
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(
redo log buffer
)以及重做日志文件(
redo log
file
)
,
前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中
,
用
于在刷新脏页到磁盘
,
发生错误时
,
进行数据恢复使用。
如果没有
redolog
,可能会存在什么问题的? 我们一起来分析一下。
我们知道,在
InnoDB
引擎中的内存结构中,主要的内存区域就是缓冲池,在缓冲池中缓存了很多的数据页。 当我们在一个事务中,执行多个增删改的操作时,
InnoDB
引擎会先操作缓冲池中的数据,如果缓冲区没有对应的数据,会通过后台线程将磁盘中的数据加载出来,存放在缓冲区中,然后将缓冲池中的数据修改,修改后的数据页我们称为脏页。 而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。 而缓冲区的脏页数据并不是实时刷新的,而是一段时间之后将缓冲区的数据刷新到磁盘中,假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却没有持久化下来,这就出现问题了,没有保证事务的持久性。
那么,如何解决上述的问题呢? 在
InnoDB
中提供了一份日志
redo log
,接下来我们再来分析一
下,通过
redolog
如何解决这个问题。
有了
redolog
之后,当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在
redolog buffer
中。在事务提交时,会将
redo log buffer
中的数据刷新到
redo log
磁盘文件中。
过一段时间之后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于
redo log
进行数据恢复,这样就保证了事务的持久性。 而如果脏页成功刷新到磁盘 或 或者涉及到的数据已经落盘,此时
redolog
就没有作用了,就可以删除了,所以存在的两个
redolog
文件是循环写的。
那为什么每一次提交事务,要刷新
redo log
到磁盘中呢,而不是直接将
buffer pool
中的脏页刷新
到磁盘呢
?
因为在业务操作中,我们操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。 而
redo log
在
往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。 这
种先写日志的方式,称之为
WAL
(
Write-Ahead Logging
)。
3.3 undo log
回滚日志,用于记录数据被修改前的信息
,
作用包含两个
:
提供回滚
(
保证事务的原子性
)
和
MVCC(
多版本并发控制
)
。
undo log
和
redo log
记录物理日志不一样,它是逻辑日志。可以认为当
delete
一条记录时,
undo
log
中会记录一条对应的
insert
记录,反之亦然,当
update
一条记录时,它记录一条对应相反的
update
记录。当执行
rollback
时,就可以从
undo log
中的逻辑记录读取到相应的内容并进行回滚。
Undo log
销毁:
undo log
在事务执行时产生,事务提交时,并不会立即删除
undo log
,因为这些
日志可能还用于
MVCC
。
Undo log
存储:
undo log
采用段的方式进行管理和记录,存放在前面介绍的
rollback segment
回滚段中,内部包含
1024
个
undo log segment
。
4 MVCC
4.1 基本概念
1). 当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:
select ... lock in share mode(
共享锁
)
,
select ...
for update
、
update
、
insert
、
delete(
排他锁
)
都是一种当前读。
在测试中我们可以看到,即使是在默认的
RR
隔离级别下,事务
A
中依然可以读取到事务
B
最新提交的内容,因为在查询语句后面加上了
lock in share mode
共享锁,此时是当前读操作。当然,当我们
加排他锁的时候,也是当前读操作。
2). 快照读
简单的
select
(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
• Read Committed:每次
select
,都生成一个快照读。
• Repeatable Read
:开启事务后第一个
select
语句才是快照读的地方。
• Serializable
:快照读会退化为当前读。
在测试中
,
我们看到即使事务
B
提交了数据
,
事务
A
中也查询不到。 原因就是因为普通的
select
是快照
读,而在当前默认的
RR
隔离级别下,开启事务后第一个
select
语句才是快照读的地方,后面执行相同的
select
语句都是从快照中获取数据,可能不是当前的最新数据,这样也就保证了可重复读。
3). MVCC
全称
Multi-Version Concurrency Control
,多版本并发控制。指维护一个数据的多个版本,
使得读写操作没有冲突,快照读为
MySQL
实现
MVCC
提供了一个非阻塞读功能。
MVCC
的具体实现,还需要依赖于数据库记录中的三个隐式字段、
undo log
日志、
readView
。
接下来,我们再来介绍一下
InnoDB
引擎的表中涉及到的隐藏字段 、
undolog
以及
readview
,从
而来介绍一下
MVCC
的原理。
4.2 隐藏字段
4.2.1 介绍
当我们创建了上面的这张表,我们在查看表结构的时候,就可以显式的看到这三个字段。 实际上除了这三个字段以外,
InnoDB
还会自动的给我们添加三个隐藏字段及其含义分别是:
而上述的前两个字段是肯定会添加的, 是否添加最后一个字段
DB_ROW_ID
,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。
4.2.2 测试
1). 查看有主键的表 stu
进入服务器中的
/var/lib/mysql/itcast/ ,
查看
stu
的表结构信息
,
通过如下指令
:
ibd2sdi stu.ibd
查看到的表结构信息中,有一栏
columns
,在其中我们会看到处理我们建表时指定的字段以外,还有额外的两个字段 分别是:
DB_TRX_ID
、
DB_ROLL_PTR
,因为该表有主键,所以没有
DB_ROW_ID隐藏字段。
2). 查看没有主键的表 employee
建表语句:
create table employee (id int , name varchar(10));
此时,我们再通过以下指令来查看表结构及其其中的字段信息:
ibd2sdi employee.ibd
查看到的表结构信息中,有一栏
columns
,在其中我们会看到处理我们建表时指定的字段以外,还有额外的三个字段 分别是:
DB_TRX_ID
、
DB_ROLL_PTR
、
DB_ROW_ID
,因为
employee
表是没有指定主键的。
♥️关注,就是我创作的动力
♥️点赞,就是对我最大的认可文章来源:https://www.toymoban.com/news/detail-508487.html
♥️这里是小刘,励志用心做好每一篇文章,谢谢大家文章来源地址https://www.toymoban.com/news/detail-508487.html