mysql的事务

1.什么是事务

事务是用户定义的一个操作序列,这些操作要么全部成功要么全部失败,是一个不可分割的工作单位(构成单一逻辑工作单元的操作集合)。如果某一事务成功,则在该事务中进行的所有更改均会提交,成为数据库中的永久组成部分。如果事务遇到错误且必须取消或者回滚,则所有更改均被清除。

在这里插入图片描述

mysql服务器逻辑架构从上往下可以分为3层:
第一层:处理客户端连接,授权认证。
第二层:服务器层,负责查询语句的解析,优化,缓存以及内置函数的实现,存储过程等。
第三层:存储引擎,负责mysql中数据的存储和提取。

mysql中服务器层不管理事务,事务是由存储引擎实现的。mysql支持事务的存储引擎有InnoDB,NDB Cluster等,其中InnoDB的使用最为广泛;其他存储引擎不支持事务,如Mylsam,Memory等。

2.事务操作

2.1 基本命令

START TRANSACTION
    [transaction_characteristic [, transaction_characteristic] ...]

transaction_characteristic: {
    WITH CONSISTENT SNAPSHOT
  | READ WRITE
  | READ ONLY
}

BEGIN [WORK]
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
SET autocommit = {0 | 1}

命令使用说明:
START TRANSACTION 或 BEGIN开始新的事务
COMMIT提交当前事务,使其更改永久化
ROLLBACK回滚当前事务,取消其更改
SET autocommit 禁用或启用当前会话的默认自动提交模式

2.2 执行流程

在这里插入图片描述

2.3回滚事务

ROLLBACK TRANSACTION:
回滚事务后,事务结束,放弃事务期间所做的任何修改,事务结束。该语句将显式事务或隐性事务回滚到事务的起点或者某个保存点。

ROLLBACK { TRAN | TRANSACTION } 
     [ transaction_name | @tran_name_variable
     | savepoint_name | @savepoint_variable ] 

2.4保存点

savepoint,保存点是事务中的一点。用于取消部分事务,当结束事务时,会自动删除该事务锁定义的所有保存点。当执行rollback时,通过指定保存点可以回退到指定的点。

3.事务特性

3.1 MySQL日志

InnoDB存储引擎提供了两种事务日志:

  • redo log(重做日志):可以理解为是当服务宕机时,重启后强制保持一致
  • undo log(回滚日志):可以理解是如果回滚时回滚到之前的某一个状态

其中redo log用于保证事务的持久性。undo log则是事务原子性和隔离性实现的基础。

undo log主要分为两种:
1.insert undo log:代表事务在insert 新纪录时产生的undo log,只在事务回滚时需要,并且在事务提交后可以被立刻丢弃
2.updata undo log:事务在进行updata或者delete时产生的undo log。不仅在事务回滚时需要,在快照读时也需要,所以不能随便删除。只有当快速读或者事务回滚不涉及该日志时,对应的的日志才会被purge线程同一清除。

3.2原子性(Atomic)

原子性是指事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中一个sql语句执行失败,则已经执行的语句也必须回滚,数据库退回到事务前的状态。

实现原理:undo log(回滚日志)。在mysql里数据每次修改前,都会把修改之前的数据作为历史保存到undo log里面,数据里面会记录操作该数据的事务id。当事务执行失败或者调用rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的状态。

3.3 持久性(Durability)

持久性也称永久性,指一个事务一旦提及,它对数据库中数据的改变就应该时永久性的。接下来的其他操作或者故障不应该对其执行结果有任何影响。

刷脏:InnoDB提供了缓存池(Buffer Pool),包含了磁盘中部分数据页的映射。当需要从数据库读取数据时,会首先从缓存池中读取,若缓存池中没有,则从磁盘读取后放到缓存池中。当需要向数据库中添加数据时,会首先写入缓存池中,缓存池中修改的数据就会定期刷新到磁盘中(这一情况称为脏刷)

脏刷引发的问题:Buffer Pool的使用大大提高了读写数据的效率,但也带来了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性就无法保证。解决这个问题通过重做日志解决。

实现原理:redo log

重做日志是一种基于磁盘的数据结构,用于在崩溃恢复期间纠正不完整事务写入的数据。

3.4一致性(Consistency)

事务操作成功后,数据库所处的状态和它的业余规则是一致的,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一),列完整性(如字段的类型,大小,长度符合要求),外键约束,用户自定义,用户自定义完整性(如账户前后,两个账户余额的和应该不变)。

一致性是事务追求的最终目标:原子性,持久性和隔离性,都是为了摆正数据库状态的一致性。

3.5 隔离性(lsolation)

隔离性是指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。与原子性,持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。

隔离性主要考虑最简单的读操作和写操作。
隔离性的探讨,主要可以分为两个方面:

  • (一个事务)写操作对(另一个事务)读操作的影响:MVCC保证隔离性
  • (一个事务)写操作对(另一个事务)写操作的影响:锁机制保证隔离性

3.5.1 事务的并发

取钱场景:老公去在 ATM 上取钱,老婆在柜台存钱,假设这个账户中有 1000 元:

  1. 老公首先执行查询操作,查询到账户余额为 1000 此时程序 将 1000 拿到内存中,老公取了 200 元,程序就执行了更新操作将账户余额改为 800。
  2. 但是当老公的程序没有 commit 的时候,老婆查询账户,此时账户余额还是 1000 元,老婆存入 200 元,程序执行了更新操作将账户余额改为 1200
  3. 然后老公将更新语句提交,接着老婆也将更新语句提交。
  4. 最后导致的结果就是该账户的余额为 1200,这就是更新丢失的问题。
  5. 引发更新丢失的根源就是查询上,因为双方都是根据从数据库查询到的数据再 对数据库中的数据进行更新的。

并发情况下,读操作可能存在的三大类型:

  • 脏读
  • 不可重复读
  • 幻读

3.5.2脏读:

当前事务(A)中可以读到其他事务(B)为提交的数据(脏数据),这种现象是脏读。
在这里插入图片描述

3.5.3 不可重复读:

在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象成为不可重复读

在这里插入图片描述

3.5.4 幻读

在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。

在这里插入图片描述

3.6 事务的隔离级别

sql标准中定义了四种隔离级别,并规定了每种隔离级别下上述几个问题是否存在。一般来说,隔离级别越低,系统开销越低,可支持的并发越高,但隔离性也越差。

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读不可能不可能可能
可串行化不可能不可能不可能

读未提交在并发是会导致很多问题,而性能相当于其他隔离级别提高却很有限,因此使用较少。
可串行化强制事务串行,并发效率很低,只有当对数据一致性要求极高且可以接收没有并发时使用,因此使用也较少。
在大多数数据库系统中,默认的隔离级别是读已提交(如Oracle)或可重复读,InnoDB默认的隔离级别就是RR。

4.MVCC

在sql标准中,可重复读是无法避免幻读问题的
InnoDB实现可重复读取(RR)解决了脏读,不可重复读,幻读等问题,使用了MVCC。

MVCC是多版本的并发控制协议,MVCC的目的是多版本并发控制,在数据库实现,就是为了解决读写冲突。

4.1 相关概念

当前读

像共享锁,排他锁这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

快照读

像不加锁的SELECT操作就是当前读,即不加锁的的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的卡考虑。快照读的实现是基于多版本并发控制器,即MVCC。但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的不一定是数据的最新版本,而有可能是之前的历史版本。

4.2 MVCC的实现原理

MVCC是为了实现读-写冲突不加锁,而这个读指的是快照读,而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现。
MVCC实现原理主要是依赖记录中的三个隐式字段,undo日志,Read Viev来实现。

4.2.1隐式字段

InnoDB存储引擎在每行数据的后面添加了三个隐藏字段

  • DB_TRX_ID(6字节):表示最近一次对本记录行做修改(insert/update)的事务ID
  • DB_ROLL_PTR(7字节):回滚指针,指向当前记录行的undo log信息
  • DB_ROW_ID(6字节):随着新插入而单调递增的行ID

在这里插入图片描述

DB_ROW_ID:当表没有主键或唯一非空索引时,innoDB就回使用这个行id自动产生聚簇索引。如果表有主键或者唯一非空索引,聚簇索引就不会包含这个行id了。

4.2.2记录行修改的具体流程

事务A(事务id为2)对该记录做出了修改,将Honor列内容改为“FMVP”:
1.事务A先对改行加排他锁
2.将改行数据加入undo log中,当作旧版本
3.修改数据,并且修改DB_TRX_ID为2,将回滚指针指向拷贝到undo log的旧版本
4.事务提交,释放排他锁。
在这里插入图片描述

4.2.3 Read View

Read View 是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID 。Read View 主要是用来做可见性判断的,把生成的读视图 (Read View)当作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的 DB_TRX_ID(即当前事务 ID )取出来,与系统当前其他活跃事务的 ID 去对比(由 Read View 维护)。当每个事务开启时,都会被分配一个 ID , 这个 ID 是递增的,所以最新的事务,ID 值越大.

假设当前列表里的事务id为[80,100]。

1.如果你要访问的记录版本的事务id为50,比当前列表最小的id80小,那说明这个事务在之前就提交了,所以对当前活动的事务来说是可访问的。

2.如果你要访问的记录版本的事务id为90,发现此事务在列表id最大值和最小值之间,那就再判断一下是否在列表内,如果在那就说明此事务还未提交,所以版本不能被访问。如果不在那说明事务已经提交,所以版本可以被访问。

3.如果你要访问的记录版本的事务id为110,那比事务列表最大id100都大,那说明这个版本是在ReadView生成之后才发生的,所以不能被访问。

MySQL 事务是指一组数据库操作,这些操作要么全部执行,要么全部不执行,其目的是保证在并发环境下,数据的一致性和完整性。MySQL 事务具有 ACID 性质,即原子性、一致性、隔离性和持久性。 MySQL 中使用事务需要使用 BEGIN、COMMIT 和 ROLLBACK 语句,其中 BEGIN 表示开启一个事务,COMMIT 表示提交事务,ROLLBACK 表示回滚事务事务的基本语法如下: ``` BEGIN; -- 执行一组数据库操作 COMMIT; -- 提交事务 -- 或者 ROLLBACK; -- 回滚事务 ``` 在 MySQL 中,事务的隔离级别分为四个等级,分别是 Read Uncommitted、Read Committed、Repeatable Read 和 Serializable。隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。 MySQL 事务的 ACID 性质有以下含义: 1. 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚,不会只执行其中的一部分操作。 2. 一致性(Consistency):事务执行前后,数据库中的数据必须保持一致性状态,即满足数据库的约束条件和完整性规则。 3. 隔离性(Isolation):事务之间应该是相互隔离的,一个事务的执行不应该被其他事务干扰,保证事务之间的数据相互独立。 4. 持久性(Durability):事务提交后,对数据库的修改应该是永久性的,即使出现系统故障或电源故障,也不应该对数据产生影响。 总之,MySQL 事务是一组数据库操作,具有 ACID 性质,可以通过 BEGIN、COMMIT 和 ROLLBACK 语句来实现,隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值