精华内容
下载资源
问答
  • mysql事务隔离级别与spring事务隔离级别的区别
    2021-01-27 03:19:42

    mysql事务隔离级别与spring事务隔离级别的区别:

    脏读:为什么会出现脏读,因为你对数据库的任何修改都会是立即生效的,至于别人能不能看到主要取决与你 是否加锁了,数据库的执行与事务没有关系,事务只是保证对数据库所做的操作会不会撤销而已,mysql默认是行级锁,修改时只会对修改的那几行加锁,加锁期间其他用户线程是看不到修改结果的,所以会导致不可重复读和幻读的问题;

    mysql的事务隔离是通过行级锁实现的;

    oracle的行级锁只有排他锁没有共享锁,默认不是使用锁机制来实现隔离的,而是通过版本控制,不会脏读但是会出现重复读问题,因为a线程在修改期间没有加锁,b线程仍然可以读取到

    幻读的原因:

    幻读的解释:幻读是因为a线程在一定范围内对一批数据进行修改,b线程新增了一条数据,导致用户以为自己没有全量修改;

    幻读的原因:因为mysql默认是通过行级锁来实现的,而不是表级锁,那么在修改期间其他线程当然可以新插入数据了;如果使用了mysql表级锁的引擎如:MyIsAM,幻读就不可能出现;

    spring的隔离级别:

    spring的隔离级别一定是数据库锁机制支持的;如果数据库没有行级锁机制,也没有版本控制那么spring也没办法;

    脏读和不可重复读的区别在于读的时候是否加了读共享锁,不可重复读加了读共享锁;脏读是写的时候加了排他锁

    spring和数据库(mysql)隔离级别的比较:

    spring管理的事务是逻辑事务,而物理事务和逻辑事务的区别在于 事务的传播行为,spring隔离级别和数据库是对应的,在做dml时,数据在数据库中其实已经修改了,只是没有生效而且由于多线程锁的关系,其他的线程过来是看不到该dml操作的结果的。

    更多相关内容
  • 我是李福春,今天的问题是,mysql是如何实现事务隔离的?在实际开发中应该如何正确的使用事务? ACID特性 事务首先具备ACID特性,即 Atomic原子性, Consistency一致性 Isolation隔离性 durability持久性; 事务...
  • MySQL事务隔离级别

    2019-03-21 01:54:28
    NULL 博文链接:https://cuishuangjia.iteye.com/blog/964885
  • MySQL事务隔离

    2020-12-14 15:48:03
    事务(Transaction)是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元。 仅从其定义来看,你可能...数据库事务拥有以下4大特性: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolat
  • 主要介绍了Mysql事务隔离级别原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • MySQL事务隔离级别详解,这个面试经常会面到,必会呀,哈哈
  • Mysql事务隔离机制

    2021-04-14 11:04:21
    Mysql事务隔离机制 什么是数据库事务,特性,隔离级别,每个隔离级别能解决什么问题? (1)数据库事务 事务是并发控制的基本单位,就是将为了完成一个独立功能的一系列的语句封装起来。 (2)事务的特性(ACID) ...

    问题:什么是数据库事务,特性,隔离级别,每个隔离级别能解决什么问题?

    (1)数据库事务

    事务是并发控制的基本单位,就是将为了完成一个独立功能的一系列的语句封装起来。

    (2)事务的特性(ACID)

    原子性(Atomicity):事务是一个整体的工作单元,事务中对数据库的操作要么全部成功,要么全部失败。失败回滚的操作事务,将不能对事务有任何影响。
    一致性(Consistency):当事务完成以后,必须使得所有的数据状态都要保持一致。
    隔离性(Isolation):是指当多个用户并发访问数据库时,比如同时访问一张表,数据库每一个用户开启的事务,不能被其他事务所做的操作干扰(也就是事务之间的隔离),多个并发事务之间,应当相互隔离。事务和事务之间是相互独立存在,事务不能去操作别的事务正在修改的数据。
    永久性(Durability):当事务完成以后,对数据所做的修改会永久保存下来,不能进行撤销。

    (3)事务的并发问题

    脏读:就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
    不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
    幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,发生了幻读。
    不可重复读的和幻读的区分:不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

    (4)事务的隔离级别

    事务的隔离级别从低到高分别是:读未提交(Read uncommitted),读已提交(Read committed),可重复读(Repeatable read ),串行化(Serializable),这四个隔离级别可以分别解决脏读,不可重复读,幻读的问题。

    读未提交(Read uncommitted):事务commit后才会更新到数据库,采用读未提交隔离级别,数据库中的数据并没有改变,因此此隔离级别没有作用。
    读已提交(Read committed):只能解决脏读的问题。
    可重复读(Repeatable read ):每次读取的结果集都相同,而不管其他事务有没有提交,可以解决脏读和不可重复读。
    串行化(Serializable):串行化是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束。mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

    SQL 标准定义了四种隔离级别,mysql全都支持,mysql默认的隔离级别是:可重复读。
    oracle中只支持2个隔离级别:读已提交和串行化,默认是读已提交。

    问题:不可重复读、幻读 ?两者区别?MySQL如何解决?

    可重复读(RR)隔离级别只能解决部分幻读的问题。
    例如:出现幻读的情况
    开启T1事务,然后查询数据
    开启事务T2,插入一条数据,提交事务
    回到事务T1,执行刚才的查询,发现T2插入的数据不存在,幻读没有发生
    T1事务里面,修改刚才插入的数据
    再次执行T1的查询,发现T2的数据出现了,幻读发生了

    为了解决不可重复读,或者为了实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。
    MySQL 已经在可重复读隔离级别下解决了幻读的问题。由于并发写问题的解决方式就是行锁,也叫记录锁,而解决幻读用的也是锁,叫做间隙锁,MySQL 把行锁和间隙锁合并在一起,解决了并发写和幻读的问题,这个锁叫做 Next-Key锁。next-key锁包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身,Mysql的InnoDB存储引擎的默认加锁方式是next-key 锁,next-key锁锁定的范围为间隙锁+记录锁,例如select * from news where number=4 for update ,区间(2,4),(4,5)加间隙锁,同时number=4的记录加记录锁。
    (1)事务隔离级别为读提交时,写数据只会锁住相应的行。
    (2)事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
    (3)事务隔离级别为串行化时,读写数据都会锁住整张表。
    (4)隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

    问题: MySQL MVCC(Multi-Version Concurrency Control)机制?

    MySQL的并发控制有三种常见的机制:悲观控制乐观控制多版本控制,其中悲观并发控制其实是最常见的并发控制机制,也就是,分为行级锁表锁,行级锁又分为共享锁排它锁,事务其实是并发控制的基本单位,事务的隔离性是数据库处理数据的几大基础之一,分为脏读、不可重复读、可重复读、串行化,MySQL通过隔离性和锁来控制事务并发的可靠性。
    MVCC是一种多版本并发控制机制。在MySQL中,MVCC只在读取已提交(Read Committed)和可重复读(Repeatable Read)两个事务级别下有效。我们都知道锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,降低其系统开销。
    MVCC的实现:MVCC是通过保存数据在某个时间点的快照来实现的, 不同存储引擎的MVCC实现是不同的。

    问题:MyISAM和InnoDB存储引擎的区别?

    (1)MyISAM是MySQL关系数据库管理系统的默认储存引擎。这种MySQL表存储结构从旧的ISAM代码扩展出许多有用的功能。在新版本的MySQL中,InnoDB引擎由于其对事务,参照完整性,以及更高的并发性等优点开始广泛的取代MyISAM。每一个MyISAM表都对应于硬盘上的三个文件。这三个文件有一样的文件名,但是有不同的扩展名以指示其类型用途:.frm文件保存表的定义,但是这个文件并不是MyISAM引擎的一部,而是服务器的一部分;.MYD保存表的数据;.MYI是表的索引文件。
    (2)InnoDB是MySQL的另一个存储引擎,正成为目前MySQL AB所发行新版的标准,被包含在所有二进制安装包里。较之于其它的存储引擎它的优点是它支持兼容ACID的事务(类似于PostgreSQL),以及参数完整性(即对外键的支持)。
    (3)目前比较普及的存储引擎是MyISAM和InnoDB,而MyISAM与InnoDB的主要的不同点在于性能和事务控制上。如果你的应用是不需要事务,处理的只是基本的CRUD操作,MyISAM是绝大部分Web应用的首选。InnoDB被设计成适用于高并发读写的情况,使用MVCC(Multi-Version Concurrency Control)以及行级锁来提供遵从ACID的事务支持。InnoDB的设计目标是处理大容量数据库系统,它的CPU利用率是其它基于磁盘的关系数据库引擎所不能比的,使用InnoDB可以应对更为复杂的情况,特别是对并发的处理要比MyISAM高效。

    展开全文
  • 开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?MySQL 事务本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持...

    开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,

    事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?

    MySQL 事务

    本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。

    数据库事务指的是一组数据操作(例如新增、修改、删除、添加索引、修改索引等操作),事务内的操作要么全部成功,要么全部失败。全部失败,什么都不做,其实不是没做,是可能做了一部分但是只要有一步失败,就要回滚所有操作,有点一不做二不休的意思。

    示例:一个网购付款的操作,用户付款后要涉及到收款、订单状态更新、扣库存、退款等一系列动作,这就是一个事务,如果一切正常那就相安无事,一旦中间有某个环节异常,那整个事务就要回滚,总不能收款了,更新了订单状态,但不扣库存吧;退款了,也要更新订单状态,恢复库存吧,这问题就大了。

    事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID,缺一不可。今天要说的就是隔离性。

    1)原子性(Atomicity)

    事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

    2)一致性(Consistency)

    事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。

    3)隔离性(Isolation)

    一个事务的执行不能其它事务干扰,即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

    4)持续性(Durability)

    也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的,接下来的其它操作或故障不应该对其执行结果有任何影响。

    事务隔离的概念说明

    以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。

    1、脏读(UPDATE)

    脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了不一定最终存在的数据,这就是脏读。

    2、不可重复读(UPDATE)

    对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。

    3、可重复读(UPDATE)

    可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

    4、幻读(INSERT)

    幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改(UPDATE),但是还未提交,此时事务B插入(INSERT)了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

    MySQL 事务隔离级别

    SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:

    1)读未提交(READ UNCOMMITTED)   脏数据

    2)读提交 (READ COMMITTED)        不可重复读

    3)可重复读 (REPEATABLE READ)    幻读

    4)串行化 (SERIALIZABLE)

    从上往下,隔离强度逐渐增强,性能逐渐变差,事务都有两面性。

    采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读(RR)是 MySQL 的默认级别。

    事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,

    下面展示了 4 种隔离级别对这三个问题的解决程度。

    隔离级别

    脏读

    不可重复读

    幻读

    读未提交(脏数据)

    可能

    可能

    可能

    读提交   (不可重复读)

    不可能

    可能

    可能

    可重复读(幻读)

    不可能

    不可能

    可能

    串行化

    不可能

    不可能

    不可能

    只有串行化的隔离级别解决了全部这 3 个问题,其他的 3 个隔离级别都有缺陷。

    MySQL 四种隔离级别

    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。

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

    1)Read Uncommitted(读取未提交内容)

    读未提交的隔离级别,所有事务都可以看到其他未提交事务的执行结果。

    本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。

    读取未提交的数据,也被称之为脏读(Dirty Read)

    2)Read Committed(读取提交内容)

    这是大多数数据库系统(如 DB2、Oracle)的默认隔离级别(但不是MySQL默认的,MySQL默认的是可重复度级别)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),即同一事务的其他实例在该实例处理期间可能会有新的commit,所以同一select可能返回不同结果。

    3)Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,MultiVersion Concurrency Control)机制解决了该问题。

    4)Serializable(可串行化)

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。

    简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

    事务隔离级别

    读未提交 (READ UNCOMMITED)

    ->读已提交 (READ COMMITTED)

    ->可重复读 (REPEATABLE READ)

    ->序列化 (SERIALIZABLE)

    隔离级别依次增强,但是导致的问题是并发能力的减弱。

    隔离级别

    脏读

    不可重复读

    幻读

    概念

    READ UNCOMMITED

    Y

    Y

    Y

    事务能够看到其他事务没有提交的修改,当另一个事务又回滚了修改后的情况,又被称为脏读dirty read

    READ COMMITTED

    X

    Y

    Y

    事务能够看到其他事务提交后的修改,这时会出现一个事务内两次读取数据可能因为其他事务提交的修改导致不一致的情况,称为不可重复读

    REPEATABLE READ

    X

    X

    Y

    事务在两次读取时读取到的数据的状态是一致的

    SERIALIZABLE

    X

    X

    X

    可重复读中可能出现第二次读读到第一次没有读到的数据,也就是被其他事务插入的数据,这种情况称为幻读phantom read, 该级别中不能出现幻读

    大多数数据库系统的默认隔离级别都是READ COMMITTED(但MySQL不是),MySQL InnoDB存储引擎默认隔离级别REPEATABLE READ,通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。

    隔离级别的原理

    下面,我们来分析这 4 种隔离级别到底是怎么个意思。

    我们可以通过以下语句查看当前数据库的隔离级别,通过下面语句可以看出使用的 MySQL 的隔离级别是 REPEATABLE-READ,也就是可重复读,这也是 MySQL 的默认级别。

    show variables like 'tx_isolation';

    # 查看事务隔离级别 5.7.20 之后

    show variables like 'transaction_isolation';

    SELECT @@transaction_isolation

    # 5.7.20 之后

    SELECT @@tx_isolation

    show variables like 'tx_isolation'

    +---------------+-----------------+

    | Variable_name | Value |

    +---------------+-----------------+

    | tx_isolation | REPEATABLE-READ |

    +---------------+-----------------+

    稍后,我们要修改数据库的隔离级别,所以先了解一下具体的修改方式。

    修改隔离级别的语句是:set [作用域] transaction isolation level [事务隔离级别]

    SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED| READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

    其中,作用于可以是 SESSION 或者 GLOBAL,GLOBAL 是全局的,而 SESSION 只针对当前回话窗口。

    隔离级别是 {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} 这四种,MySQL不区分大小写。

    例如,下面这个语句的意思是设置全局隔离级别为读提交级别。

    mysql> set global transaction isolation level read committed;

    MySQL 中执行事务

    事务的执行过程如下,以 begin 或者 start transaction 开始,然后执行一系列操作,最后要执行 commit 操作,事务才算结束。当然,如果进行回滚操作(rollback),事务也会结束。

    643956b7ac6fa051e4b0113b961745c6.png

    需要注意的是,begin 命令并不代表事务的开始,事务开始于 begin 命令之后的第一条语句执行的时候。

    例如,下面示例中,select * from xxx 才是事务的开始,

    begin;

    select * from xxx;

    commit; -- 或者 rollback;

    另外,通过以下语句可以查询当前有多少事务正在运行。

    select * from information_schema.innodb_trx;

    > select * from information_schema.innodb_trx;

    +-----------------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+------------------+----------------------------+

    | trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | trx_rows_locked | trx_rows_modified | trx_concurrency_tickets | trx_isolation_level | trx_unique_checks | trx_foreign_key_checks | trx_last_foreign_key_error | trx_is_read_only | trx_autocommit_non_locking |

    +-----------------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+------------------+----------------------------+

    | 421972982987472 | RUNNING | 2021-01-18 15:52:23 | NULL | NULL | 0 | 8629489 | NULL | NULL | 0 | 0 | 0 | 1136 | 0 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 421972982991688 | RUNNING | 2021-01-18 15:52:12 | NULL | NULL | 0 | 8629464 | NULL | NULL | 0 | 0 | 0 | 1136 | 0 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 421972982983256 | RUNNING | 2021-01-18 15:51:58 | NULL | NULL | 0 | 8629405 | NULL | NULL | 0 | 0 | 0 | 1136 | 0 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 421972982941096 | RUNNING | 2021-01-18 15:51:07 | NULL | NULL | 0 | 8629295 | NULL | NULL | 0 | 0 | 0 | 1136 | 0 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 319255680 | RUNNING | 2021-01-18 15:50:56 | NULL | NULL | 2 | 8629246 | NULL | NULL | 0 | 1 | 2 | 1136 | 1 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 421972982995904 | RUNNING | 2021-01-18 15:48:43 | NULL | NULL | 0 | 8628861 | NULL | NULL | 0 | 0 | 0 | 1136 | 0 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    | 319254768 | RUNNING | 2021-01-18 15:46:32 | NULL | NULL | 2 | 8628542 | NULL | NULL | 0 | 1 | 2 | 1136 | 1 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 0 |

    +-----------------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+------------------+----------------------------+

    7 rows in set (0.002 sec)

    好了,重点来了,开始分析这几个隔离级别了。

    接下来我会用一张表来做一下验证,表结构简单如下:

    CREATE TABLE `user` (

    `id` int(11) NOT NULL AUTO_INCREMENT,

    `name` varchar(30) DEFAULT NULL,

    `age` tinyint(4) DEFAULT NULL,

    PRIMARY KEY (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

    初始只有一条记录:

    mysql> SELECT * FROM user;

    +----+-----------------+------+

    | id | name | age |

    +----+-----------------+------+

    | 1 | 古时的风筝 | 1 |

    +----+-----------------+------+

    1、读未提交  (脏数据)

    MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。

    而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。

    但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决。

    任何事务对数据的修改都会第一时间暴露给其他事务,即使事务还没有提交。

    下面来做个简单实验验证一下,首先设置全局隔离级别为读未提交。

    set global transaction isolation level read uncommitted;

    设置完成后,只对之后新起的 session 才起作用,对已经启动 session 无效。

    如果用 shell 客户端那就要重新连接 MySQL;

    如果用 Navicat ,MySQL Workbench、SecureCRT等,连接MySQL,那就要创建新的查询窗口。

    测试详细步骤如下:

    1)启动两个事务,分别为事务A和事务B,

    2)在事务A中使用 update 语句,修改 age 的值为10,初始是1 ,在执行完 update 语句之后,在事务B中查询 user 表,会看到 age 的值已经是 10 了,这时候事务A还没有提交,而此时事务B有可能拿着已经修改过的 age=10 去进行其他操作了。

    3)在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,重新把 age 回滚到了初始值1,那其实事务B得到的数值10 就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的。

    如下图,顺着中间粉色的时间轴,往表示两事务中操作的执行顺序,重点看图中 age 字段的值。

    21eb0b47298a72e55f4eaa352744b9f4.png

    读未提交,其实就是可以读到其他事务未提交的数据,但没有办法保证你读到的数据最终一定是提交后的数据,如果中间发生回滚,那就会出现脏数据问题,读未提交没办法解决脏数据问题。更别提可重复读和幻读了,想都不要想。

    2、读已提交  (不可重复度)

    既然读未提交没办法解决脏数据问题,那么就有了读提交。

    读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据,这样脏数据问题迎刃而解了。

    读已提交事务隔离级别是大多数流行数据库的默认事务隔离界别,比如 Oracle,但是不是 MySQL 的默认隔离界别。

    我们继续来做一下验证,首先把事务隔离级别改为读提交级别。

    set global transaction isolation level read committed;

    之后需要重新打开新的 session 窗口,也就是新的 shell 窗口才可以。

    1)同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。

    2)此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。

    这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读提交隔离级别。

    7289840b9a06f9afa12f87bfadecb1ee.png

    每个 select 语句都有自己的一份快照,而不是一个事务一份,所以在不同的时刻,查询出来的数据可能是不一致的。

    读提交解决了脏读的问题,但是无法做到可重复读,也没办法解决幻读。

    3、可重复读 (幻读)

    可重复是对比不可重复而言的,上面说的不可重复读是指同一事务不同时刻读到的数据值可能不一致。

    而可重复读是指,事务不会读到其他事务对已有数据的修改,及时其他事务已提交,也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。但是,对于其他事务新插入的数据是可以读到的,这也就引发了幻读问题。

    同样的,需改全局隔离级别为可重复读级别。

    set global transaction isolation level repeatable read;

    在这个隔离级别下,启动两个事务,两个事务同时开启。

    首先看一下可重复读的效果,事务A启动后修改了数据,并且在事务B之前提交,事务B在事务开始和事务A提交之后两个时间节点都读取的数据相同,已经可以看出可重复读的效果。

    bcea747c72d54868562754c01aadceae.png

    可重复读做到了,这只是针对已有行的更改(Update)操作有效,但是对于新插入(Insert)的行记录,就没这么幸运了,幻读就这么产生了。幻读是因为新插入数据引发的问题。

    我们看一下这个过程:

    1)事务A开始后,执行 update 操作,将 age = 1 的记录的 name 改为"风筝2号";

    2)事务B开始后,在事务A执行完 update 后,事务B执行 insert 操作,插入记录 age =1,name = "古时的风筝",这时事务B和事务A修改的那条记录值相同,然后提交。

    3)事务B提交后,事务A中执行 select,查询 age=1 的数据,这时,会发现多了一行,并且发现还有一条 name = "古时的风筝",age = 1 的记录,这其实就是事务B刚刚插入的,这就是幻读。

    dad213ab512ffa405b70d5c0aa094505.png

    需重点说明的是,当你在 MySQL 中测试幻读的时候,并不会出现上图的结果,幻读并没有发生,

    MySQL 的可重复读隔离级别,其实解决了幻读问题,这会在后面的内容说明

    4、串行化

    串行化是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束,非常不利于数据库的高并发操作。

    MySQL 中是如何实现事务隔离的

    首先说读未提交,它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。

    再来说串行化:

    1)读的时候加共享锁,也就是其他事务可以并发读,但是不能写。

    2)写的时候加排它锁,其他事务不能并发写也不能并发读,因为写操作还没有完成,读操作可能会出现幻读的错误。

    最后说读已提交和可重复读。这两种隔离级别是比较复杂的,既要允许一定的并发,又想要兼顾的解决问题。

    1、实现可重复读

    为了解决不可重复读,或者为了实现可重复读,MySQL 采用了 MVCC (多版本并发控制) 的方式。

    我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,事务 ID 记为 transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。

    1cf31aa31ba7bb633f26743583c2c6bf.png

    按照上面这张图理解,一行记录现在有 3 个版本,每一个版本都记录这使其产生的事务 ID,比如事务A的transaction id 是100,那么版本1的row trx_id 就是 100,同理版本2和版本3。

    在上面介绍读提交和可重复读的时候都提到了一个词,叫做快照,学名叫做一致性视图,这也是可重复读和不可重复读的关键,可重复读是在事务开始的时候生成一个当前事务全局性的快照,而读提交则是每次执行语句的时候都重新生成一次快照。

    对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:

    1)当前事务内的更新,可以读到;

    2)版本未提交,不能读到;

    3)版本已提交,但是却在快照创建后提交的,不能读到;

    4)版本已提交,且是在快照创建前提交的,可以读到;

    利用上面的规则,再返回去套用到读提交和可重复读的那两张图上就很清晰了。还是要强调,两者主要的区别就是在快照的创建上,可重复读仅在事务开始是创建一次,而读提交每次执行语句的时候都要重新创建一次

    MySQL MVCC 实现原理

    MVCC(Multi Version Concurrency Control,多版本并发控制)

    与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)

    MVCC最大的优势:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能

    MySQL的逻辑架构大概分为四层:

    1)顶层是接入层,不同语言的客户端通过mysql的协议与mysql服务器进行连接通信,接入层进行权限验证、连接池管理、线程管理等。

    2)下面是mysql服务层,包括sql解析器、sql优化器、数据缓冲、缓存等。

    3)再下面是mysql中的存储引擎层,mysql中存储引擎是基于表的。

    4)最后是系统文件层,保存数据、索引、日志等。

    2e9140c3dd05f85bf1d6aea0e365c829.png

    第一层: 服务层(为客户端服务)

    为请求做连接处理,授权认证,安全等。

    第二层:核心服务

    比如查询解析,优化,缓存,内置函数;存储过程,触发器,视图等。

    对于第二层来说,所有跨存储引擎的功能都在这一层实现。

    第三层:存储引擎

    负责MySQL中数据的存储和提取。服务器通过api与存储引擎通信,这些接口屏蔽了存储引擎之间的差异。也就是说,接口的存在,导致不同存储引擎的差异不会影响到上层查询过程。

    架构分层之后,可以更好的理解一些问题,比如我们很关心的并发问题。

    MySQL层面的并发控制,实际上是分为两种:

    1)服务层的并发

    2)存储引擎层的并发的

    第四层:系统文件层,保存数据、索引、日志等。

    61c274c077cad92c5934f011ce4e0bc8.png

    MYSQL 事务日志

    事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。

    如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。

    MySQL Innodb中跟数据持久性、一致性有关的日志,有以下几种:

    1)Bin Log

    是MySQL服务层产生的日志,常用来进行数据恢复、数据库复制,常见的 MySQL 主从架构,就是采用 Slave同步Master的binlog实现的

    2)Redo Log

    记录了数据操作在物理层面的修改,MySQL中使用了大量缓存,修改操作时会直接修改内存,而不是立刻修改磁盘,事务进行中时会不断的产生redo log,在事务提交时进行一次flush操作,保存到磁盘中。当数据库或主机失效重启时,会根据redo log进行数据的恢复,如果redo log中有事务提交,则进行事务提交修改数据。

    3)Undo Log

    除了记录redo log外,当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它记录了修改的反向操作,比如,插入对应删除,修改对应修改为原来的数据,通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的数据,实现MVCC

    2、并发写问题

    存在这的情况,两个事务,对同一条数据做修改,最后结果应该是哪个事务的结果呢,肯定要是时间靠后的那个对不对。并且更新之前要先读数据,这里所说的读和上面说到的读不一样,更新之前的读叫做“当前读”,总是当前版本的数据,也就是多版本中最新一次提交的那版。

    假设事务A执行 update 操作, update 的时候要对所修改的行加行锁,这个行锁会在提交之后才释放。而在事务A提交之前,事务B也想 update 这行数据,于是申请行锁,但是由于已经被事务A占有,事务B是申请不到的,此时,事务B就会一直处于等待状态,直到事务A提交,事务B才能继续执行,如果事务A的时间太长,那么事务B很有可能出现超时异常。如下图所示。

    c17a7bd124920f00ac4fafbcae016cd2.png

    加锁的过程要分有索引和无索引两种情况,比如下面这条语句

    update user set age=11 where id = 1;

    id 是这张表的主键,是有索引的情况,那么 MySQL 直接就在索引数中找到了这行数据,然后干净利落的加上行锁就可以了。

    而下面这条语句

    update user set age=11 where age=10;

    表中并没有为 age 字段设置索引,所以, MySQL 无法直接定位到这行数据。那怎么办呢,当然也不是加表锁了。MySQL 会为这张表中所有行加行锁,没错,是所有行。但是呢,在加上行锁后,MySQL 会进行一遍过滤,发现不满足的行就释放锁,最终只留下符合条件的行。虽然最终只为符合条件的行加了锁,但是这一锁一释放的过程对性能也是影响极大的。所以,如果是大表的话,建议合理设计索引,如果真的出现这种情况,那很难保证并发度。

    3、解决幻读

    上面介绍可重复读的时候,那张图里标示着出现幻读的地方实际上在 MySQL 中并不会出现,MySQL 已经在可重复读隔离级别下解决了幻读的问题。

    前面刚说了并发写问题的解决方式就是行锁,而解决幻读用的也是锁,叫做间隙锁,MySQL 把行锁和间隙锁合并在一起,解决了并发写和幻读的问题,这个锁叫做 Next-Key锁。

    假设现在表中有两条记录,并且 age 字段已经添加了索引,两条记录 age 的值分别为 10 和 30

    11050d622405b5eebc304c26bce0c8e2.png

    此时,在数据库中会为索引维护一套B+树,用来快速定位行记录。

    B+索引树是有序的,所以会把这张表的索引分割成几个区间。

    7e9dd021bdf698caa41d006c1bb6cd20.png

    如图所示,分成了3 个区间,(负无穷,10]、(10,30]、(30,正无穷],在这3个区间是可以加间隙锁的。

    之后,我用下面的两个事务演示一下加锁过程。

    0c9a79fe39cfb198c5eaa1dc4a26a71d.png

    在事务A提交之前,事务B的插入操作只能等待,这就是间隙锁起得作用。

    当事务A执行update user set name='风筝2号’ where age = 10; 的时候,由于条件 where age = 10 ,数据库不仅在 age =10 的行上添加了行锁,而且在这条记录的两边,也就是(负无穷,10]、(10,30]这两个区间加了间隙锁,从而导致事务B插入操作无法完成,只能等待事务A提交。不仅插入 age = 10 的记录需要等待事务A提交,age<10、10

    这是有索引的情况,如果 age 不是索引列,那么数据库会为整个表加上间隙锁。

    所以,如果是没有索引的话,不管 age 是否大于等于30,都要等待事务A提交才可以成功插入。

    总结

    MySQL 的 InnoDB 引擎才支持事务,其中可重复读是默认的隔离级别。

    读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。

    读提交解决了脏读问题,行锁解决了并发更新的问题。

    并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。

    本文转自:

    参考推荐:

    展开全文
  • 作者:伞U ...好久没碰数据库了,只是想起自己当时在搞数据库的...为了说明问题,我们打开两个控制台分别进行登录来模拟两个用户(暂且成为用户 A 和用户 B 吧),并设置当前 MySQL 会话的事务隔离级别。 一. read unco
  • 查看事务隔离级别 在 MySQL 中,可以通过show variables like ‘%tx_isolation%’或select @@tx_isolation;语句来查看当前事务隔离级别。 查看当前事务隔离级别的 SQL 语句和运行结果如下: mysql> show variables ...
  • 主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 查看mysql 事务隔离级别 mysql> show variables like '%isolation%'; +---------------+----------------+ | Variable_name | Value | +---------------+----------------+ | tx_isolation | READ-COMMITTED | +---...
  • MySQL 四种事务隔离级别详解及对比 按照SQL:1992 事务隔离级别,InnoDB默认是可重复读的(REPEATABLE READ)。MySQL/InnoDB 提供SQL标准所描述的所有四个事务隔离级别。你可以在命令行用–transaction-isolation选项...
  •  我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。所以...
  • MYSQL事务隔离与锁

    2021-06-22 02:45:51
    本套课程主要是对mysql事务隔离级别以及锁相关知识进行深入讲解其中包括:mysql四大特性的深入理解、四种隔离级别的实现原理、一致性读的实现原理、全局锁、表锁、行锁,间隙锁的介绍与分析,对于难点和重点会使用...
  • Mysql事务隔离级别测试

    千次阅读 2022-01-28 16:24:04
    以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。 脏读 脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据...

    一、概念说明

    以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。

    脏读

    脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。

    可重复读

    可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

    不可重复读

    对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。

    幻读

    幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

    事务隔离级别

    SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:

    1. 读未提交(READ UNCOMMITTED)
    2. 读提交 (READ COMMITTED)
    3. 可重复读 (REPEATABLE READ)
    4. 串行化 (SERIALIZABLE)

    可重复读是 MySQL 的默认级别。 

    二、开始测试

    前提准备

    查看隔离级别

    show variables like '%tx_isolation%'

     数据库表

    DROP TABLE IF EXISTS `goods`;
    CREATE TABLE `goods`  (
      `goods_id` int(8) NOT NULL AUTO_INCREMENT,
      `goods_name` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`goods_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

     默认表中有一条数据

    读未提交

    首先设置全局隔离级别为读未提交。

    set global transaction isolation level read uncommitted;

    两事务中操作的执行顺序如下:

     A事务开启线程查询

     A事务修改

     B事务开启线程查询,拿到的数据是banana。

     

    A事务此时rollback,表中存储数据是apple

    上面B产生了脏读,要想解决脏读这个问题可以采用读已提交的隔离级别

    读已提交

    首先设置全局隔离级别为读已提交。

    set global transaction isolation level read committed;

    两事务中操作的执行顺序如下:

      A开启线程查询

       B开启线程查询,查询与A一致,都是表中数据;

       A线程修改数据

       B线程查询,A事务未提交,B中查询仍是apple

      A线程提交

       B线程查询得到提交后数据

     读提交解决了脏读的问题,但是无法做到可重复读

    可重复读

    首先设置全局隔离级别为可重复读。

    set global transaction isolation level repeatable read;

    下图过程测试实现可重复读

       A开启线程查询

    B开启线程查询,查询与A一致,都是表中数据;

        A线程修改数据并提交

        B线程在同一线程内查询,得到结果未变。与可重复读的概念理解一致,就是同一事务里不同时间点查询,不会受到其他事务修改数据的影响。

     此时C新开启线程查询,读到就是修改后的数据

    下图过程测试产生否幻读的问题

       A开启线程查询

        B开启线程插入数据并提交

        A线程再次查询,没有查到3 cherry

    A线程插入3 cherry,报主键=3已经存在。

     

     此时就产生了幻读,A事务本身查询看不到主键=3的数据,但插入时却仍报主键冲突。

    串行化

       A开启线程查询

       B开启线程插入数据,

    总结:

            读未提交会产生脏读问题;

            为了解决脏读,使用读已提交,会产生可不重复读问题;

            为了解决不重复读,使用可重复读;

            MySQL 已经在可重复读隔离级别下解决了幻读的问题,通过行锁和间隙锁的组合 Next-Key 锁实现。

            最简单粗暴的方式是串行,影响性能最大,故很少使用;

    展开全文
  • 一、MySQL 事务   本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。   数据库事务指的是一组数据操作,事务内的操作要么全部成功,要么全部失败。什么都不做,不一定是真的什么都没...
  • 设置事务隔离级别为读取已提交| 事务A 事务B 开启事务,设置事务隔离级别为读取已提交 查到5条记录 开启事务,插入一条记录id=6 事务提交 继续查询,依然查到6条记录 这就出现了一个问题,...
  • 查询mysql事务隔离级别

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

    千次阅读 2019-03-24 18:33:13
    msql事务隔离级别        MySQL定义了4类隔离级别,包括一些具体规则,用来限定事物内外的哪些改变时可见的,哪些改变时不可见的,低级别的隔离级一般支持更高的并发处理,并...
  • 锁只是并发控制的一种粒度,只是一个很小的部分: 从不同场景下是否需要控制并发,(已知无交集且有序的数据的变更,MySQL 的 MTS 相同前置事务的多事务并发回放) 并发控制的粒度,(锁是一种逻辑粒度,可能还存在...
  • 深入理解mysql事务隔离级别和锁机制

    千次阅读 2022-03-13 10:30:52
    其实本质上,出现出现这些问题的本质就是数据库的多事务并发问题,针对这些问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制机制等来解决并发问题。接下来,我会详细的介绍这些机制,让大家深入理解...
  • 开启事务,设置事务隔离级别为读未提交 查到5条记录 开启事务,插入一条记录id=6 ,事务并未提交 继续查询,查到6条记录 事务回滚 继续查询,查到5条记录 这样在事务A中就出现了脏读数据 事务脏...
  • MySQL 事务隔离机制隔离性与隔离级别四个案例看懂 MySQL 事务隔离级别查看隔离级别READ_UNCOMMITTED脏读不可重复读幻读READ_COMMITTEDREPEATABLE_READSERIALIZABLE事务隔离的实现总结 提到事务,大家肯定不陌生。最...
  • mysql事务隔离级别

    千次阅读 2019-06-01 08:13:07
    隔离级别 MySQL是一个服务器/客户端架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称之为一个会话(Session)。我们可以同时在不同的会话里输入各种语句,...
  • Mysql事务隔离机制 文章目录Mysql事务隔离机制什么是事务事务的四个特征 (ACID)MySQL的事务隔离机制MySql并发一致性问题 在介绍什么是事务隔离机制前,先了解一下什么是事务 什么是事务 事务是在程序运行中一系列...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 135,907
精华内容 54,362
关键字:

mysql 事务隔离

mysql 订阅