精华内容
下载资源
问答
  • 主要介绍了Mysql事务隔离级别原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 为了保证事务操作的原子性,必须实现基于日志的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;

    展开全文
  • 经常提到数据库的事,事务隔离还有隔离级别,那什么是事务隔离隔离级别又是什么呢?本文就帮大家梳理一下。 MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。 数据库...

    经常提到数据库的事,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下。

    MySQL 事务

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

    数据库事务指的是一组数据操作,事务内的操作要么就是全部成功,要么就是全部失败,什么都不做,其实不是没做,是可能做了一部分但是只要有一步失败,

    就要回滚所有操作。

    假设一个网购付款的操作,用户付款后要涉及到订单状态更新、扣库存以及其他一系列动作,这就是一个事务,如果一切正常那就相安无事,一旦中间有某个环节异常,

    那整个事务就要回滚,总不能更新了订单状态但是不扣库存吧,这问题就大了

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

    概念说明

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

    脏读

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

    读到了并不一定最终存在的数据,这就是脏读。

    可重复读

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

    不可重复读

    对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。

    通常针对数据更新(UPDATE)操作

    幻读

    幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,

    并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

    事务隔离级别

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

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

    从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

    事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。

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

    一探究竟

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

    如何设置隔离级别

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

     查看事务隔离级别 5.7.20 之后
    show variables like 'transaction_isolation';
    SELECT @@transaction_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> set global transaction isolation level read committed;

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

    preview

    需要注意的是,begin 命令并不代表事务的开始,事务开始于 begin 命令之后的第一条语句执行的时候。例如下面示例中,select * from xxx 才是事务的开始,

    begin;
    select * from xxx; 
    commit; -- 或者 rollback;

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

    select * from information_schema.innodb_trx;

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

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

    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 |
    +----+-----------------+------+

    读未提交

    MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。但有利就有弊,

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

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

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

    preview

    set global transaction isolation level read uncommitted;

    设置完成后,只对之后新起的 session 才起作用,对已经启动 session 无效。如果用 shell 客户端那就要重新连接 MySQL,如果用 Navicat 那就要创建新的查询窗口。

    启动两个事务,分别为事务A和事务B,在事务A中使用 update 语句,修改 age 的值为10,初始是1 ,在执行完 update 语句之后,在事务B中查询 user 表,会看到 age 的值已经是 10 了,

    这时候事务A还没有提交,而此时事务B有可能拿着已经修改过的 age=10 去进行其他操作了。在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,

    那其实事务B得到的就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的。

    读未提交,其实就是可以读到其他事务未提交的数据,但没有办法保证你读到的数据最终一定是提交后的数据,如果中间发生回滚,那就会出现脏数据问题,

    读未提交没办法解决脏数据问题。更别提可重复读和幻读了,想都不要想。

    读提交

    既然读未提交没办法解决脏数据问题,那么就有了读提交。读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据。那脏数据问题迎刃而解了。

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

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

    set global transaction isolation level read committed;

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

    同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,

    事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。

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

    这就是不可重复读,也就是读提交隔离级别。

    可重复读做到了,这只是针对已有行的更改操作有效,但是对于新插入的行记录,就没这么幸运了,幻读就这么产生了。我们看一下这个过程:

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

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

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

    preview

    要说明的是,当你在 MySQL 中测试幻读的时候,并不会出现上图的结果,幻读并没有发生,MySQL 的可重复读隔离级别其实解决了幻读问题,这会在后面的内容说明

    串行化

    串行化是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,

    它就相当于单线程,后一个事务的执行必须等待前一个事务结束。

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

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

    再来说串行化。读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。

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

    实现可重复读

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

    我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,

    事务 ID 记为 transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。

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

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

    而读提交则是每次执行语句的时候都重新生成一次快照。

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

    1. 当前事务内的更新,可以读到;
    2. 版本未提交,不能读到;
    3. 版本已提交,但是却在快照创建后提交的,不能读到;
    4. 版本已提交,且是在快照创建前提交的,可以读到;

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

    而读提交每次执行语句的时候都要重新创建一次。

    并发写问题

    存在这的情况,两个事务,对同一条数据做修改。最后结果应该是哪个事务的结果呢,肯定要是时间靠后的那个对不对。并且更新之前要先读数据,

    这里所说的读和上面说到的读不一样,更新之前的读叫做“当前读”,总是当前版本的数据,也就是多版本中最新一次提交的那版。

    假设事务A执行 update 操作, update 的时候要对所修改的行加行锁,这个行锁会在提交之后才释放。而在事务A提交之前,事务B也想 update 这行数据,

    于是申请行锁,但是由于已经被事务A占有,事务B是申请不到的,此时,事务B就会一直处于等待状态,直到事务A提交,事务B才能继续执行,

    如果事务A的时间太长,那么事务B很有可能出现超时异常。如下图所示。

     preview

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

    update user set age=11 where id = 1

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

    而下面这条语句

    update user set age=11 where age=10

    表中并没有为 age 字段设置索引,所以, MySQL 无法直接定位到这行数据。那怎么办呢,当然也不是加表锁了。MySQL 会为这张表中所有行加行锁,没错,是所有行。

    但是呢,在加上行锁后,MySQL 会进行一遍过滤,发现不满足的行就释放锁,最终只留下符合条件的行。虽然最终只为符合条件的行加了锁,但是这一锁一释放的过程对性能也是影响极大的。

    所以,如果是大表的话,建议合理设计索引,如果真的出现这种情况,那很难保证并发度。

    解决幻读

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

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

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

    此时,在数据库中会为索引维护一套B+树,用来快速定位行记录。B+索引树是有序的,所以会把这张表的索引分割成几个区间。

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

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

    在事务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<30 的记录页无法完成,

    而大于等于30的记录则不受影响,这足以解决幻读问题了。

    这是有索引的情况,如果 age 不是索引列,那么数据库会为整个表加上间隙锁。所以,如果是没有索引的话,不管 age 是否大于等于30,

    都要等待事务A提交才可以成功插入。

    总结

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

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

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

     

     

     

     

     

    展开全文
  • 近来需要学习的东西实在太多了,看了容易忘记。准备写个blog记录下,方便以后复习。本文主要是讲解mysql事务隔离级别和不同级别所出现的问题。

    mysql事务

           Markdown编辑器用的不顺手,自己选的,跪着也要用完。
           事务通俗的来讲就是SQL要么全部执行成功,要么全部执行失败回滚到执行前的状态。不存在部分执行成功,部分执行失败的情况。

    事务特性

           事务通常需要满足四个特性,也就是事务的ACID特性。对于Oracle数据库来说,其默认的事务隔离级别是READ COMMITTED;对于InnoDB存储引擎,其默认的事务隔离级别为REPEATABLE READ。

    1. 原子性(atomicity)。原子性是指整个数据库事务是个不可分割的工作单位。只有事务中的数据库所有操作操作都执行成功,才算整个事务成功。
    2. 一致性(consistency)。一致性是指事务讲数据库从一种状态转变为另一种一致的转态。
    3. 隔离性(isolation)。隔离性也叫并发控制、可串行化、锁等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离。
    4. 持久性(durability)。事务提交后,其结果将会保存到硬件上。

    事务的隔离级别

    事务的隔离级别有

    1. read uncommitted
    2. read committed
    3. repeatable read
    4. serializable

    read uncommitted

    事务1读到了事务2未提交的数据,出现了脏读
    事务1读到了事务2未提交的数据,出现了脏读

    read committed

    事务2增加或者修改了数据,事务1前后两次读取到的数据不一样(不满足事务的一致性)。
    在这里插入图片描述

    repeatable read

    1核1G服务器,开启2个事务,我以很快的速度提交事务,一直提示

    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    

    这个问题。后来查询了下,发现是因为有事务没有提交,使用

    select * from information_schema.innodb_trx \G
    

    来查看事务提交情况,使用kill trx_mysql_thread_id就可以了。在这里插入图片描述
    repeatable read 会出现幻读,比如我事务1修改了特定或者全部数据,我事务2新增加了一个1条记录。事务2提交后,事务1也提交,再查询,发现有条数据没有被更新成为指定值。
    repeatable read

    serializable

    事务最高隔离级别,事务是一个一个执行的,测试的时候发现,更新操作需要等待另外一个事务commit后才能执行成功。
    在这里插入图片描述

    事务实现原理

       阿里面试事务原理

    • 事务实现原理
    • redo 和undo
    • 日志查看

    小记

    2、本文参考了 MySQL技术内幕-InnoDB存储引擎 书籍,该书指粗略的看了下,不是太明白,有机会大家多交流交流。
    2、Markdown还是用的不是很熟练。和写论文的LaTeX一样,用起来不顺手QAQ。

    在这里插入图片描述

    展开全文
  • MySQL事务隔离级别详解

    万次阅读 多人点赞 2018-12-27 00:45:23
    MySQL中,事务支持实在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有引擎都支持事务。比如MySQL原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被取代的原因之一。 隔离事务的四大特性AC...

    前言

    提到事务,你肯定不会陌生,最经典的例子就是转账,甲转账给乙100块,当乙的账户中到账100块的时候,甲的账户就应该减去100块,事务可以有效的做到这一点。

    在MySQL中,事务支持实在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有引擎都支持事务。比如MySQL原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被取代的原因之一。

    隔离性

    事务的四大特性ACID(Atomicity原子性、Consistency一致性、Isolation隔离性、Durability持久性),今天就来讲讲其中的隔离性。当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了事务隔离级别这个概念。

    首先你要知道,隔离得越结实,效率就会越低。因此需要我们考虑到这两点,不能只顾一边。SQL的标准事务隔离级别包括:

    • 读未提交(read uncommitted):一个事务还没有提交时,它做的变更就能被别的事务看到。
    • 读提交(read committed):一个事物提交之后,它做的变更才会被其他事务看到。
    • 可重复读(repeatable read):一个事物执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交变更对其他事务也是不可见的。
    • 串行化(serializable):对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。

    其中,“读提交”和“可重复读”比较难理解,这里用一个例子说明一下。

    假设数据表中 T 中只有一列,其中一行的值为 1,下面是按照时间顺序执行两个事务的行为。

    create table T(c int) engine = InnoDB;
    insert into T(c) values(1);

    在不同的隔离级别下,事务A查到的结果是不一样的:

    • 若隔离级别是“读未提交”,则 v1 的值是2,这时候事务 B 虽然还没有提交,但是结果已经被事务 A 看到了,因此 v2、v3 也都是2。
    • 若隔离级别是“读提交”,则 v1 是1,v2 的值是 2 ,事务 B 的更新在提交后才被事务 A 看到。所以 v3 的值也是 2。
    • 若隔离级别是“可重复读”,则 v1、v2是 1,v3 是2,之所以 v2 还是1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
    • 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住,直到事务 A 提交后,事务 B 才可以继续执行,所以从事务 A 的角度看,v1、v2的值是1,v3的值是2。

    在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的。这里需要注意的是,“读未提交”隔离界别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

    我们可以看到在不同的隔离级别下,数据库行为是有所不同的。Oracle 数据库的默认隔离级别其实是“读提交”,因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,你一定要记得将 MySQL 的隔离级别设置为“读提交”。配置的方式是:将启动参数 transaction-isolation 的值设置成 READ-COMMITTED,你可以用 show variables 来查看当前的值。

    每个事务隔离级别都有存在的意义,都有自己的场景,下面来一个“可重复读”的场景:

    假设你在管理一个个人银行账户表,一个表存了每个月月底的余额,一个表存了账单明细,这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时候使用“可重复读”隔离级别就很方便,事务启动时的视图可以认为是静态的,不受其他事务更新的影响。

    事务隔离的实现

    理解了事务隔离级别,我们再来看看事务隔离具体是怎么实现的,这里我们展开说明“可重复读”。在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作,记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

    假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

    当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在试图 A、B、C里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。

    同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C对应的事务是不会冲突的。那么回滚日志什么时候删除呢?答案是,再不需要的时候才删除,也就是说,系统会判断,当没有事务在需要用到这些回滚日志时,回滚日志才会被删除。什么时候才是不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

    基于上面的说明,我们来讨论一下为什么建议你尽量不要使用长事务。

    长事务意味着系统里面会存在很老的事务视图,由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

    在MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小,作者见过数据只有20GB,而回滚段有200GB 的库,最终只好为了清理回滚段,重新建个库。

    除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

    事务的启动方式

    如前面所述,长事务有这些潜在风险,我当然建议读者尽量避免。其实很多业务开发的同学并不是有意使用长事务,通常是由于误用所致。MySQL 的事务启动方式有以下几种:

    1. 显式启动事务语句,begin 或者 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
    2. set autocommit = 0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动 commit 或者 rollback 语句,或者断开连接。

    有些客户端连接框架会默认连接成功后先执行一个 set autocommit = 0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务,因此,建议读者总是使用 set autocommit = 1,通过显式语句的方式来启动事务。

    但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务。第二种方式每个事务在开始时都不需要主动执行一次“begin”,减少了语句的交互次数。如果读者也有这个顾虑,建议使用 commit work and chain 语法。

    在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。

    你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。

    select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

    小结

    这篇文章主要介绍了MySQL的事务隔离几倍的现象和实现,根据实现原理分析了长事务存在的风险,以及如何用正确的方式避免长事务。

    篇尾继续留个问题:现在知道了系统里面如何避免长事务,如果你是业务开发负责人同时也是数据库负责人,你会有什么方案来避免出现或者处理这种情况呢?问题答案下篇揭晓。

    上一篇问题答案

    上一篇的问题是:

    前面说到定期全量备份的周期,“取决于系统重要性,有的是一天一备,有的是一周一备”,那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?

    优势是“最长恢复时间”更短。

    在一天一备的模式里,最坏的情况下需要应用一天的 binlog。比如,每天 0 点做一次全量备份,而要恢复出一个到昨天晚上 23 点的备份。在一周一备的模式里,最坏的情况就是需要应用一周的 binlog 了。

    数据库系统中对应的指标就是恢复目标时间(RTO)。当然,一天一备的成本要远高于一周一备,毕竟是全量备份。所以全量备份的周期还是需要根据业务重要性来评估。

    展开全文
  • 一、MySQL 事务   本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。   数据库事务指的是一组数据操作,事务内的操作要么全部成功,要么全部失败。什么都不做,不一定是真的什么都没...
  • 经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离隔离级别又是什么呢?本文就帮大家梳理一下。 MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引擎下,...
  • 由于Mysql 默认的隔离级别是...本文只介绍事务隔离级别相关的特性 违反事务隔离级别的几个特征 首先需要了解一下违反事物隔离级别的几个特征: 脏读 当前事务中读取到了其它没有提交的事务修改的数据———读.
  • mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务。 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败。...事务隔离级别 事务的隔离级别有4个层级,隔离级别依
  • MySQL事务隔离级别实现原理

    千次阅读 2018-12-27 22:38:51
    MySQL的众多存储引擎中,只有InnoDB支持事务,所有这里讨论的事务隔离级别指的是MySQL InnoDB下的事务隔离级别,一般而言,隔离级别分为:读未提交:一个事务可以读取到另一个事务未提交的修改。这会带来脏读、幻...
  • 点击上方“后端开发技术”,选择“设为星标” ,优质文章和资源,及时送达上一篇MySQL的文章讲了事务的特性和原理(最详细的MySQL事务特性及原理讲解!(一)) 作为基础,小伙伴们自行...
  •  我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。所以...
  • 36谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
  • 主要是通过视图的方式来实现的:数据库里面会创建一个视图,访问...可重复读: 在事务启动时创建的视图,整个事务存在期间都用这个视图,所以读到的数据不会发生变化. 串行化:   是直接使用加锁的方式来避免并行访问. ...
  • 事务隔离是指一个事务所做的修改,对另一个事务的可见性。如果事务与事务之间不进行隔离,那么就会导致数据...mysql的四种事务隔离级别,帮助你理解和解决遇到这一些问题,来看看适合你业务的事务隔离级别是什么吧!
  • MySQL支持四种事务隔离级别: {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} 即读未提交 | 不可重复读| 可重复读 | 串行化,默认为可重复读。 设置当前会话隔离级别为不可重复读: ...
  • 事务隔离级别1.Read uncommitted(读未提交)2.Read committed(读已提交)3.Repeatable read(可重复读)4.Serializable(可串行化)三.深入理解事务隔离1.版本链2.read view3.innodb中的锁4.行级锁的三种算法5.快照读和...
  • MySQL-事务隔离级别设置

    千次阅读 2017-12-25 09:11:32
    先了解下 第一类丢失...撤销一个事务时, 把其他事务已经提交的更新数据覆盖(此情况在事务中不可能出现, 因为一个事务中修改时此记录已加锁, 必须等待此事务完成后另一个事务才可以继续UPDATE) 脏读 一个事务
  • MySQL的众多存储引擎中,只有InnoDB支持事务,以下是InnoDB下的事务隔离级别 1. 读未提交(Read Uncommitted) 原理:任何操作都不加锁 2. 读提交(Read Commit): MySQL默认 原理:读操作不加锁,写操作...
  • 分别是原子性、一致性、隔离性、持久性。 1、原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库...
  • 一个逻辑工作单元必须有四个属性,称为 ACID(原子性、致性、隔离性和持久性)属性,只有这样才能成为一个事务。 数据库事物的四大特性(ACID): 1)原子性:(Atomicity) 务必须是原子工作单元;对于其数据修改,...
  • MySQL事务隔离级别以及实现原理

    千次阅读 2018-06-04 10:13:10
    一、事务完整性问题脏读:可以读取其他事务未提交的数据,如果该事务回滚,则数据为错误数据。不可重复读:A事务查看,B事务修改提交,A事务再次查看,数据不一样。幻读:幻读与不可重复读相似,但不可重复读重点...
  • 所谓的数据库事务操作其实就是一组原子性的操作,要么全部操作成功,要么全部操作失败。  并行事务的四大问题:  1.更新丢失:和别的事务读到相同的东西,各自写,自己的写被覆盖了。(谁写的快谁的更新就丢失...
  • mysql 事务隔离级别

    千次阅读 2012-12-26 11:47:58
    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。本文将以具体的例子来讲解这4类隔离级别。 ...
  • MySQL事务隔离级别以及MVCC机制

    千次阅读 2016-03-02 12:27:03
    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 Read Uncommitted(读取未提交内容)  在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,921
精华内容 14,368
关键字:

mysql事务隔离级别原理

mysql 订阅