MySQL事务隔离级别详解

Posted 晴天的空间

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL事务隔离级别详解相关的知识,希望对你有一定的参考价值。

一、什么是事务?

事务是逻辑上的一组操作,要么全执行,要么全不执行。

事务最经典栗子也经常被拿出来的栗子就是银行转账了。比如小明要给小红转账1000元,这个转账会涉及到两个关键操作:将小明的余额减1000元,将小红的余额减1000元。万一这两个操作之间突然出现错误,导致小明余额减少但是小红余额没有增加,这种情况是肯定不允许的。事务就是保证这两个关键操作要么都成功,要么都不成功。

二、事务的特性(ACID)

  • **原子性:**事务最小的执行单位,不允许分割。事务的原子性确保动作要么全部执行,要么全部不执行。
  • **一致性:**执行事务的前后,数据保持一致。例如转账的业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。
  • **隔离性:**并发访问数据库时,一个用户的事务不应该被其他事务所影响,各并发事务之间数据库是独立的。
  • **持久性:**一个事务被提交后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有影响。

三、并发事务带来的问题

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但是可能会带来以下的问题:

  • **脏读(Dirty read):**当一个事务正在访问数据并且对其进行了修改,但是还没提交事务,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据的修改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是“脏读”,依据“脏数据”所做的操作可能是会出现问题的。
  • 修改丢失(Lost of modify):是指一个事务读取一个数据时,另外一个数据也访问了该数据,那么在第一个事务修改了这个数据之后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,这种情况就被称为修改丢失。例如:事务1读取表中数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果都是19,但是事务1的修改记录丢失了。
  • 不可重复读(Unrepeatableread):指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况就被称为不可重复读。
  • 幻读(Phantom read):幻读与不可重复读类似,幻读是指一个事务读取了几行数据,这个事务还没结束,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读

不可重复读和幻读区别:

不可重复读的重点是修改,幻读的重点是新增或者删除。

栗子1(同样的条件,你读取过的数据,再次读取的时候不一样了):事务1中的A先生读取自己的工资是1000的操作还没结束,事务2的B先生就修改了A先生的工资为2000,A先生再次读取自己工资的时候就变成2000了,这就是不可重复读。

栗子2(同样的条件,第1次和第2次读取出来的记录条数不一样):假如某工资表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,总共查询到4条记录,这是事务2又查询了一条工资大于3000的记录,事务1再次读取查询到的记录就是5条了,这就是幻读。

四、事务隔离级别

SQL标准定义了四个隔离级别:

  • **读取未提交(READ-UNCOMMITTED):**最低的隔离级别,允许读取尚未提交的数据变更,可能造成脏读、不可重复读、幻读。
  • **读取已提交(READ-COMMITTED):**允许读取并发事务已经提交的数据,可以避免脏读,但是可能造成不可重复、幻读。
  • **可重复读(REPEATABLE-READ):**对同一字段多次读取的结果都是一致的,除非本身事务修改,可以避免脏读和不可重复读,但是可能造成幻读。
  • **可串行化(SERIALIZABLE):**最高的隔离级别,完全服从ACID的隔离级别,所以的事务依次执行,可以避免脏读、不可重复读、幻读。
隔离级别脏读不可重复读幻读
读取未提交
读取已提交×
可重复读××
可串行化×××

mysql InnoDB存储引擎默认的事务隔离级别是可重复读(REPEATABLE-READ),可以通过命令select @@tx_isolation;语句来查看,MySQL 8.0 该语句改为SELECT @@transaction_isolation;

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

MySQL InnoDB存储引擎的可重复读并不能避免幻读,需要应用使用加锁读来保证,这加锁读使用到的机制就是Next-Key Locks

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是读取已提交(READ-COMMITTED)InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。

InnoDB存储引擎在分布式事务的情况下一般会用到可串行化隔离级别。

🌈 拓展一下(以下内容摘自《MySQL 技术内幕:InnoDB 存储引擎(第 2 版)》7.7 章):

InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对事务的原有ACID要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。

四、实际情况演示

MySQL命令行的默认配置中事务都是自动提交的,即执行SQL语句就会马上执行COMMIT操作。可以用命令START TRANSACTION开始一个事务。

我们可以通过下面命令设置事务隔离级别。

SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]

我们再来看一下我们在实际操作中使用到的一些并发控制语句:

  • START TRANSACTION | BEGIN :显示的开启一个事务。
  • COMMIT:提交事务,使得对数据库做的所有修改成为永久性。
  • ROLLBACK:回滚到结束用户的事务,并撤销正在进行的所有未提交的修改。

(脏读)读取未提交

(避免脏读)读取已提交

不可重复读

还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。

可重复读

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysjbfC4b-1651149978452)(https://qtspace.cn/contentimg/81.jpg)]

幻读

演示幻读出现的情况

sql 脚本 1 在第一次查询工资为 500 的记录时只有一条,sql 脚本 2 插入了一条工资为 500 的记录,提交之后;sql 脚本 1 在同一个事务中再次使用当前读查询发现出现了两条工资为 500 的记录这种就是幻读。

幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除。

解决幻读的方法

  • 将事务隔离级别调整为 SERIALIZABLE
  • 在可重复读的事务级别下,给事务操作的这张表添加表锁。
  • 在可重复读的事务级别下,给事务操作的这张表添加 Next-Key Locks

说明:Next-Key Locks 相当于 行锁 + 间隙锁

参考:https://javaguide.cn/database/mysql/transaction-isolation-level.html

mysql 事务隔离级别 详解

问题

在工作中真实遇到的问题:用python连接mysql,查询数据,同时有别的代码在更新mysql中的数据,前者是一直是保持连接的数据库,每一分钟select一次,但第二次却查不到更新后的数据?why?

查询资料结果

应该是mysql的事务隔离级别的设置问题,把 mysql 的 Innodb 的默认事务隔离级别是重复读repeatable read)修改为 提交读read committed) 即可。

什么是事务?

概念:

  例如:在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序

举例

  修改表中的数据,首先是删除原有记录,然后再插入,有2步,但是如果插入失败的话会导致数据丢失。

  用事务的概念来处理:删除和插入是一件事情,只有2步都成功,数据才发生改变。否者,恢复到原来的数据。

mysql事务的隔离级别

1.Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

2.Repeatable read (可重复读):可避免脏读、不可重复读的发生。【在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交】

3.Read committed (读已提交):可避免脏读的发生。【只有在事务提交后,其更新结果才会被其他事务看见】

4.Read uncommitted (读未提交):最低级别,任何情况都无法保证。

** 对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。

** 每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。

在MySQL数据库中查看当前事物的隔离级别:

select @@tx_isolation; 或者是 show variables like ‘%iso%‘;

在MySQL数据库中设置事务的隔离级别为 Read Committed :

set global transaction isolation level read committed;

技术分享图片

如果遇到不允许修改:

技术分享图片

是因为权限问题,先登出mysql,切换到root下,然后再去修改 @@tx_isolation

数据库事务的四大特性(ACID)

 1.原子性(Atomicity)

  是指事务包含的所有操作要么全部成功,要么全部失败回滚。因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

2.一致性(Consistency)

  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

  拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

3.隔离性(Isolation)

  隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

  即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

  关于事务的隔离性数据库提供了多种隔离级别,即上文提到的。

4.持久性(Durability)

   持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

  例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

 

参考链接

https://blog.csdn.net/dong976209075/article/details/8802778

http://blog.itpub.net/30127122/viewspace-2134370/

https://www.cnblogs.com/fjdingsd/p/5273008.html

https://www.cnblogs.com/ctaixw/p/5867414.html

以上是关于MySQL事务隔离级别详解的主要内容,如果未能解决你的问题,请参考以下文章

MySQL事务隔离级别详解

MySQL事务隔离级别详解

MySQL事务隔离级别详解

MySQL 中事务以及事务隔离级别的详解

MySQL事务隔离级别锁相关的详解

mysql 事务隔离级别详解