精华内容
下载资源
问答
  • 主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 我是李福春,今天的问题是,mysql是如何实现事务隔离的?在实际开发中应该如何正确的使用事务? ACID特性 事务首先具备ACID特性,即 Atomic原子性, Consistency一致性 Isolation隔离性 durability持久性; 事务...
  • 数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏...
  • MySQL 四种事务隔离级别详解及对比 按照SQL:1992 事务隔离级别,InnoDB默认是可重复读的(REPEATABLE READ)。MySQL/InnoDB 提供SQL标准所描述的所有四个事务隔离级别。你可以在命令行用–transaction-isolation选项...
  • 然后说说修改事务隔离级别的方法: 1.全局修改,修改mysql.ini配置文件,在最后加上 代码如下: #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE. [mysqld] transaction-...
  • 为了保证事务操作的原子性,必须实现基于日志的REDO/UNDO机制:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯...

    一、ACID特性

    持久性,我们就不讲了,易懂。

    1、原子性

    在同一个事务内部的一组操作必须全部执行成功(或者全部失败)。

    为了保证事务操作的原子性,必须实现基于日志的REDO/UNDO机制:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到“全部操作失败”的目的。 最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个crash recovery的过程:读取日志进行REDO(重演将所有已经执行成功但尚未写入到磁盘的操作,保证持久性),再对所有到崩溃时尚未成功提交的事务进行UNDO(撤销所有执行了一部分但尚未提交的操作,保证原子性)。crash recovery结束后,数据库恢复到一致性状态,可以继续被使用。

    某个应用在执行转帐的数据库操作时,必须在同一个事务内部调用对帐户A和帐户B的操作,才能保证数据的一致性。

    但是,原子性并不能完全保证一致性。在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。 例如,事务1需要将100元转入帐号A:先读取帐号A的值,然后在这个值上加上100。但是,在这两个操作之间,另一个事务2修改了帐号A的值,为它增加了100元。那么最后的结果应该是A增加了200元。但事实上,事务1最终完成后,帐号A只增加了100元,因为事务2的修改结果被事务1覆盖掉了。

    简而言之,就是:原子性仅能够保证单个事务的一致性。就像redis一样,也只能保证单操作的线程安全,并不能保证多操作下的线程安全。

    2、一致性

    按照我个人的理解,在事务处理的ACID属性中,一致性是最基本的属性,其它的三个属性都为了保证一致性而存在的。

    我们举个反例来理解下一致性概念。例如:从帐户A转一笔钱到帐户B上,如果帐户A上的钱减少了,而帐户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。

    为了保证并发情况下的一致性,引入了隔离性,即保证每一个事务能够看到的数据总是一致的,就好象其它并发事务并不存在一样。

    3、隔离性

    数据库四种隔离级别,以及常见的几种读异常,大家应该都是耳熟能详的,但数据库底层是怎么实现隔离性的呢?都采用了哪些技术呢? 主要有两个技术:MVCC(多版本并发控制)和锁。

    (1)MVCC(多版本并发控制)

    多版本并发控制,顾名思义,在并发访问的时候,数据存在版本的概念,可以有效地提升数据库并发能力,常见的数据库如MySQL、MS SQL Server、IBM DB2、Hbase、MongoDB等等都在使用。

    简单讲,如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据的历史版本,从而避免了互斥等待。

    InnoDB采用的MVCC实现方式是:在需要时,通过undo日志构造出历史版本。

    (2)锁

    1) 锁的分类

    • Shared Locks(共享锁/S锁)

    若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

    • Exclusive Locks(排它锁/X锁)

    若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。

    注意:排他锁会阻止其它事务再对其锁定的数据加读或写的锁,但是不加锁的就没办法控制了。

    • Record Locks(行锁)

    行锁,顾名思义,是加在索引行(对!是索引行!不是数据行!)上的锁。比如select * from user where id=1 and id=10 for update,就会在id=1id=10的索引行上加Record Lock。

    • Gap Locks(间隙锁)

    间隙锁,它会锁住两个索引之间的区域。比如select * from user where id>1 and id<10 for update,就会在id为(1,10)的索引区间上加Gap Lock。

    • Next-Key Locks(间隙锁)

    也叫间隙锁,它是Record Lock + Gap Lock形成的一个闭区间锁。比如select * from user where id>=1 and id<=10 for update,就会在id为[1,10]的索引闭区间上加Next-Key Lock。

    这样组合起来就有,行级共享锁,表级共享锁,行级排它锁,表级排它锁。

    2) 什么时候会加锁?

    在数据库增删改查四种操作中,insert、delete和update都是会加排它锁(Exclusive Locks)的,而select只有显式声明才会加锁:

    • select: 即最常用的查询,是不加任何锁的
    • select ... lock in share mode: 会加共享锁(Shared Locks)
    • select ... for update: 会加排它锁

    3) 四种隔离级别

    不同的隔离级别是在数据可靠性和并发性之间的均衡取舍,隔离级别越高,对应的并发性能越差,数据越安全可靠。

    • READ UNCOMMITTED

    顾名思义,事务之间可以读取彼此未提交的数据。机智如你会记得,在前文有说到所有写操作都会加排它锁,那还怎么读未提交呢?

    机智如你,前面我们介绍排它锁的时候,有这种说明: 排他锁会阻止其它事务再对其锁定的数据加读或写的锁,但是对不加锁的读就不起作用了。

    READ UNCOMMITTED隔离级别下, 读不会加任何锁。而写会加排他锁,并到事务结束之后释放。

    实例1:

    查-写:查并没有阻止写,表明查肯定并没有加锁,要不写肯定就阻塞了。写很明显,会加排它锁的。

    实例2: 写-写:阻塞,表明,写会加排它锁。

    • READ COMMITTED

    顾名思义,事务之间可以读取彼此已提交的数据。

    InnoDB在该隔离级别(READ COMMITTED)写数据时,使用排它锁, 读取数据不加锁而是使用了MVCC机制。

    因此,在读已提交的级别下,都会通过MVCC获取当前数据的最新快照,不加任何锁,也无视任何锁(因为历史数据是构造出来的,身上不可能有锁)。

    但是,该级别下还是遗留了不可重复读和幻读问题: MVCC版本的生成时机: 是每次select时。这就意味着,如果我们在事务A中执行多次的select,在每次select之间有其他事务更新了我们读取的数据并提交了,那就出现了不可重复读,即:重复读时,会出现数据不一致问题,后面我们会讲解超支现象,就是这种引起的。

    • REPEATABLE READ

    READ COMMITTED级别不同的是MVCC版本的生成时机,即:一次事务中只在第一次select时生成版本,后续的查询都是在这个版本上进行,从而实现了可重复读

    但是因为MVCC的快照只对读操作有效,对写操作无效,举例说明会更清晰一点: 事务A依次执行如下3条sql,事务B在语句1和2之间,插入10条age=20的记录,事务A就幻读了。

    1. select count(1) from user where age=20;
    -- return 0: 当前没有age=20的
    2. update user set name=test where age=20;
    -- Affects 10 rows: 因为事务B刚写入10条age=20的记录,而写操作是不受MVCC影响,能看到最新数据的,所以更新成功,而一旦操作成功,这些被操作的数据就会对当前事务可见
    3. select count(1) from user where age=20;
    -- return 10: 出现幻读
    

    REPEATABLE READ级别,可以防止大部分的幻读,但像前边举例读-写-读的情况,使用不加锁的select依然会幻读。

    • SERIALISABLE

    大杀器,该级别下,会自动将所有普通select转化为select ... lock in share mode执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低。

    读-写,写-写均互斥。

    4)总结:几类读异常

    读-写-读,引起的异常

    • 脏读:读取了脏数据(不存在的数据)。 事务一读 事务二写(未提交) 事务二读(脏数据) 事务二回滚

    • 不可重复读:既可以读取修改的数据,也可以读取新增的数据(幻读)。 事务一读 事务二写(更新已提交) 事务二读(数据不一致,不可重复读)

    • 幻读:仅可以读取新增的数据,但是无法读取修改的数据; 事务一读 事务二写(新增已提交) 事务二读(数据不一致,幻读)

    • 附命令

    查看表的加锁情况: select * from information_schema.INNODB_LOCKS; 事务状态 select * from information_schema.INNODB_TRX;

    展开全文
  • 作者:伞U ...好久没碰数据库了,只是想起自己当时在搞数据库的...为了说明问题,我们打开两个控制台分别进行登录来模拟两个用户(暂且成为用户 A 和用户 B 吧),并设置当前 MySQL 会话的事务隔离级别。 一. read unco
  • 事务隔离 查询:默认事务隔离级别 mysql> select @@tx_isolation;当前会话的默认事务隔离级别 mysql> select @@session.tx_isolation;当前会话的默认事务隔离级别 mysql> select @@global.tx_isolation;全局的事务...
  • 今天就复盘一下单机事务隔离性是如何实现的? 隔离的本质就是控制并发,如果SQL语句就是串行执行的。那么数据库的四大特性中就不会有隔离性这个概念了,也就不会有脏读,不可重复读,幻读等各种问题了 对数据库的...

    请添加图片描述

    并发场景

    最近做了一些分布式事务的项目,对事务的隔离性有了更深的认识,后续写文章聊分布式事务。今天就复盘一下单机事务的隔离性是如何实现的?

    隔离的本质就是控制并发,如果SQL语句就是串行执行的。那么数据库的四大特性中就不会有隔离性这个概念了,也就不会有脏读,不可重复读,幻读等各种问题了

    对数据库的各种并发操作,只有如下四种,写写,读读,读写和写读

    写-写

    事务A更新一条记录的时候,事务B能同时更新同一条记录吗?

    答案肯定是不能的,不然就会造成脏写问题,那如何避免脏写呢?答案就是加锁

    读-读

    MySQL读操作默认情况下不会加锁,所以可以并行的读

    读-写 和 写-读

    基于各种场景对并发操作容忍程度不同,MySQL就搞了个隔离性的概念。你自己根据业务场景选择隔离级别。

    √ 为会发生,×为不会发生

    隔离级别脏读不可重复读幻读
    read uncommitted(未提交读)
    read committed(提交读)×
    repeatable read(可重复读)××
    serializable (可串行化)×××

    所以你看,MySQL是通过锁和隔离级别对MySQL进行并发控制的

    MySQL中的锁

    行级锁

    InnoDB存储引擎中有如下两种类型的行级锁

    1. 共享锁(Shared Lock,简称S锁),在事务需要读取一条记录时,需要先获取改记录的S锁
    2. 排他锁(Exclusive Lock,简称X锁),在事务要改动一条记录时,需要先获取该记录的X锁

    如果事务T1获取了一条记录的S锁之后,事务T2也要访问这条记录。如果事务T2想再获取这个记录的S锁,可以成功,这种情况称为锁兼容,如果事务T2想再获取这个记录的X锁,那么此操作会被阻塞,直到事务T1提交之后将S锁释放掉

    如果事务T1获取了一条记录的X锁之后,那么不管事务T2接着想获取该记录的S锁还是X锁都会被阻塞,直到事务1提交,这种情况称为锁不兼容。

    多个事务可以同时读取记录,即共享锁之间不互斥,但共享锁会阻塞排他锁。排他锁之间互斥

    S锁和X锁之间的兼容关系如下

    兼容性X锁S锁
    X锁互斥互斥
    S锁互斥兼容

    update,delete,insert 都会自动给涉及到的数据加上排他锁,select 语句默认不会加任何锁

    那什么情况下会对读操作加锁呢?

    1. select … lock in share mode,对读取的记录加S锁
    2. select … for update ,对读取的记录加X锁
    3. 在事务中读取记录,对读取的记录加S锁
    4. 事务隔离级别在 SERIALIZABLE 下,对读取的记录加S锁

    InnoDB中有如下三种锁

    1. Record Lock:对单个记录加锁
    2. Gap Lock:间隙锁,锁住记录前面的间隙,不允许插入记录
    3. Next-key Lock:同时锁住数据和数据前面的间隙,即数据和数据前面的间隙都不允许插入记录

    写个Demo演示一下

    CREATE TABLE `girl` (
      `id` int(11) NOT NULL,
      `name` varchar(255),
      `age` int(11),
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into girl values
    (1, '西施', 20),
    (5, '王昭君', 23),
    (8, '貂蝉', 25),
    (10, '杨玉环', 26),
    (12, '陈圆圆', 20);
    

    Record Lock

    对单个记录加锁

    如把id值为8的数据加一个Record Lock,示意图如下
    在这里插入图片描述
    Record Lock也是有S锁和X锁之分的,兼容性和之前描述的一样。

    SQL执行加什么样的锁受很多条件的制约,比如事务的隔离级别,执行时使用的索引(如,聚集索引,非聚集索引等),因此就不详细分析了,举几个简单的例子。

    -- READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ 利用主键进行等值查询
    -- 对id=8的记录加S型Record Lock
    select * from girl where id = 8 lock in share mode;
    
    -- READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ 利用主键进行等值查询
    -- 对id=8的记录加X型Record Lock
    select * from girl where id = 8 for update;
    

    Gap Lock

    锁住记录前面的间隙,不允许插入记录

    MySQL在可重复读隔离级别下可以通过MVCC和加锁来解决幻读问题

    当前读:加锁
    快照读:MVCC

    但是该如何加锁呢?因为第一次执行读取操作的时候,这些幻影记录并不存在,我们没有办法加Record Lock,此时可以通过加Gap Lock解决,即对间隙加锁。
    在这里插入图片描述
    如一个事务对id=8的记录加间隙锁,则意味着不允许别的事务在id=8的记录前面的间隙插入新记录,即id值在(5, 8)这个区间内的记录是不允许立即插入的。直到加间隙锁的事务提交后,id值在(5, 8)这个区间中的记录才可以被提交

    我们来看如下一个SQL的加锁过程

    -- REPEATABLE READ 利用主键进行等值查询
    -- 但是主键值并不存在
    -- 对id=8的聚集索引记录加Gap Lock
    SELECT * FROM girl WHERE id = 7 LOCK IN SHARE MODE;
    

    由于id=7的记录不存在,为了禁止幻读现象(避免在同一事务下执行相同的语句得到的结果集中有id=7的记录),所以在当前事务提交前我们要预防别的事务插入id=7的记录,此时在id=8的记录上加一个Gap Lock即可,即不允许别的事务插入id值在(5, 8)这个区间的新记录

    在这里插入图片描述
    给大家提一个问题,Gap Lock只能锁定记录前面的间隙,那么最后一条记录后面的间隙该怎么锁定?

    其实mysql数据是存在页中的,每个页有2个伪记录

    1. Infimum记录,表示该页面中最小的记录
    2. upremum记录,表示该页面中最大的记录

    为了防止其它事务插入id值在(12, +∞)这个区间的记录,我们可以给id=12记录所在页面的Supremum记录加上一个gap锁,此时就可以阻止其他事务插入id值在(12, +∞)这个区间的新记录

    Next-key Lock

    同时锁住数据和数据前面的间隙,即数据和数据前面的间隙都不允许插入记录
    所以你可以这样理解Next-key Lock=Record Lock+Gap Lock
    在这里插入图片描述

    -- REPEATABLE READ 利用主键进行范围查询
    -- 对id=8的聚集索引记录加S型Record Lock
    -- 对id>8的所有聚集索引记录加S型Next-key Lock(包括Supremum伪记录)
    SELECT * FROM girl WHERE id >= 8 LOCK IN SHARE MODE;
    

    因为要解决幻读的问题,所以需要禁别的事务插入id>=8的记录,所以

    • 对id=8的聚集索引记录加S型Record Lock
    • 对id>8的所有聚集索引记录加S型Next-key Lock(包括Supremum伪记录)

    表级锁

    表锁也有S锁和X锁之分

    在对某个表执行select,insert,update,delete语句时,innodb存储引擎是不会为这个表添加表级别的S锁或者X锁。

    在对表执行一些诸如ALTER TABLE,DROP TABLE这类的DDL语句时,会对这个表加X锁,因此其他事务对这个表执行诸如SELECT INSERT UPDATE DELETE的语句会发生阻塞

    在系统变量autocommit=0,innodb_table_locks = 1时,手动获取InnoDB存储引擎提供的表t的S锁或者X锁,可以这么写

    对表t加表级别的S锁

    lock tables t read
    

    对表t加表级别的X锁

    lock tables t write
    

    如果一个事务给表加了S锁,那么

    • 别的事务可以继续获得该表的S锁
    • 别的事务可以继续获得表中某些记录的S锁
    • 别的事务不可以继续获得该表的X锁
    • 别的事务不可以继续获得表中某些记录的X锁

    如果一个事务给表加了X锁,那么

    • 别的事务不可以继续获得该表的S锁
    • 别的事务不可以继续获得表中某些记录的S锁
    • 别的事务不可以继续获得该表的X锁
    • 别的事务不可以继续获得表中某些记录的X锁

    所以修改线上的表时一定要小心,因为会使大量事务阻塞,目前有很多成熟的修改线上表的方法,不再赘述

    隔离级别

    读未提交:每次读取最新的记录,没有做特殊处理
    串行化:事务串行执行,不会产生并发

    所以我们重点关注读已提交可重复读的隔离实现!

    这两种隔离级别是通过MVCC(多版本并发控制)来实现的,本质就是MySQL通过undolog存储了多个版本的历史数据,根据规则读取某一历史版本的数据,这样就可以在无锁的情况下实现读写并行,提高数据库性能

    那么undolog是如何存储修改前的记录?

    对于使用InnoDB存储引擎的表来说,聚集索引记录中都包含下面2个必要的隐藏列

    trx_id:一个事务每次对某条聚集索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列

    roll_pointer:每次对某条聚集索引记录进行改动时,都会把旧的版本写入undo日志中。这个隐藏列就相当于一个指针,通过他找到该记录修改前的信息

    如果一个记录的name从貂蝉被依次改为王昭君,西施,会有如下的记录,多个记录构成了一个版本链

    在这里插入图片描述

    为了判断版本链中哪个版本对当前事务是可见的,MySQL设计出了ReadView的概念。4个重要的内容如下

    m_ids:在生成ReadView时,当前系统中活跃的事务id列表
    min_trx_id:在生成ReadView时,当前系统中活跃的最小的事务id,也就是m_ids中的最小值
    max_trx_id:在生成ReadView时,系统应该分配给下一个事务的事务id值
    creator_trx_id:生成该ReadView的事务的事务id

    当对表中的记录进行改动时,执行insert,delete,update这些语句时,才会为事务分配唯一的事务id,否则一个事务的事务id值默认为0。

    max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比如现在有事务id为1,2,3这三个事务,之后事务id为3的事务提交了,当有一个新的事务生成ReadView时,m_ids的值就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4

    请添加图片描述
    执行过程如下:

    1. 如果被访问版本的trx_id=creator_id,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问
    2. 如果被访问版本的trx_id<min_trx_id,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问
    3. 被访问版本的trx_id>=max_trx_id,表明生成该版本的事务在当前事务生成ReadView后才开启,该版本不可以被当前事务访问
    4. 被访问版本的trx_id是否在m_ids列表中
      4.1 是,创建ReadView时,该版本还是活跃的,该版本不可以被访问。顺着版本链找下一个版本的数据,继续执行上面的步骤判断可见性,如果最后一个版本还不可见,意味着记录对当前事务完全不可见
      4.2 否,创建ReadView时,生成该版本的事务已经被提交,该版本可以被访问

    好了,我们知道了版本可见性的获取规则,那么是怎么实现读已提交和可重复读的呢?

    其实很简单,就是生成ReadView的时机不同

    举个例子,先建立如下表

    CREATE TABLE `girl` (
      `id` int(11) NOT NULL,
      `name` varchar(255),
      `age` int(11),
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    Read Committed

    Read Committed(读已提交),每次读取数据前都生成一个ReadView

    在这里插入图片描述
    下面是3个事务执行的过程,一行代表一个时间点
    在这里插入图片描述
    先分析一下5这个时间点select的执行过程

    1. 系统中有两个事务id分别为100,200的事务正在执行
    2. 执行select语句时生成一个ReadView,mids=[100,200],min_trx_id=100,max_trx_id=201,creator_trx_id=0(select这个事务没有执行更改操作,事务id默认为0)
    3. 最新版本的name列为西施,该版本trx_id值为100,在mids列表中,不符合可见性要求,根据roll_pointer跳到下一个版本
    4. 下一个版本的name列王昭君,该版本的trx_id值为100,也在mids列表内,因此也不符合要求,继续跳到下一个版本
    5. 下一个版本的name列为貂蝉,该版本的trx_id值为10,小于min_trx_id,因此最后返回的name值为貂蝉

    在这里插入图片描述

    再分析一下8这个时间点select的执行过程

    1. 系统中有一个事务id为200的事务正在执行(事务id为100的事务已经提交)
    2. 执行select语句时生成一个ReadView,mids=[200],min_trx_id=200,max_trx_id=201,creator_trx_id=0
    3. 最新版本的name列为杨玉环,该版本trx_id值为200,在mids列表中,不符合可见性要求,根据roll_pointer跳到下一个版本
    4. 下一个版本的name列为西施,该版本的trx_id值为100,小于min_trx_id,因此最后返回的name值为西施

    当事务id为200的事务提交时,查询得到的name列为杨玉环。

    Repeatable Read

    Repeatable Read(可重复读),在第一次读取数据时生成一个ReadView
    在这里插入图片描述
    可重复读因为只在第一次读取数据的时候生成ReadView,所以每次读到的是相同的版本,即name值一直为貂蝉,具体的过程上面已经演示了两遍了,我这里就不重复演示了,相信你一定会自己分析了。

    参考博客

    [1]https://souche.yuque.com/bggh1p/8961260/gyzlaf
    [2]https://zhuanlan.zhihu.com/p/35477890

    展开全文
  • 前言 ...我们都知道事务的几种性质,数据库中的一致性和隔离性等是实现事务的基本思想,在系统有大量的并发访问的情况下,了解和熟练应用数据库的本身的事务隔离级别,对于写出健壮性,并发处理能力强
  • 主要介绍了Mysql事务隔离级别原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 数据库隔离级别有四种,应用《高性能mysql》一书中的说明:然后说说修改事务隔离级别的方法:1.全局修改,修改mysql.ini配置文件,在最后加上#可选参数有:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,...
  • 查看事务隔离级别 在 MySQL 中,可以通过show variables like ‘%tx_isolation%’或select @@tx_isolation;语句来查看当前事务隔离级别。 查看当前事务隔离级别的 SQL 语句和运行结果如下: mysql> show variables ...
  • 事务隔离的时候,事务T启动的时候会创建一个视图read-view,之后事务T执行期间,即使有其他事务修改了数据,事务T还是看到和启动时一样的。 在事务分析的过程中我们需要注意事务的起点。 begin/start transaction ...

    引言

    在事务隔离的时候,事务T启动的时候会创建一个视图read-view,之后事务T执行期间,即使有其他事务修改了数据,事务T还是看到和启动时一样的。

    在事务分析的过程中我们需要注意事务的起点。
    begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。
    一般默认autocommit=1

    那事务隔离具体怎么操作?视图是啥?

    一、视图

    MYSQL里有两个视图的概念:
    1、一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
    2、另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。

    二、“快照”

    在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库的。

    实际上并不是说要把整个数据库都拷贝一遍。而是主要记录事务的id。
    在InnoDB中里面的每一个事务都有一个唯一id 叫做transaction id。
    事务开始的时候像InnoDB申请的。并且有严格的递增顺序。

    因为每行数据会有多个版本,每一次事务更新数据的时候都会生成一个新的数据版本,并且吧自己的事务id transaction id 赋值给这个数据版本你的事务ID。

    这个属性记录为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。

    在这里插入图片描述
    图中的虚线其实是undo log。

    怎么实现?

    在可重复读中,以事务自己的启动时刻为准,如果一个数据版本是在我启动之前生成的,就可以看见,承认。如果是我启动以后才生成的,我就不认,我必须找到它的上一个版本。

    在这个过程中,InnoDB为每一个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃指的是”,事务启动但是还没有提交。

    数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。

    这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。
    在这里插入图片描述
    这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:
    如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
    如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
    如果落在黄色部分,那就包括两种情况
    a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。

    具体例子

    现在有事务ABC
    1.在事务A开始前,系统里面只有一个活跃事务ID是99;
    2.在事务A、B、C的ID为100、101、102;
    3.三个事务开始前,(1,1)这一行数据的row trx_id是90

    这样,事务 A 的视图数组就是[99,100], 事务 B 的视图数组是[99,100,101], 事务 C 的视图数组是[99,100,101,102]。
    目前有以下逻辑关系
    在这里插入图片描述
    第一次修改数据的是C把数据从 (1,1) 改成了 (1,2)。这时候,这个数据的最新版本的 row trx_id 是 102,而 90 这个版本已经成为了历史版本。
    第二个有效更新是事务 B,把数据从 (1,2) 改成了 (1,3)。这时候,这个数据的最新版本(即 row trx_id)是 101,而 102 又成为了历史版本。
    但A事务去查看的时候,就会在历史版本中一个个看,第一个看到101的,不在可见视图中,就是大于100,不看,第二个102,大于100,不看。再来看到90,比低水位小,可看。
    这样事务A不论什么时候查询,看到的行数据的结果都是一致的,所以叫做一致性读

    再看更新逻辑

    更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。

    除了 update 语句外,select 语句如果加锁,也是当前读。

    如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以读到版本号是 101 的数据,返回的 k 的值是 3。下面这两个 select 语句,就是分别加了读锁(S 锁,共享锁)和写锁(X 锁,排他锁)。

    展开全文
  • 之前在网上查询mysql事务隔离相关资料,一直只是脑子里有一个印象,久而久之还是会忘记,忘记后又要到网上查找相关资料,但是没实践过就对mysql事务隔离级别理解不是特别的深入,现在自己亲手实践体验一下这个这四个...
  • 查询mysql事务隔离级别

    千次阅读 2019-10-11 16:37:59
    3、事务隔离级别越高,越能保证数据的完整性,但是对并发性能影响大,就比如隔离级别为串行化时,读取数据会锁住整张表。 4、不可重复读和幻读很相似,两者整体上都是两次读的数据不相同,但是不可重复读一般时更新...
    转载链接: https://blog.csdn.net/zhuoya_/article/details/80299250

    点击>>MySQL下的存储引擎查看MySQL下的几种存储引擎。在几种存储引擎中,可以看到只有InnoDB存储引擎支持事务,那么本篇就来介绍InnoDB下支持的事务。

    本篇目录

    **************************************************************

    *     事务是什么?                                                                   *

    *     事务遵循的四个特性  (AICD)                                        *

    *     事务中的基本操作                                                            *

    *     事务的四中隔离级别                                                         *

    *     通过设置隔离级别来保证事务的隔离性基本操作                *

    **************************************************************

    一、事务(transaction)

    事务指的就是一种SQL语句的集合。

    二、事务的四个特性


    事务的原子性通过Undo log来保证。(Undo日志记录数据修改前的状态)

    事务的持久性通过Redo log来保证。(Redo日志记录某数据块被修改后的值)

    事务的隔离性通过设置隔离级别来保证。

    事务的一直性通过Redo log来保证。

    三、事务中的基本操作

    (1)回滚:撤销指定SQL语句的功能。

    mysql>rollback;

    (2)提交:将未存储的SQL语句结果写到数据库表中。

    mysql>commit;

    (3)保留点:事务处理中设置的临时占位符。

    mysql>savepoint   XXX;

    注意点:

    1、不能回退select、create和drop语句。

    2、事务提交是默认提交。每执行一条语句就把该语句当成一个事务进行提交。

    3、当出现start transaction;语句时,隐式提交会关闭。

    4、当rollback或者commit语句出现,事务自动关闭,隐式提交自动恢复。

    5、通过设置autocommit=0可以取消自动提交,直到看到autocommit=1才会提交。

    6、如果没有设置保留点,rollback会回滚到start transaction处;如果设置了,并且在rollback指定了该保留点,则回滚到保留点(rollback to XXX)。

    四、事务的四中隔离级别

    在MySQL中有四中隔离级别。每一种隔离级别的作用也不同,下面通过一张表来看一下:

    事务的隔离级别 /  问题脏读不可重复读幻读
    未提交读(read uncommitted)没有避免没有避免没有避免
    已提交读(read committed)已避免没有避免没有避免
    可重复读(repeatable read)已避免没有避免没有避免
    可串行化/可序列化(serializable)已避免已避免

    已避免

    在MySQL中默认的隔离级别是可重复读


    五、设置隔离级来保证事务的隔离性

    该部分结合并发中出现的脏读、不可重复读和幻读来操作。

    (1)

    什么是脏读?


    案例一:未提交隔离级别无法避免脏读。

    a.客户端A开启事务,查询账户余额。


    b.在事务A未提交之前,开启客户端B,更新LIMing的账户余额。


    c.在事务B未提交时,在客户端A查询表中数据,看到了事务B更新的数据。


    d.此时,如果事务B因某些原因需要进行回滚,则所有操作被撤销,A中读到的数据就是脏数据。


    二、

    什么是不可重复读?


    案例二:已提交读避免了脏读,无法避免不可重复读。

    a.客户端A开启事务,查询表中数据。


    b.在事务A还没有提交,开启事务B,更新表中ShangGuan的账户余额。


    c.在事务A中查看ShangGuan的账户余额。余额未该变,说明杜绝了脏读。


    d.事务B提交。


    在事务A中查询账户余额,发生该变。对于开发者来说保证了数据的一致性。但是对于用户来说,两次的读取结果不一样,发生了不可重复读。


    三、

    案例三:可重复读隔离级别避免了不可重复读,但是数据任然保持一致性。

    a.设置隔离级别为可重复读,开启事务A,查询最初的账户余额。


    b.在事务A未提交时,开启事务B,更新表中LiHong的账户余额。


    c.事务B未提交时,在事务A中查询表。发现没有发生变化,避免了不可重复读。


    d.紧接着,在事务A中对LiHong的账户余额做计算,会发现把在事务B中的结果拿来用,在开发人员看来保证了事务的一致性。


    四、

    什么是幻读?


    案例四、可重复读隔离级别下的幻读

    a.在客户端A中开启事务,查询表中数据。


    b.A事务为提交,在客户端B中开启事务,向表中插入一条数据。


    c.在客户端A中计算表中账户余额总和。


    经过操作发现在RPEATABLE-READ隔离级别下并没有发生幻读现象?????

    问号脸,什么原因。

    我查了一下表的存储引擎,如下,是InnoDB存储引擎:


    再查一下,数据库存储引擎


    我忘记了在Linux下的MySQL存储引擎默认是MyISAM存储引擎,它对于非索引项会加表锁。所以查询没出现幻读可能就是这个原因吧。

    在网上查了一下,设置默认存储引擎为InnoDB步骤有:在/etc/my.cnf中,在mysqld后面增加default-storage-engine=INNODB即可。在这儿偷个懒就不验证了。

    五、

    可串行化隔离级别:

    a.在客户端A下设置隔离级别,开启事务。


    b.在A事务未提交时,开启B事务,设置隔离级别,进行插入数据,发现不允许


    ************************************************************************************************************

    总结:

    1、以上操作版本是:

    2、事务的隔离性可通过设置隔离级别来保证。

    3、事务隔离级别越高,越能保证数据的完整性,但是对并发性能影响大,就比如隔离级别为串行化时,读取数据会锁住整张表。

    4、不可重复读和幻读很相似,两者整体上都是两次读的数据不相同,但是不可重复读一般时更新引起的;幻读是插入和删除引起的。

    ***************************************************************************************************************

    展开全文
  • 主要是通过视图的方式来实现的:数据库里面会创建一个视图,访问的时候以视图的逻辑为准. 读未提交: 直接返回记录上的最新值,没有视图概念. 读已提交: 在每个SQL语句开始执行的时候创建视图,所以在查询SQL执行前如果...
  • MySQL事务隔离级别是怎么实现的?

    千次阅读 2019-03-12 23:09:29
    MySQL隔离级别以及各个隔离级别实现的原理
  • MySQL事务隔离级别详解

    万次阅读 多人点赞 2018-12-27 00:45:23
    MySQL中,事务支持实在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有引擎都支持事务。比如MySQL原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被取代的原因之一。 隔离事务的四大特性AC...
  • 主要介绍了MySql四种隔离级别,帮助大家更好的理解和学习MySQL,感兴趣的朋友可以了解下
  • mysql默认事务隔离级别是什么?

    千次阅读 2021-01-18 19:02:07
    mysql数据库事务的隔离级别有4个,而默认的事务处理级别就是【REPEATABLE-READ】,也就是可重复读。下面本篇文章就来带大家了解一下mysql的这4种事务的隔离级别,希望对大家...mysql的4种事务隔离级别,如下所示:...
  • 主要介绍了MySQL事务隔离及其对性能产生的影响,在MySQL的优化方面具有一定的借鉴意义,需要的朋友可以参考下
  • 近来需要学习的东西实在太多了,看了容易忘记。准备写个blog记录下,方便以后复习。本文主要是讲解mysql事务隔离级别和不同级别所出现的问题。
  • MySQL-事务隔离级别设置

    千次阅读 2017-12-25 09:11:32
    先了解下 第一类丢失...撤销一个事务时, 把其他事务已经提交的更新数据覆盖(此情况在事务中不可能出现, 因为一个事务中修改时此记录已加锁, 必须等待此事务完成后另一个事务才可以继续UPDATE) 脏读 一个事务
  • 本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB一、事务的基本要素(ACID)1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 128,643
精华内容 51,457
关键字:

mysql如何实现事务隔离

mysql 订阅