MySQL事务

368 阅读8分钟

往期推荐

事务

事务是应用程序中一系列严密的操作(多条数据库操作语句的集合),所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做(回滚)。比如说,在实现转账功能时,A用户向B用户转帐使用update更新转账后的账户余额,B用户收到A用户的转账使用update更新账户余额,这些数据库操作语句就构成一个事务。在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。

事务的特性(ACID)

事务必须满足的4个条件(ACID):

原子性(Atomicity) 在事务的所有操作中,要么全部成功完成,要么全部不完成,不可能只成功执行了部分操作。如果事务在执行过程中发生错误,则会被回滚(Rollback)到事务开始前的状态。由undolog日志来保证,它记录了需要回滚的日志信息,事务回滚撤销已经执行成功的操作。

一致性(Consistency) 几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。一致性由其他三个特性来保证。

隔离性(Isolation) 当多个事务操作数据库中同一个记录或多个记录时,对事务进行隔离开来有序执行,避免同时对同一数据做操作(不同事务之间互不影响)。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的,由MVCC来保证。MySQL具有四种不同隔离级别,分别为:读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

持久性(Durability) 一个事务成功处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。持久性由redolog来保证,mysql修改数据时会在redolog中记录一份日志数据,就算数据没有保存成功,只要日志保存成功了,数据仍然不会丢失。

MySQL的四种隔离级别

MySQL标准定义了4种隔离级别,用来限定事务内外的哪些改变是可见的,哪些是不可见的。级别低的隔离级别一般支持更高的并发处理,且拥有更低的系统开销。

读未提交(read uncommitted) 读未提交,即所有事务都可以看到其他未提交事务的执行结果。读未提交是安全性最低的隔离级别,出现的事务并发问题就是脏读。如事务A读取了事务B未提交的更新的数据,而此时事务B回滚,那么A读取到的数据就是“脏数据”,这个过程就是“脏读”。 读已提交(read committed) 读已提交,读取已提交的内容,它满足了隔离的简单定义:一个事务只能看见已提交事务所做的改变,所以读已提交不会出现“脏读”(给写数据行级排他锁(其他事务不能读写),这样写过程的结果是无法读取的,直到事务处理完毕才释放排他锁,给读的数据加行级共享锁(只能读,不能写),这样读的时候也是无法写的,一旦读完该行就释放共享锁)。而读已提交出现的并发问题是“不可重复读”,如事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。 可重复读(repeatable read ) 可重复读是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行可重复读避免了“不可重复读”(给写的数据加行级排他锁(其他事务不能读写),事务结束释放,给读的数据加行级共享锁(其他事务只能读不能写),事务结束后释放),而可重复读出现的并发事务问题是“幻读” 。幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

  • InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题(MVCC只在读已提交和可重复读这两个隔离级别下工作,读取数据时通过一种类似快照的方式将数据保存下来,这样读锁和写锁就不冲突了,不同的事务的session会看到自己特定版本的数据和版本链)。

  • 聚集索引记录中两个隐藏列:

  1. trx_id: 用来存储每次对某条聚集索引记录进行修改的时候的事务id。
  2. roll_pointer:每次对哪条聚集索引记录有修改的时候,都会把老版本写入undo日志中,这个roll_pointer就是存入了一个指针,它指向这条聚集索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(插入操作的undo日志该属性值为null,因为它没有老版本)
  • 读已提交和可重复读的区别在于他们生成的ReadView的策略不同。
  1. 开始事务时创建readview,readview是维护当前活动的事务的id(活动的事务即未提交的事务)通过排序生成的一个数组。访问数据时,获取数据中的事务id(获取的是事务id最大的记录),然后和开始事务时创建的readview进行比较,如果比readview小(意味该事务已经提交),则可以访问数据。如果比readview大或者在readview中间[多个活动的事务id组成的数组](比readview数组中的第一个元素大意味该事务在readview生成之后出现的,在readview中意味着该事务还没提交),则不可以访问,获取roll_pointer,取上一版本的数据的事务id重新比较。

  2. 读已提交隔离级别下的事务在每次查询的开始都会生成一个独立的readview,而可重复读隔离级别则在第一次查询的时候生成一个readview,之后的查询都是复用之前的readview。

串行化 串行化是级别最高的隔离级别,它是通过在每个读的数据行上加共享锁,如果其他事务想对已经加上了共享锁的表进行改写,则会被挂起直到该数据行释放锁(可能导致大量的超时现象和锁竞争),强制事务串行执行。串行化解决了并发事务中的“幻读”问题。

MySQL 事务处理的两种方法

1、用 BEGIN、ROLLBACK、COMMIT来实现

  • BEGIN / start transactional 开始一个事务
  • ROLLBACK 事务回滚
  • COMMIT 提交事务

2、直接用 SET 来改变 MySQL 的自动提交模式:

  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交

使用保留点 SAVEPOINT

savepoint 是在数据库事务处理中实现“子事务”(subtransaction),也称为嵌套事务的方法。事务可以回滚到 savepoint 而不影响 savepoint 创建前的变化, 不需要放弃整个事务。ROLLBACK 回滚的用法可以设置保留点 SAVEPOINT,执行多条操作时,回滚到想要的那条语句之前(事务提交之前,当执行rollback时,通过指定保存点可以回退到指定的点)。

回退事务的操作(committed之前

  • 1.设置保存点 savepoint a
  • 2.取消保存点a之后事务 rollback to a
  • 3.取消全部事务 rollback

MySQL8.0事务的使用

  • 查看MySQL默认的事务提交方式

SELECT @@AUTOCOMMIT ,1代表自动提交事务,0代表手动提交

  • 设置事务提交方式 SET AUTOCOMMIT = 0或1

  • 查看MySQl默认的事务隔离级别(repreatable-read) SELECT @@transaction_isolation

image.png

  • 设置全局的事务隔离级别

set global transaction isolation level 隔离级别

  • 设置当前会话的隔离级别 set session transaction isolation level 隔离级别

默认标题_动态分割线_2021-07-15-0.gif