暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL-事务+MVCC

董源龙 2021-09-23
129

并发产生的问题:



脏写


脏读


不可重复读

Nonrepeatable Read


幻读

phantom Read

用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的'幻影'行


脏读eg

时间

会话A

会话B

1

Select * from test


2

1 row

A:1


3


Insert into t select 2

4

Select * from test


5

2 row

a:1

a:2


时间

会话A

会话B

1

Begin

Begin

2


查询账户余额1000

3


取出500,余额500

4

 查询账户余额500(脏读)


5


撤销事务,余额1000

6

转入100,余额600


7

提交事务



不可重复读

时间

会话A

会话B

1

Begin

Begin

2

Select * from test


3

1 row

A:1


4


Insert into t select 2

5


Commit

6

Select * from test


7

2 row

a:1

a:2



幻读

时间

会话A

会话B

1


开始事务

2

开始事务


3

统计总存款10000


4


新增一个存款账户,存款100

5


提交事务

6

再次统计总存款数10100(幻读)




事务隔离级别

事务隔离级别

脏读

不可重复读

幻读

第一类丢失更新
第二类丢失更新

读未提交

read uncommited

读已提交(RC)

read committed

可重复读(RR)

repeatable read

串行化-serializable






读未提交

read uncommited

所有事务都可以看到其他未提交事务的执行结果

 很少用于实际应用

性能不比其他级别好多少

读取未提交数据-脏读-Dirty Read

读已提交(RC)

read committed

一个事务提交之后,才会被其他事务看到

解决脏读,不可重复读

大多数数据库系统默认隔离级别

满足隔离简单定义

一个事务只能看见已经提高事务所做的改变

同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果

一个事务可以读取到另一个尚未提交事务的修改数据

可重复读(RR)

repeatable read

MySQL的默认事务隔离级别

确保同一事务多个实例在并发读取数据时,看到同样的数据行。

解决脏写、脏读、不可重复读

基于undo log多版本链条 + ReadView机制

解决多个事务读写的时候避免频繁加互斥锁的影响

串行化-serializable

最高的隔离级别

通过强制事务排序,使之不可能相互冲突,解决幻读问题


每个读数据行上加上共享锁

可能导致大量的超时现象和锁竞争


查看事务隔离级别

    show variables  like 'tx_isolation';
    复制



    设置隔离级别

      SET [SESSION|GLOBALTRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
      复制
        SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
        复制



        隔离性底层实现

            锁+MVCC

                在使用 READ COMMITTD、REPEATABLE READ 这两种隔离级别的事务下,每条记录在更新的时候都会同时记录一条回滚操作,就会形成一个版本链,在执行普通的SELECT 操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。READ COMMITTD、REPEATABLE READ这两个隔离级别的一个很大不同就是生成ReadView的时机不同,READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复这个ReadView就好了。



            READ COMMITTD隔离级别下导致不可重复读的根本原因

                READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView

                REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView



        多版本并发控制(MVCC),是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务戳关联,读操作只读事务开始前的数据库的快照。这样在读操作的时候不会阻塞写操作,写操作不会阻塞读操作的同时,也避免了脏读和不可重复读。

        MVCC,多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。

        关键词:事务版本号、表的隐藏列(事务版本号,undo log链)、ReadView

        当前读

        快照读


        实现原理

            MVCC就是多版本并发控制,用来解决读写冲突。准确的说,MVCC主要基于"维护一条数据的多个版本,进而保证在读操作的同时不会阻塞写操作,写操作的同时也不会阻塞读操作"。在mysql中的具体实现主要是由隐藏字段,undolog,readview等去完成的,具体实现逻辑看下面的MVCC实现原理。

                roll_pointer:回滚指针,指向这条记录的上一个版本

                trx_id:事务ID,记录创建/修改这条记录的事务ID,用于版本比较,从而找到快照

        ReadView

            RR隔离级别

                当事务第一次执行查询sql时会生成一致性视图ReadView,它由执行查询时所有未提交事务ID数组[未提交事务min_trx_id,未提交事务max_trx_id]组成,查询的数据结果需要跟ReadView做比较从而得到快照结果。

            版本链比对规则

                如果trx_id < min_id,表示这个版本是已提交的事物生成的,这个数据是可见的

                如果trx_id > max,表示这个版本是由将来启动的事务生成的,是肯定不可见的

                如果min_id <= trx_id<= max_id

                    若row的trx_id在数组中

                        表示这个版本是由还没提交的事务生成的,不可见,当前自己的事务是可见的

                    若row的trx_id不在数组中

                        表示这个版本是已经提交的事务生成的,是可见的


        QA

        MVCC是否解决了幻读


        幻读是由MCCC多版本并发控制机制实现


        InnoB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control 间隙锁)机制解决幻读问题

            多版本只是解决不可重复读问题,而加上间隙锁(并发控制)解决幻读问题

            同一事务中,同一查询多次进行时候,由于其他插入操作的事务提交,导致每次返回的结果集


        文章转载自董源龙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论