当前位置:   article > 正文

MySQL 的事务_mysql事务

mysql事务

MySQL 的事务

1、事务的定义

事务是一个最小的,不可在分割的工作单元,通常一个事务对应一个完整的业务,一个完整的业务需要批量的DML(insert,update,delete)语句共同联合完成。

2、事务的ACID特性:

一致性、持久性、原子性、隔离性

  • 原子性atomicity: 当前事务的操作要么同时成功,要么同时失败。原子性由undo log日志保证。
  • 一致性consistency: 事务的最终目的,(其他三个都是为了保证一致性的)
  • 隔离性isolation:事务并发执行时,内部的操作不能互相干扰。各种锁和MVCC实现
  • 持久性durability:一旦提交了事务,他对数据库的改变就是永久性的。redo log日志保证。

3、四种事务隔离级别

innoDB引擎中定义了四种隔离级别,级别越高,事务的隔离性越好,但是性能越低。

  • read uncommit (读未提交):有脏读问题
  • read commit (读已提交):有不可重复读问题   --- oracle默认
  • repeatable read (可重复读):幻读问题 --- mysql默认
  • serializable(串行):上面的问题都可以解决,但是性能最低
  • 脏读:事务A读取到了事务B更新了但是未提交的数据,然后事务B由于某种错误发生回滚,那么事务A读取到的就是脏数据。----读取未提交的数据!
  • 不可重复读:指在数据库访问时,一个事务在前后两次相同的访问中却读到了不同的数据内容。---- 前后多次读取,数据内容不一致!
  • 幻读:指的是事务A在查询完记录总数后,事务B执行了新增数据的操作,事务A再次查询记录总数,发现两次查询的结果不一致,平白无故的多了几条记录,这种现象称为幻读。---- 前后多次读取,数据内容不一致!

不可重复读和幻读的差别在哪里呢?----- 本质是一样的,两者都表现为两次读取的结果不一致。但是不可重复读指的是两次读取同一条记录的值不同,而幻读指的是两次读取的记录数量不同。

4、mysql中的锁分类

在这里插入图片描述

MVCC (multi-version concurrency control)  多版本并发控制

1、什么是当前读和快照读?

当前读:即读取当前最新提交的数据,本质上是基于锁的并发读操作。

  • select 。。。 lock in share mode (共享锁),
  • select 。。。 for update;(排他锁)
  • update; insert; delete (排他锁)

快照读:  读取某一个快照建立时的数据(也就是某一个时间点的数据),也称作一致性读。快照读主要体现在select操作时,不同隔离级别下,行为的不同:

  • 串行级别下:普通select也会变成当前读,即加共享锁
  • RC级别下:每次select时,都会建立新的快照
  • RR级别下:
    • 事务启动后,首次select会建立快照
    • 事务启动时选择了 with consistent snapshot,事务启动时就建立快照
    • 基于旧数据的修改操作,会重新建立快照。

2、当前读,快照读,和MVCC之间的关系?

MVCC多版本并发控制是一个概念:“维持一个数据的多个版本,使得读写操作没有冲突”,快照读其实就是MVCC实现的一种方式,是一种非阻塞读。相对的,当前读就是悲观锁的一种具体实现。

3、MVCC 能解决什么问题?

数据库并发场景有三种:

读-读: 不存在任何问题,也不需要并发控制

读-写: 有线程安全问题,可能造成事务的隔离性问题,如脏读,幻读,不可重复读

写-写: 有线程安全问题,可能会存在更新丢失的问题。

MVCC带来的好处:

MVCC是一种用来解决读-写冲突的 无锁并发控制,简而言之,是因为人们不满意采用悲观锁这样性能不佳的形式去解决读-写冲突问题,而提出的解决方案。

MVCC为事务分配单向增长的时间戳,为每个修改保存一个版本,版本和事务的时间戳相关联,读操作只读该事务开始前的数据库快照,因此MVCC可以解决:

        1、并发读写数据库时,在读操作的时候不用阻塞写操作,写操作也不会阻塞读操作,提高了并发读写的性能

        2、可以解决脏读,幻读,不可重复读等事务隔离问题,但是无法解决更新丢失问题。

4、MVCC 的实现原理?

  • 3个隐式字段
  • undo日志
  • read view

5、MVCC中的隐式字段?

数据库表字段除了我们自定义的字段外,数据库还隐式的定义了DB_TRX_ID、 DB_ROLL_PTR、 DB_ROW_ID 等字段

  • DB_TRX_ID:6byte,最近修改(插入和更新)的事务的id
  • DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本
  • DB_ROW_ID 6byte,隐藏的自增ID,如果数据表没有主键,innoDB会自动以DB_ROW_ID创建一个聚簇索引。

6、MVCC中的undo log日志?

undo log日志主要分为两种:

  1. insert undo log:事务在insert新记录的时候产生的undo log, 只在事务回滚的时候需要,并且事务提交后,就被立即丢弃
  2. update undo log: 事务在update 或者 delete时产生的undo log,不仅事务回滚时需要,快照读的时候也需要,因此不能随便删除,有对应的purge线程统一清除。

对MVCC有帮助的实际是update undo log,其实就是一个版本链。不同事务或者相同事务对同一条记录的修改,会导致该记录的undo log成为一个记录版本的线性表(链表),链首的位置就是最新的旧记录(记录最近一次被修改之前的数据),链尾就是最早的旧记录。下面介绍一下流程:

1、数据库中本来存在一条记录

 2、现在开启了一个事务1,对这个记录的name进行修改操作:

  • 数据库首先对该行加一个排他锁
  • 加完锁后,先把这行数据拷贝到undo log中,作为旧记录
  • 拷贝完后,按照执行修改那么的操作,并且修改隐藏字段的事务ID(假设为1,然后递增),回滚指针则指向拷贝到undo log中的副本记录,表示上一个版本
  • 事务提交后,释放锁。

 3、现在又来了一个事务2,想修改同一行数据,将age变成30

  • 同样先加排他锁
  • 准备把这行记录拷贝到undo log中,作为旧记录,但是发现该行记录已经有undo log了,那么就把最新的旧数据作为链表的表头,插在该行记录的undo log的最前面
  • 修改该行数据,并且把隐藏的事务id设置为2,回滚指针指向刚拷贝到undo log的副本记录
  • 事务提交,释放锁。

7、MVCC中的read view 视图?

read view就是事务进行快照读操作的时候产生的一个读视图。在一个事务执行到快照读的那一个时刻,会生成数据库系统当前的一个快照,记录并且维护系统当前活跃事务的id(每个事务开启的时候都会被分配一个id,这个id是递增的,因此id越大,代表事务越新)

read view 读视图 的三个属性

  1. trx_list: 当前系统中所有活跃的事务id(开启了事务,但是还没有commit)
  2. low_limit_id:当前系统中最大的事务id然后在加1,(就是要生成的下一个事务的事务id)
  3. up_limit_id: 在生成read view时,当前系统活跃的事务中,最小的事务id(就是trx_list中的最小值。)

read view 遵循一个可见性算法:当一个事务读取某条数据的时候,会根据DB_TRX_ID(也就是undo log日志中链首的id在坐标轴上的位置来进行可见性判断):

 1、DB_TRX_ID < up_limit_id,则当前事务可以看到DB_TRX_ID的记录,否则进入下一个判断。

 2、DB_TRX_ID >= low_limit_id, 则说明DB_TRX_ID所在的记录提交是在readview生成之后,当前事务肯定是不可见的。

 3、DB_TRX_ID是否在活跃事务trx_list之中,如果在,说明readview生成时刻,DB_TRX_ID还未提交,因此 无法读到DB_TRX_ID修改的数据。如果不在,说明DB_TRX_ID在readview生产之前就已经提交了,那么修改的结果是可见的。

解释一下第3点:

当前事务2开始select的时候,几个参数的值为:

trx_list --- >  1、2、3 

low_limit_id ---> 5

up_limit_id ---> 1

DB_TRX_ID ---> 4

对应上述第3点的后半段,因此事务2是可以看到事务4修改的值

当前事务2开始select的时候,几个参数的值为:

trx_list --- >  1、2、4

low_limit_id ---> 5

up_limit_id ---> 1

DB_TRX_ID ---> 3 (假设事务3是当前时刻最后一次提交的)

对应上述第3点的前半段,因此事务2无法看到事务4修改的值

8、RC , RR 级别下的 InnoDB 快照读有什么不同

在上述介绍中,发现:事务的快照读的结果是十分依赖该事务首次出现快照读的时机,它决定了该事务后续快照读结果的能力(如事务2首次读 在事务4 提交修改之前和之后的差异)。这个其实是在mysql默认的RR级别下才有的现象。

  • 可重复读RR:某个事务对某条记录的第一次快照读会创建一个read view,这个readview会记录当前时刻其他所有活动事务的快照,此后在调用快照读的时候,还是使用同一个read view,所以对read view创建之后的修改不可见。
  • 读已提交RC:事务中,每次快照读都会新生产一个快照和一个read view,因此在RC级别下事务可以看到别的事务更新的内容。

总结: RR级别同一个事务第一次快照读才会创建read view(后续都用这个一个read view才会保证每次读的到数据都是一样的,即可重复读),而RC级别每次快照读都会创建一个readview

8、RR 级别下如果两个事务同时修改一行记录呢?

insert或者update,都会被阻塞。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/610589
推荐阅读
相关标签
  

闽ICP备14008679号