Mysql 事务隔离级别

一、数据库事务正确的四个要素(ACID)[1]

  原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分;

  一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;比如我们做银行转账的相关业务,A转账给B,要求A转的钱B一定要收到。如果A转了钱而B没有收到,那

么数据库数据的一致性就得不到保障,在做高并发业务时要注意合理的设计。

  隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;

  持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因各种异常导致数据不一致或丢失。

 

二、并行事务的四个问题

  先理解锁的概念:https://www.cnblogs.com/Jomini/p/13219722.html

2.1、丢失更新

         别的事务读到相同的东西,各自写,自己的写被覆盖了。

         两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,后果比较严重。一般是由于没加锁的原因造成的。

        解决方式 :使用数据库锁机制

      (详细: https://www.cnblogs.com/doucheyard/p/5662171.html )

 

2.2、脏读(Dirty reads)

        读到别的事务未提交的数据

        一个事务A读取到了另一个事务B还没有提交的数据,并在此基础上进行操作。如果B事务rollback,那么A事务所读取到的数据就是不正确的,会带来问题。

 

2.3、不可重复读(Non-repeatable reads)

          两次读之间有别的事务修改

         在同一事务范围内读取两次相同的数据,所返回的结果不同。比如事务B第一次读数据后,事务A更新数据并commit,那么事务B第二次读取的数据就与第一次是不一样的。

 

2.4、 幻读(Phantom reads)

         两次读之间有别的事务增删

         一个事务A读取到了另一个事务B新提交的数据。比如,事务A对一个表中所有行的数据按照某规则进行修改(整表操作),同时,事务B向表中插入了一行原始数据,那么后面事务A再对表进行操作时,会发现表中居然还有一行数据没有被修改。

 

2.5、注意:不可重复读和幻读的区别是,不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样。对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据。有关锁的问题,下面会讨论。

 

三、解决方式

       SQL定义了下面的4个等级的事务隔离级别:

3.1、未提交读(READ UNCOMMITTED )

          最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;

          原理:

        (1)事务对当前被读取的数据不加锁;

                 事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,能读到事务2对该记录的修改版本,即使该修改尚未被提交。

        (2)事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。

                  事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。

3.2、提交读(READ COMMITTED)

         一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现 丢失更新、脏读,但可能出现不可重复读、幻读;

         原理:

         (1)事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;

            【注意是:一旦读完该行,立即释放该共享锁(注意是读完立即释放),所以第二次读的数据将会不同,即导致 “不可重复读” 的问题】

                事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的只能是事务2对其更新前的版本,要不就是事务2提交后的版本。

         (2)事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。

                  事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。

3.3、可重复读(REPEATABLE READ)

         保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;

           原理: 

           (1) 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;

                 事务1读取某行记录时,事务2能对该表读取,但不能修改,事务1再次读取该记录,读到的仍然是第一次读取的那个版本。

            【注意:和 “提交读” 的读操作不同的是,这里读加的共享锁 是整个事务都加了,而不是读完就解锁;】

           (2) 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放

                  事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。

             问题:因为事务1读取和修改的的时候只是加了行级别锁, 所以其他事务可以对该表插入数据,会导致幻读 

    MVCC(多版本并发控制)  https://www.cnblogs.com/lmj612/p/10598971.html

                               

    InnoDB在每行记录后面保存两个隐藏的列来,分别保存了这个行的创建时间和行的删除时间。

    这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1

    在读取事务开始时,系统会给当前读事务一个版本号,事务会读取版本号<=当前版本号的数据

    此时如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然而然的就读不到更新后的数据了

3.4、序列化(SERIALIZABLE)

  MVCC(多版本并发控制) + next-key locking https://blog.csdn.net/qq_35590091/article/details/107734005

 

        最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。

       隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。 

         原理:

       (1)事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;

                 事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。

       (2)事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。

                事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。

posted @ 2020-11-21 17:26  抽象Java  阅读(159)  评论(0编辑  收藏  举报