MYSQL的事务机制详细介绍(ACID和隔离级别)

MYSQL的事务机制详细介绍(ACID和隔离级别)

什么是事务?

  • 定义:一个事务是由一个或者多个SQL语句组成的不可分割的单元。只有事务中的所有操作都正常完成,事务的结果才会提交给数据库;如果有部分数据处理失败,那么整个事务都回退到此事务执行前的状态。所以事务要么成功,要么失败。
  • 注意:
    1.事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,保证事务执行的原子操作
    2.事务的所有SQL全部执行完成,才能提交(commit)事务,把结果写入磁盘。
    3.在SQL执行过程中,某个SQL执行出错,事务会回滚(rollback)到初始状态。
    4.设置保存点(savePoint),可以使事务回滚到该保存点的状态,而不是事务的初始状态。相当于生成快照来保护现场。
    !!!:只有InnoDB存储引擎支持事务,所以关于事务的操作一定要在InnoBD存储引擎下进行。

事务的ACID特性

  • 事务的原子性(Atomic):事务是一个不可分割的整体,必须具有原子特性(虽然可能是多个操作,但必须把多个操作当成一个原子操作,不可再分)
  • 事务的一致性(Consistency):一个事务执行前后必须保持数据库的状态一致(一致不是指相同),数据库的一致性状态需要用户负责,由并发控制机制实现。(例:网购时将商品加入购物车需要两步:减少商品的库存,将商品加入购物车,这两个执行必须保证一致性)如果并发控制不当,就会导致数据的不一致问题(数据脏读、不可重复读、幻读)。
  • 事务的隔离性(Isolation):当多个事务并发操作时,为了保证事务的安全性,将一个事务内部的操作和其他事务隔离开。比如:事务A执行过程中,其他并发操作的事务不可访问事务A的结果(事务A执行完后其他事务才可以访问事务A的结果)。(这里会引出事务的隔离级别,会在下文介绍)
  • 事务的持久性(Durability):事务完成之后,数据库会把该事务修改的数据永久性的保存下来,即使数据库出现故障,也能恢复数据。

事务的隔离级别

  1. 先来看看事务操作不经隔离,并发处理时会出现的三种现象:
  • 脏读(Dirty Read):一个事务读取了另一个事务未提交的数据。
    例:事务A和事务B并发执行,事务A中更改了一个表中money的值,但还没提交(commit)这个结果,这时事务B读取到了事务A改变的money值,而事务A可能执行出错而回滚(rollback),事务B读到的数据就是事务A执行出错的数据(脏数据)。这个过程就称为脏读。
  • 不可重复度(NONRepeatable Read):一个事务的操作导致另一个事务前后两次读取到不同的数据。
    例:事务A和事务B并发执行,事务B先读取了一个表中money的值后(事务B还没结束),事务A更新这个money值,更新并提交(事务A结束)后事务B又去读取money值,发现前后两次读取的数据不一样。
  • 虚读/幻读(Phantom Read):一个事务的操作导致另一个事务前后两次查询结果的数据量不同。
    例:事务A和事务B并发执行,事务B先读取到一个表中的数据,事务A再添加或删除了一条满足事务A读取条件的记录,事务B再次读取时发现读取到上一次不存在的记录或者发现上次读到的某条记录不见了。

注意:

  • 不可重复读和虚读中的事务B 前后两次查询为一个事务整体,不是说‘’第一次查询后事务B就结束,第二次查询又再执行一遍事务B‘’;而是事务B包括前后两次查询操作。
  • 不可重复读指前后读取的同一个数据的内容不一致,虚读指前后读取的数据量(数据量)不一致
  1. 在JDBC编程中,Connection对象定义的5种事务隔离级别:
  • Transation_None:表示不支持事务
  • Transation_Read_Uncommited:未提交读(一个事务A的执行结果提交前另一个事务可以读取事务A的操作数据)。这个级别就会出现脏读、不可重复读、幻读
  • Transation_Read_Commited:已提交读(一个事务A的执行结果提交前另一个事务不可以读取事务A的操作数据,提交后才可以读)。这个级别可以避免脏读,但仍然会出现不可重复读和幻读。
  • Transation_Repeatable_Read:可重复读(事务保证可以再次读取相同的数据)。这个级别避免了脏读和不可重复读,但会出现幻读。
  • Transation_Serializable:可序列化/串行化(事务与事务之间不能并发处理,按顺序一个事务一个事务的执行)。隔离的最高级别,可以避免脏读、不可重复读、幻读。

注意:事务的隔离级别越高,解决的问题越多,但花费的代价也越大(串行化意味着不并发,这样执行效率低)
在这里插入图片描述

  1. JDBC中与事务相关的操作
    具体使用解析请看JDBC如何处理事务和隔离级别
  • 调用Connection对象的setAutoCommit(false)方法来禁止自动提交事务,然后就可以把多个数据库操作的SQL语句作为一个事务,在操作完成以后调用commit()方法来实现事务提交。如果其中一个语句执行失败,就会抛出异常而不会调用commit()。在这种情况下,就可以在异常代码处理中调用rollback()对已经发生的事务操作进行回滚。通过此种方法可以保持对数据库进行多次操作后,数据仍然是保持一致的。
  • 使用JDBC对数据库的事务设置隔离级别:在调用Connection对象的setAutoCommit(false)方法之前,调用Connection对象的setTransactionIsolation(int level)即可设置当前链接的隔离级别。我们可以看此方法的部分源码来确定每个隔离级别对应的int值:
    在这里插入图片描述
    在这里插入图片描述
  • 隔离级别的设置只对当前连接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个连接,也称作一个会话,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个连接(或者称一个会话),而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关,也就是说每个Connection对象都需要设置各自的隔离级别。
  1. MYSQL中与事务相关的SQL
  • 查看MYSQL是否自动提交事务:select @@autocommit;
    在这里插入图片描述
    如果是自动提交(1),意味着每执行一个SQL就会自动提交结果。
  • 修改手动/自动提交事务:set autocommit=0/1;(默认是1)
    在这里插入图片描述
    注意:修改只是暂时的,退出MySQL后下次再启动autocommit=1;
  • 开启一个事务:begin;
  • 提交一个事务:commit;
  • 设置一个名为point1的保存点:savepoint point1;
  • 回滚一个事务到初始位置:rollback;
  • 回滚到point1:rollback to point1;

下面我举个例子:
在这里插入图片描述
开启事务后,我将student中id=4的neme改成了’wangwu’,虽然我没有commit,但是再次查询已经更改成功了!!!这是为啥???
我来解释一下:开启一个事务,MySQL会为你操作的数据建一个缓存(准确的说是一个副本,这里的缓存和服务器在内存中创建的方便下次操作同一个数据的缓存还不太一样,事务中的缓存可以理解为副本/快照,但也是在内存中创建的),这个副本是将内存中的数据复制一份去操作,而不直接操作内存中的原数据,如果事务操作没有commit,缓存中的数据就不会提交到内存,内存中的值也就不变,但缓存中的值已经改变,而我们在一个事务中操作的都是这个事务的缓存中的数据,所以查询的结果是更改的。
大家看一下,当我在没有commit的情况下把MySQL 关闭(exit)后,再次打开,去查询刚才更改的数据就会发现数据没有被更改。这是因为没有commit,副本中的数据就没有提交到内存,断电(MySQL服务器exit)后那个事务的副本就清除了。再次查询查询到的是刚从磁盘加载到内存的值。
在这里插入图片描述

  • 查看事务的隔离级别:select @@tx_isolation;
    在这里插入图片描述
    注意:
    (1)MySQL默认的隔离级别是可重复读
    (2)MySQL中事务的隔离级别的英文字段与JDBC中的不一样但有联系,下面给大家一张表:
    在这里插入图片描述
  • 设置隔离级别:set tx_isolation=‘隔离级别’;
  • 在这里插入图片描述
发布了19 篇原创文章 · 获赞 9 · 访问量 2216

猜你喜欢

转载自blog.csdn.net/weixin_44480874/article/details/99696580