• mysql事务隔离级别与实现原理


    参考:https://www.cnblogs.com/crazylqy/p/7610831.html

    说明:

          (1)下文的读说的都是快照读,不是当前读,当前读需要加锁。

          快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

      在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:

       快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)

            select * from table where ?;

       当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。  

           select * from table where ? lock in share mode;

                        select * from table where ? for update;

                        insert into table values (…);

                        update table set ? where ?;

                        delete from table where ?;

    一:概论

    1. 事务里的写操作,四种隔离级别,都会加排他锁。

    2. 事务里的读操作,前三种隔离级别,不会加锁,最后一种隔离级别,会加共享锁。

    3. 上面的写、读操作,都是隐式加的锁。  可以自己显示对读操作进行加锁:SELECT ... LOCK IN SHARE MODE(对这行数据加共享锁);SELECT ... FOR UPDATE(对这行数据加排它锁)。

    4. 事务里的锁都是同一时刻释放的,就是事务提交或者回滚的那一刻。

    二:事务隔离级别

    MySQL/InnoDB定义的4种隔离级别:(mysql的级别不是ios定义的级别,iso定义的RR存在幻读,而MySQL定义的RR不存在幻读)

      脏读 不可重复读 幻读 实现方式
    读未提交 存在 存在 存在 读没有控制,写加排他锁
    读已提交 不存在 存在 存在

    快照读:读MVCC,写加排他锁

    当前读:读加行锁,写加排它锁

    可重复读 不存在 不存在 存在

    读MVCC,写加排他锁

    当前读:读加间隙锁,写加排它锁

    串行化 不存在 不存在 不存在 读加共享锁,写加排他锁

    在读未提交、读已提交、可重复读的隔离级别中:

    两个读的事务,可并发;

    一读、一写的事务,可并发;(因为只有写加锁)

    两个写的事务,不可以并发。(以内都加了排他锁)

    三:MVCC在读已提交、可重复读事务隔离级别的应用

    概念

    读锁:也叫共享锁、S锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
    写锁:又称排他锁、X锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    表锁:操作对象是数据表。Mysql大多数锁策略都支持,是系统开销最低但并发性最低的一个锁策略。事务t对整个表加读锁,则其他事务可读不可写,若加写锁,则其他事务增删改都不行。
    行级锁:操作对象是数据表中的一行,行级锁对系统开销较大,但处理高并发较好。    MVCC技术是行级锁的一种变种。

    MVCC(Mutil-Version Concurrency Control),就是多版本并发控制。一般在数据库管理系统中,通过对行的多个版本的控制,只需要很小的开销,就可以实现非锁定读,实现了读-写,写-读的并发执行,提升了系统的性能。

    原理

    隐藏的三个字段

    InnoDB的MVCC,是通过在每行纪录后面保存三个隐藏的列来实现的。这三个列,一个保存了行的创建时间,一个保存了行的过期时间(或删除时间),当然存储的并不是实际的时间值,而是系统版本号;一个保存了行的上一个版本地址。 每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行纪录的版本号进行比较。在可重复读隔离级别下,MVCC具体的操作如下:

    SELECT
    InnoDB会根据以下两个条件检查每行纪录,只有符合上述两个条件的纪录,才能作为查询结果返回。

    • InnoDB只查找版本早于当前事务版本的数据行,即,行的系统版本号小于或等于事务的系统版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

    • 行的删除版本,要么未定义,要么大于当前事务版本号。这样可以确保事务读取到的行,在事务开始之前未被删除。

    INSERT

    • InnoDB为插入的每一行保存当前系统版本号作为行版本号。

    DELETE

    • InnoDB为删除的每一行保存当前系统版本号作为行删除标识 。

    UPDATE

    • InnoDB插入一行新纪录,保存当前系统版本号作为行版本号;同时,保存当前系统版本号到原来的行作为行删除标识。

    优点:
    保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好。

    缺点:
    每行纪录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

     

    ReadView

    ReadView(读视图),ReadView中主要就是有个列表来存储我们系统中当前活跃着的事务,也就是还未提交的事务。 通过这个读视图来判断行记录的某个版本是否对当前事务可见,假设当前列表里的事务id为[80,100],具体行记录的可见性如下:

    1. 如果你要访问的行记录的版本为50,比当前列表最小的事务id80要小,说明此版本的行记录已经被提交,所以对当前事务来说是可访问的。

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

    3. 如果你要访问的行记录的版本为110,比当前列表最小的事务id100都大,说明这个版本是在ReadView生成之后才发生的,所以对当前事务来说是可访问的。

    4. 如果数据不可见,我们需要去哪里找上个版本的数据呢?

    通过行记录的上一个版本地址去undo log信息中寻找上个版本的行记录,按照上面(1)、(2)、(3)的步骤,判断此版本的行记录对当前事务是否可见,以此类推。

    具体流程如下:

     

    例子

    1.有一行记录,这行记录被3个事务更新过,分别为事务50、事务60、事务100,其中事务50和事务60已经提交,事务100未提交。 如下图所示:

     

    2.一个select事务,要查询id为1的记录,此时生成的ReadView列表只有[100]。 要访问的行记录最近的版本号是100,在ReadView列表中,所以不能访问;这时候就通过上一个版本地址找下一条,找到name值为小明1的那条记录,行版本号是60,小于ReadView列表中的最小值,可以访问,查找到小明1的那条记录。

     

     3.这时候我们把事务id为100的事务提交了,并且新建了一个id为110的事务,也修改这条行记录,并且不提交事务。如下图所示:

     

     

    4.之前那个select事务又执行了一次查询,要查询id为1的记录。

    如果你是已提交读隔离级别,这时候你会重新生成一个ReadView,那你的活动事务列表中的值就变了,变成了[110]。 你查找到就是小明2这条记录。

    如果你是可重复读隔离级别,这时候你的ReadView还是第一次select时候生成的ReadView,也就是活动事务列表中的值还是[100]。所以select的结果还是小明1这条记录。第二次select结果和第一次select结果一样,所以叫可重复读!

      也就是说已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

     

     
  • 相关阅读:
    linux 查看系统负载:uptime
    centos who命令 查看当前登录系统用户信息
    centos7 管理开机启动:systemd
    Linux ethtool 命令
    Linux ifconfig 命令
    linux centos7 目录
    POJ 1169
    POJ 1163
    POJ 1154
    POJ 1149
  • 原文地址:https://www.cnblogs.com/DengGao/p/mysql.html
Copyright © 2020-2023  润新知