数据库事务、隔离级别到底是什么?实现原理是什么?

1.数据库事务
根据百度百科,数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

其实transaction也可以翻译成交易,比如大家耳熟能详的银行转账交易。交易成功的核心就在于A账户的转出操作与B账户的转入操作是否都能成功。

2.事务四大特性简介
众所周知,事务还有ACID四大特性,分别为原子性,一致性,隔离性和持久性。下面简述一下四大特性:

1)原子性:事务的定义中就包含了这个概念,即事务中包含的操作必须保证全部执行,不能部分执行。

2)一致性:如果事务中的操作全部执行或者全部不执行,我们可以说此时的数据是保持一致性的,只不过如果全部成功,是从一种一致性到另外一种一致性;如果全部失败,数据也是一致的,即与事务开始前的数据保持一致。后面讲到一致性的实现时会再具体阐述。

3)隔离性:比如现在有两个事务T1和T2,假设T1先开始,T2后开始,并且两个事务中都会用到数据D,那么针对于在T2执行期间能否看到T1操作后的数据D的问题,则是后面会提到的隔离级别的问题,此问题涉及到事务的隔离性。

4)持久性:凡是提到持久性,必然涉及到将数据写入磁盘的操作,以避免断电导致的数据丢失问题。

以上关于事务的定义和事务的四大特性只是概念性的描述,网上随便一搜都能找到,但是要想真正理解事务及其特性,就不得不了解其实现原理。如果想要彻底了解,需要先把知识分层,上面提到的事务及其四大特性是数据库原理层面的,也就是说无论用的是MySQL还是其他诸如Oracle或者是其他数据库,事务的概念和特性是一样的,但是具体到某一种DBMS(即常说的数据库软件例如MySQL),其实现细节会有所区别。

3.原子性,一致性和持久性实现原理
下面以MySQL为例,详细描述一下MySQL是如何实现原子性,一致性,隔离性和持久性的。在具体讲MySQL的四大特性的实现原理之前,我们首先对MySQL是什么做个简单的定义,MySQL=程序+文件(配置文件,日志文件,数据文件)。简单来说MySQL是一个软件,此软件是由管理数据的程序和各种文件组成的,其中文件又包含了不同的类别。所以我们常见的CRUD操作,本质上就是通过程序创建,检索,更新或者删除文件。

在讲之前,我们先来想一下如果让你来实现原子性和一致性,你会怎么做?假设现在有一个事务,包含A,B,C三个插入操作,假设执行到C时,程序出错,那么此时数据文件中只包含了A和B两个操作的数据。之前已经讲过,原子性的含义是全部执行或者全部不执行,那显然如果我们能捕获程序错误,然后回滚A和B操作,或者重试C操作,那么就能保证原子操作。那么如何回滚A和B?又如何重试C呢?我们可以把回滚信息存入文件,把事务信息也存入文件。MySQL的做法就是将事务信息写入redo log(即重写日志),而回滚信息写入undo log(即回滚日志)。所以通过redo和undo日志文件,MySQL实现了事务的原子性和一致性。其实此处的一致性只是保证了事务不会只被执行一部分操作,并不能保证事务一定会执行。之所以这么说是因为MySQL为了保证一致性,会先将redo日志写入磁盘,然后再写入数据文件,假如在写数据文件时MySQL挂掉了,当重启MySQL后程序会检查redo日志中记录的日志是否正常提交或者回滚,如果没有则会根据不同的策略选择来继续写数据文件或者是回滚。MySQL其实通过redo和undo日志同时也实现了持久性。

4.隔离级别简介
前三个特性讲完了,接下来讲下隔离性,在讲这个特性之前,先要说下什么是隔离级别?与事务的定义类似,隔离级别也是一个通用的概念,ISO和ANSI SQL标准中定义了四种隔离级别,分别为未提交读,提交读,可重复读和可串行化。下面我将通过图例来分别讲解下这四种隔离级别的含义。SQL标准虽然定义了一套关于隔离级别和问题的对应关系,但是各个数据库实现并不相同,并不是所有数据库都实现了SQL标准的四个隔离级别。下面以MySQL为例来对隔离级别做个简单介绍。假设有两个事务A和B,且两者均会操作Account表中的某一行数据。

1)未提交读

时间 转账事务A 转账事务B
t1 开始事务
t2 开始事务
t3 查询Account账户余额为0
t4 修改Account账户余额为500
t5 读取Account账户(500)
t6 修改Account账户余额为0
t7 提交事务

当事务隔离级别为未提交读时,事务B可以在事务A提交之前就能读取的中间数据,但是大家可以很明显的看出,此时B中数据其实是个脏数据。也即我们常说的未提交读会导致脏读问题。

2)提交读

时间 转账事务A 转账事务B
t1 开始事务
t2 读取Account账户余额为0
t3 开始事务
t4 查询Account账户余额为0
t5 读取Account账户(阻塞)
t6 修改Account账户余额为500
t7 修改Account账户余额为100
t8 提交事务
t9 读取Account账户(100)

当事务隔离级别为提交读,事务B开始时读取了一次账户信息,事务A提交后又查询了一次,两次结果不一样。很明显,此处出现了我们常说的不可重复读的问题,即B事务开始读取时为0,最后又读一次变为100。

3)可重复读

时间 转账事务A 转账事务B
t1 开始事务
t2 读取Account账户余额为0
t3 开始事务
t4 查询Account账户余额为0
t5 读取Account账户(0)
t6 修改Account账户余额为500
t7
t8 修改Account账户余额为100
t9 提交事务
t10 读取Account账户(0)

当事务隔离级别设置为可重复读,则事务B的读取操作不会影响到事务A。但是此时还可能有一个问题,那就是可能出现“幻读”,即如果事务B读取时没有记录,但是其他事务插入了一个新行,事务B再次读取某个范围时会查询到新插入的行,此行称为“幻行”。对于MySQL来说,可重复读是默认隔离级别,且MySQL通过MVCC和Next-Key Locking来实现可重复读,所以对于MySQL的可重复读来说,不存在幻读问题。(注:SQL标准的隔离级别和相关问题的对应关系。https://blog.csdn.net/qq_36431213/article/details/86497493)

4)可串行化

确保数据的一致性且可以接受没有并发。

5.隔离性实现原理
上面已经大体描述了隔离级别的含义,下面我们就来具体看下MySQL是如何实现事务隔离性的?简单来说,在MySQL中,事务的隔离性是通过锁来实现的。MySQL中有三种行锁算法,Record Lock(单行锁定),Gap Lock(间隙锁)和Next-Key Locking(Record+Gap)。在讲之前,还需要说到一个MySQL的特点,那就是MySQL有两种读取数据的模式,一种是一致性非锁定读,另一种是一致性锁定读。

先说一下一致性锁定读:
1)SELECT ... FOR UPDATE (显式加写锁)

2)SELECT ... LOCK IN SHARE MODE (显式加读锁)

再谈一下一致性非锁定读:
一致性非锁定读的含义是当事务读取的某行数据被其他事务锁定时,可以不用等待锁释放,而是读取此行的一个具有一致性的数据快照,而这个快照数据就存储在undo日志中。这种一行数据有多个版本的技术也叫做MVCC,即多版本并发控制。MVCC也是一个通用概念,有多种实现方式,MySQL是通过undo日志来实现的MVCC。

接下来我们继续讲解MySQL是如何实现四个隔离级别的:

1)未提交读:显然不需要对行加锁,这样其他事务就可以同时访问此行。

2)提交读:在InnoDB的默认配置下,此隔离级别采用一致性非锁定读模式。即如果此行正被其他事务锁定,那么MySQL通过MVCC技术,当前事务可以读取此行的一个最近提交的快照数据。

3)可重复读:在InnoDB的默认配置下,此隔离级别采用一致性非锁定读模式。MySQL通过Next-Key Locking锁定一个范围,使范围内的数据不能同时在其他事务中插入,避免了当前事务出现“幻行”问题。MySQL通过MVCC,在读取某行数据时,如果此行被其他事务锁定,则当前事务不用阻塞,直接读取当前事务开始时的此行的一个数据快照,来解决可重复读问题。即在整个事务中,读取的始终是此行的同一个数据快照。MySQL的可重复读已经达到了SQL标准的Serializable标准,即(无脏读,可重复读MVCC,无幻读Next-Key Locking)。

4)可串行化:串行化执行事务,即发现有其他事务正在操作此行,则此事务阻塞直到其他事务结束。MySQL会对每个select加上lock in share mode,不支持一致性非锁定读。

扩展:我们在真实使用事务的时候,常见场景有通过Spring配置数据源,配置ORM映射框架例如Mybatis,在使用事务的时候本质上是利用数据库的API,例如对于MySQL来说是begin, commit和rollback等。获取数据库连接,开启事务,提交事务等。Spring中的事务传播机制属于Spring自己的概念,本质上为根据不同的事务传播机制确定是否利用同一个连接,或者是否将多个事务逻辑通过同一个Connection或者多个Connection中来提交或者回滚,有兴趣的可以看下Mybatis关于事务的源码。

关于MySQL的ACID实现原理其实是比较复杂的,网上也很难找到比较完整的描述,本文也只是简单介绍,如果想要具体更细致的了解相关原理,推荐两本书《MySQL技术内幕InnoDB存储引擎》和动物书《高性能MySQL》,欢迎大家留言交流。
————————————————
版权声明:本文为CSDN博主「piezi.liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lunda5/article/details/104163805

版权声明:
作者:youlijiang
链接:https://www.cnesa.cn/734.html
来源:正群欣世
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>