mysql 事务

本文来源:

  https://www.modb.pro/db/42303

 

1、什么是事务?

  事务就是 一组原子性的SQL,或者说一个独立的工作单元。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。

  

  控制事务的语法:

  

1、BEGIN 或 START TRANSACTION 显式开启一个事务;
2、COMMIT 。提交事务,并使得对数据库进行的所有操作是永久性的;
3、ROLLBACK 。回滚事务,并撤销正在进行的所有未提交的修改;

4、SAVEPOINT identifier 在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
5、RELEASE SAVEPOINT identifier 删除一个事务的保存点;
6、ROLLBACK TO identifier 把事务回滚到标记点;
7、SET TRANSACTION 用来设置事务的隔离级别。
  InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE

 

2、举个例子说说你理解的事务

把转账的操作简化抽成一个事务,包含如下步骤:

  1. 查询CMBC账户的余额是否大于10万

  2. 从CMBC账户余额中减去10万

  3. 在ICBC账户余额中增加10万

1、START TRANSACTION;
2、SELECT balance FROM CMBC WHERE username='you';
3、UPDATE CMBC SET balance = balance - 100000.00 WHERE username = 'you';
4、UPDATE ICBC SET balance = balance + 100000.00 WHERE username = 'you';、
5、COMMIT;

 

 

3、什么是ACID特性?

  原子性(atomicity)

    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。

 

  一致性(consistency)

    数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,CMBC账户中也不会损失100万,不然lemon要哭死因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。

 

  隔离性(isolation)

  通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,

  当执行完第三条语句、第四条语句还未开始时,此时如果有其他人准备给你的CMBC账户存钱,那他看到的CMBC账户里还是有10万的。

  

  持久性(durability)

  一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

 

4、什么是脏读、不可重复读、幻读

    脏读

    在事务A 修改数据之后,提交数据之前,这个时候另一个事务 B 来读取数据,如果不加控制,事务 B 读取到 A 修改过的数据,之后 A 又对数据

  修改在提交,则 B 读取到的数据是脏数据。这个过程称为脏读。

    

 

    不可重复读

    一个事务内在读取某些数据后的某个时间,再次读取以前读过的数据,缺发现读出的数据已经发生了变更,或者某些记录被删除了。

    

 

    幻读

    事务A在按查询条件读取某个范围的记录时,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row)

           

 

    

5、不可重复读与幻读有什么区别?

  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改)
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除)

 

6、MySQL事务的四个隔离级别分别是?解决了什么问题

  SQL实现了四个标准的隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。

  低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

 

 

  各个隔离级别可以不同程度的解决脏读、不可重复读、幻读。隔离级别各有所长,没有完美的解决方案,要结合具体的业务场景谈具体实施。

 

7、MySQL存储引擎类型有哪些?

  最常用的存储引擎是InnoDB引擎和MyISAM存储引擎,InnoDB是MySQL的默认事务引擎。

 

 

8、InnoDB存储引擎的特点和应用场景?

  InnoDB是MySQL的默认「事务引擎」,被设置用来处理大量短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会回滚。

 

9、InnoDB与MyISAM对比

 

10、mvvc 的原理 

  

 

  我们看上面这个数据库日常操作的例子。

   1、session 1 修改一条记录,没有提交;以此同时,session 2 来查询这条记录是多少呢?

   2、session 1 提交之后 session 2 查询出来的又应该是多少呢?

  由于MySQL支持多种隔离级别,这个问题是需要看session2的事务隔离级别的,情况如下:

  1、隔离级别为 READ-UNCOMMITTED 的情况下:

     session 1 commit 前后 session 2 去查看都会看到的修改结果是 a = 20

  2、隔离级别为 READ-COMMITTED 情况下: 

     session 1 commit 前查看的数据是 a = 10, commit 之后看到的是 a = 20

  3、隔离级别为 REPEATABLE-READ, SERIALIZABLE 情况下: 

     session 1 commit前后 session 2 去查看都会看到的是修改后的结果 a = 10   

   

  问题:

  其实不管隔离级别,我们也抛开数据库中的ACID,我们思考一个问题:众所周知,InnoDB的数据都是存储在B-tree里面的,修改后的数据到底要不要存储在实际的B-tree叶子节点?session2是怎么做到查询出来的结果还是10,而不是20呢?

  MVCC实现原理

    原理很简单,数据库就是通过 undo 和 mvcc 来实现的。

    通过 DB_ROLL_PT 回溯查找数据的历史版本

      1、首先 InnoDB 每一行数据还有一个 DB_ROLL_PT 的回滚指针,用于指向该行修改前的上一个历史版本。

               

 

 

      2、当插入的是一条新数据时候,记录上对应的回滚指针为 NULL

 

            

         更新记录时候,原纪录将被放到 undo 表空间中,并通过 DB_ROLL_PT 指向该记录。

 

 

 

    https://blog.csdn.net/qq_56263094/article/details/125317049

  基本概念

    快照读  

      简单的 select (不加锁) 就是快照读,快照读取可能是最新的,也有可能是历史数据

      Read Committed : 每个 select 都会产生一个快照读

      Repeatable Read : 开启事物后第一个 select 才是快照读的地方

      Serializable : 快照读会退化为当前读(加锁)

    

     MVCC

      全称Multi-Version Concurrency Control,多版本并发控制,指维护一个数据的多个版本,使得读写操作没有冲突,也就是不会产生读写锁冲突,而快照读为MySQL实现                MVCC提供了一个非阻塞读功能,这给我们高并发性能提升不少

          MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView 
          接下来,我们再来介绍一下InnoDB引擎的表中涉及到的隐藏字段 、undolog 以及 readview,从
而来介绍一下MVCC的原理。
     

    隐藏字段

      

    举个例子当我们创建了这张表,在查看表结构的时候,只看得到这三个显示的字段,但是实际上除了这三个字段意外,InnoDB还会自动的给我们添加三个隐藏字段,如下:

      

 

    而上面前俩个字段是肯定添加的,而第三个要看是否自己设置了主键,如果没有指定主键,则会添加该字段

    

  undolog

  回滚日志,在insert,update,delete的时候产生便于数据回滚的日志

    1、在insert的时候,产生的undo log 日志旨在回滚的时候需要,在事务提交后,可被立即删除

    2、而update,delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除

  

  版本链

   

 

    DB_TRX_ID : 代表最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是自增的

    DB_ROLL_PTR : 指向上一个版本,由于这条数据是才插入的,没有被更新过,所以该字段值为null。

    

    然后,有四个并发事务同时在访问这张表【重点】

 

          第一步

 

    

 

 

    当事务2开始修改数据时候,会产生 undo log 日志,记录数据变更之前的样子,并且会记录本次操作的事务 id

 

  

 

 

    第二步

                

      当事务3执行第一条修改语句时,也会记录记录undo log日志,记录数据变更之前的样子,然后更新版本记录,并且记录本次操作事务id

      

 

      

 

     第三步

        

 

 

      当事务4执行第一条修改语句时,也会记录undo log日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID
                

 

      最终我们发现,不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录

      

    Readview

      Readview(读视图)是快照读SQL执行的时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id

      Redaview中包含了四个核心字段:

      

 

      看图有点抽象,我觉得可以这样理解

        m_ids:当前活跃的事务ID集合 ==>当前并发过程中还未提交的事务ID集合
        min_trx_id:最小活跃事务ID ==>在还未提交的事务ID集合中最小那个
        max_trx_id:预分配事务ID,当前最大事务ID+1 ==>也就是说下一个事务ID应该是当前还未提交的事务id集合中最大那个ID+1
            creator_trx_id:ReadView创建者的事务ID ==>这个好理解 就是哪个事务创建的这个ReadView那就是那个事务的ID

      而在readview中就规定了版本链数据的访问规则:

      trx_id 代表当前undolog版本链对应事务ID

      

 

      1、trx_id 可以理解为每一条匹配的 undo log 版本链的事务 ID,也就是哪个事务 ID 产生的这条记录

      2、从版本链尾部到头部开始匹配

      3、如果 trx_id == creator_trx_id 那就说明改记录是自己所在的事务更改的,是一家人。那肯定可以访问该版本

      4、如果 trx_id < min_trx_id,也就是说访问的这个事务已经提交了,因为目前还未结束的事务ID 都比它大。

      5、如果trx_id > max_trx_id,那就说明已经有事务在生成ReadView之后开启了,我们肯定不能访问该版本,防止产生重复读的问题

      6、如果min_trx_id <= trx_id <=max_trx_id,如果trx_id不在m_ids集合里面,那就说明该事务已经提交了,提交了我们肯定可以访问,如果没有提交,那就不能访问

   不同的隔离级别,生成ReadView的时机不同

      READ COMMITTED :在事务中每一次执行快照读时生成ReadView。

      REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

   原理分析

      1、READ COMMITTED 隔离级别

        READ COMMITTED 隔离级别下,在事务中每一次执行快照读时候生成 ReadView,如下图

         在分析事务5 中,两次快照读取数据,是如何获取数据的呢?

             在事务5中,查询了两次id为30的记录,由于隔离级别为Read Committed,所以每一次进行快照读都会生成一个ReadView,那么两次生成的ReadView如下:
                       

        

 

 

                  这里有图也可以好好理解一下ReadView每一个字段所代表的具体含义

     那么俩次快照读在获取数据的时候,就需要根据所生成的ReadView和ReadView的版本链访问规则,到undo log版本练中匹配数据,最终决定此次快照读使用哪一条记录的数据       

 

        A.先来看第一次快照读具体的读取过程

 

 

 

         下图是版本链和访问规则      

      在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

           1、 先匹配这条记录       

      

 

          这条记录对应的 trx_id 为4,也就是将4带入右侧的匹配规则中

              trx_id (4) == creator_trx_id (5) 不满足

             

 

         

        

 

      

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 

      

      

 

 


    

 

 

 

 

 

         

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

     

        

    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

          Read Committed : 每个 select 都会产生一个快照读

 

        Repeatable Read:开启事务后第一个select才是快照读的地方

 

        Serializable:快照读会退化为当前读(加锁) 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

       

 

 

 

 

 

 

 

 

 

 

         

 

 

 

 

      

 

 

 

 

 

https://www.bilibili.com/video/BV1YJ411J7vb/?spm_id_from=333.337.search-card.all.click&vd_source=3b043fbb3f7302824c9ad5570dccd79a

  

  https://www.bilibili.com/video/BV1te4y1o7KN/?spm_id_from=333.999.0.0&vd_source=3b043fbb3f7302824c9ad5570dccd79a

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
posted @ 2022-10-24 22:20  dogRuning  阅读(93)  评论(0编辑  收藏  举报