微信搜索“coder-home”或扫一扫下面的二维码,关注公众号,第一时间了解更多干货分享,还有各类视频教程资源。扫描它,带走我
事务的并发会带来很多问题。比如脏读、不可重复读、幻读等问题。这么问题怎么修复呢?我们逐个复现一下,然后看下MySQL是怎么修复这些问题的。请参考文章:MySQL事务并发问题和解决方案
事务的概念
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
事务的四大特性
事务的四大特性:ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)。下面逐一说明特性的含义。
原子性
一个事务中对数据库所有的操作,要么都成功要么都失败,不能成功一部分失败一部分。
因为原子是参加化学反应的最小单位,所以在数据库事务的特性这里就引用了原子的这样的概念。事务也是对数据库操作的最小单位,这就是事务原子性的特征。
举例说明一下。我们使用经典的例子,两个人之间相互转账来说明事务的原子性的特征。
A向B转账50块钱,会发生两个动作:
- A的账户减少50块钱
- B的账户增加50块钱
上面的这两个动作需要同时成功才可以,不能A减少了B没有增加、或者A没有减少B却增加了。发生这两种情况的任何一种都不可以,这两个动作要么都成功,要么都失败。这就是事务的原子性的体现。
一致性
一个 事务执行的结果必须是使数据库从一个一致性状态
变到另一个一致性状态
。一致性与原子性是密切相关的。
我是不太明白这句话的含义,结合实例我才能理解的比较透彻。我还是举例说明一下吧,还是用转账的经典案例。
- A和B两人原先各有100块钱
- A和B两个账户的总数是200块=100+100。
- 如果此时,A给B转了50块钱,那么A的账户里减少50块,B账户里增加50块这两个动作要么都成功,要么都失败,这是事务的原子性的一种体现。
- 转账后,A和B两个账户的总数还是200块=50+150。
他们从原先的200=100+100变成另外一个状态的200=50+150,这个一致性状态的变化就是事务的一致性的体现。不能转账前总额是200=100+100,转账后变成250=50+200或者其他结果,这样数据库在事务的前后状态就不一致了,就违背了事务的一致性特征。
隔离性
数据库中多个事务之间是相互独立的,互不干扰的。不能因为某一个事务而影响到其他的事务。
举例说明,还是拿转账的案例来说。
- A和B之间相互转账,
- 如果与此同时C和D也在相互转账。
- E和F之间此时也在相互转账。
- 那么这三个事务是互不影响的,各自转各自的,他们彼此之间是互相透明的。感知不到其他事务的存在。
这个例子这就是事务隔离性的体现。
说到数据库的隔离性,就不得不提隔离性下面的四个隔离级别。隔离级别分为:读未提交、读已提交、可重复读、串行化四个级别。这4种隔离级别,并行性能依次降低,安全性依次提高。隔离级别越高,数据库的并发性和性能就越低。
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
读未提交
读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
换句话理解一下就是:别人改数据的事务尚未提交,我在我的事务中也能读到。
读已提交
读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
换句话理解一下就是:别人改数据的事务已经提交,我在我的事务中才能读到。
可重复读
可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
换句话理解一下就是:别人改数据的事务已经提交,我在我的事务中也不去读。
串行化
串行化,又叫序列化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。
- “读”锁和“读”锁之间相互冲突
- “写”锁和“写”锁之间相互冲突
- “读”锁和“写”锁之间也相互冲突。
当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
换句话理解一下就是:我的事务尚未提交,别人就别想读数据。
持久性
一个事务对数据库的影响是永久的,不会因为时间的改变而改变。即便是数据库发生了重启,事务对数据库的修改也不会因为重启而丢失,事务对数据库的修改是持久性的,也就是说事务对数据库的修改要落地到磁盘上。
我们还是使用转账的例子来说一下。
A给B转了50块钱,不能到了明天或后天这个转账记录就丢失了或失效了,也不能因为数据库重启而导致A向B转账50块钱这个数据记录而丢失。这就是事务的持久性的体现,事务对数据库的修改是永久的写在磁盘上的。
事务并发带来的问题
事务四大特性之一:隔离性的存在表明各个事务之间是互不影响,彼此感知不到对方的存在。
但是在数据库系统中,多事务并发存在是可能导致脏读、不可重复的、幻读的问题出现的。
脏读
脏读是指一个事务在执行过程中,前后读取到的数据不一致,是因为它读取到其它事务update
后但未提交的数据
注意:这里的重点是读取到其他事务insert 、update、delete
更新后还未提交
的数据。
脏读示例图如下所示:
不可重复读
不可重复读是指一个事务在执行过中,前后读取到的数据不一致,是因为读取到其它事务update
或delete
后并且已提交的数据。
注意:这里强调的是读取到其它事务update
更新或delete
删除后并且已提交
的数据。
不可重复读示例如下所示:
幻读
幻读是指一个事务在执行的过程中,前后读取到的数据不一致,是因为读取到其他事务insert
且已提交的数据。
注意:这里强调的是读取到其他事务insert
插入后并且已提交
的数据。读到更新和删除的的数据不算是幻读,算做不可重复读。
幻读示例如下所示:
查看修改MySQL的隔离级别
MySQL中事务的默认隔离级别为可重复读。而像Oracle、SQLserver、postgresql等厂商的数据库的默认事务隔离级别为读已提交。
所以,在从其他数据库产品向MySQL数据库迁移的时候,需要格外注意这一点。如果有必要,则需要将MySQL的默认隔离级别修改为读提交,从而避免业务系统在事务层级可能出现的bug。
-
查看MySQL的版本
本实验中所有的操作都是基于MySQL5.7.31版本来做的。不同的MySQL版本,有些参数名称可能不同,这里需要稍微注意一下。MySQL的版本如下:
mysql> select version(); +------------+ | version() | +------------+ | 5.7.31-log | +------------+ 1 row in set (0.01 sec)
-
查看MySQL当前默认的事务隔离级别
在修改之MySQL事务隔离级别之前,我们先查看一下当前使用的事务隔离级别是什么,如何查看?可以使用
show variables like 'transaction_isolation';
命令来查看。如下所示:mysql> show variables like 'transaction_isolation'; +-----------------------+-----------------+ | Variable_name | Value | +-----------------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set (0.01 sec) mysql>
-
修改MySQL的默认隔离级别
修改的时候,我们可以基于session级别来修改也可以基于global级别来修改。实验的时候,建议使用session级别来修改就可以。修改MySQL的session级别的隔离级别命令如下:
mysql> set session transaction_isolation='READ-UNCOMMITTED'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'transaction_isolation'; +-----------------------+------------------+ | Variable_name | Value | +-----------------------+------------------+ | transaction_isolation | READ-UNCOMMITTED | +-----------------------+------------------+ 1 row in set (0.01 sec) mysql>
接下来我会针对每一种隔离级别下面如何解决脏读、不可重复读、幻读来做针对性的实验操作。
请参考文章:MySQL事务并发问题和解决方案
微信搜索“coder-home”或扫一扫下面的二维码,关注公众号,第一时间了解更多干货分享,还有各类视频教程资源。扫描它,带走我