精华内容
下载资源
问答
  • 隔离级别
    千次阅读
    2022-02-17 11:08:15

    目录

    一、 4种隔离级别

    1、读未提交(Read Uncommitted)

    2、读提交(Read Committed)

    3、可重复读(Repeated Read)

    4、串行读(Serializable)

    二、不同隔离级别的示例

    三、隔离级别的设置与查看

    1、设置隔离级别

    2、查看当前隔离级别

    四、事务隔离的实现原理

    1、隔离级别实现的原理

    2、为什么建议尽量不要使用长事务?

    3、如何避免长事务对业务的影响?

    五、隔离级别引起的问题

    1、脏读

    2、不可重复读

    3、幻读

    4、在可重复读级别出现前后数据不一致现象

    六、总结


    一、 4种隔离级别

    InnoDB默认是可重复读的(REPEATABLE READ)。

    1、读未提交(Read Uncommitted)

    一个事务还没提交时,它做的变更就能被别的事务看到

    允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

    2、读提交(Read Committed)

    一个事务提交之后,它做的变更才会被其他事务看到

    只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)

    3、可重复读(Repeated Read)

    一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。

    可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读

    4、串行读(Serializable)

    串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后面访问的事务必须等前一个事务执行完成,才能继续执行。

    完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

    隔离级别

    脏读(Dirty Read)

    不可重复读(NonRepeatable Read) 

    幻读(Phantom Read) 

    读未提交(Read uncommitted)

    可能  

    可能 

    可能  

    读提交(Read committed)

    不可能

    可能 

    可能 

    可重复读(Repeatable read)

    不可能

    不可能

    可能 

    串行化(Serializable )

    不可能

    不可能

    不可能

    二、不同隔离级别的示例

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

     

    我们来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么:

    1)若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。

    2)若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A看到。所以, V3 的值也是 2。

    3)若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。

    4)若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。

    在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。

    1、在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。
    
    2、在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。
    
    3、这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;
    
    4、而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

    总结来说,存在即合理,哪个隔离级别都有它自己的使用场景,你要根据自己的业务情况来定。我想你可能会问那什么时候需要“可重复读”的场景呢?我们来看一个数据校对逻辑的案例。

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

    三、隔离级别的设置与查看

    1、设置隔离级别

    1)在my.ini中设置级别

    vim /etc/my.ini
    transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}

    2)使用set transaction 设置单个会话或者所有新进连接的隔离级别

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

    (1)global,意思是此语句将应用于之后的所有session,而当前已经存在的session不受影响

    (2)选择session,将应用于当前session内此点之后的所有事务(包括已开始但未提交的)。

    (3)什么都不写,将应用于当前session内的下一个还未开始的事务。

    2、查看当前隔离级别

    SELECT @@global.tx_isolation;
    
    SELECT @@session.tx_isolation;
    
    SELECT @@tx_isolation;
    
    show variables like 'transaction_isolation';
    +-----------------------+----------------+
    | Variable_name | Value |
    +-----------------------+----------------+
    | transaction_isolation | READ-COMMITTED |
    +-----------------------+----------------+

    四、事务隔离的实现原理

    1、隔离级别实现的原理

    在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上一次的最新值,通过回滚操作,都可以得到前一个状态的值。假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录:

     

    当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。

    如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。

    对于 read-viewA,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。

    你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。

    什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

    2、为什么建议尽量不要使用长事务?

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

    在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有200GB 的库。最终只好为了清理回滚段,重建整个库。除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

    3、如何避免长事务对业务的影响?

    1)从应用开发端来看:

    (1)确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1,否则事务一直等着被提交占用资源。

    (2)确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。

    (3)业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。

    2)其次,从数据库端来看:

    (1)监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;

    (2)Percona 的 pt-kill 这个工具不错,推荐使用;

    (3)在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;

    (4)如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。

    五、隔离级别引起的问题

    1、脏读

    脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据(出现在读未提交隔离级别下)

    ##############  session 1 ##############
    mysql> select @@session.tx_isolation;
    +-----------------------+
    | @@session.tx_isolation |
    +-----------------------+
    | REPEATABLE-READ       |
    +-----------------------+
    1 row in set (0.00 sec)
    
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into ttd values(1);
    Query OK, 1 row affected (0.05 sec)
    
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)
    
    
    ############session 2(会话2):##############
    
    # 在REPEATABLE-READ 级别下 不会出现脏读
    mysql> select * from ttd;
    Empty set (0.00 sec)     
    
    #设置级别为读未提交
    mysql> set session transaction isolation level read uncommitted;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @@session.tx_isolation;
    +------------------------+
    | @@session.tx_isolation |
    +------------------------+
    | READ-UNCOMMITTED       |
    +------------------------+
    1 row in set (0.00 sec)
    
    #读到了session 1未提交,但修改的数据
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |
    
    +------+
    1 row in set (0.00 sec)

    结论:session 2 在READ-UNCOMMITTED 下读取到session 1 中未提交事务修改的数据.

    2、不可重复读

    是指在一个事务内(前提是在事务中,如果一个session是事务,另一个session不是事务则不影响),多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。

    这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(发生在读提交隔离级别下)

    ############session 1(会话1):##############
    
    #设置隔离级别为 读提交
    mysql> select @@session.tx_isolation;
    +------------------------+
    | @@session.tx_isolation |
    +------------------------+
    | READ-COMMITTED         |
    +------------------------+
    1 row in set (0.00 sec)
    
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)
    
    
    ############session 2(会话2):##############
    
    mysql> insert into ttd values(2);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |
    |    2 |
    +------+
    2 rows in set (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.02 sec)
    
    ############session 1(会话1):##############
    
    #和第一次的结果不一样,READ-COMMITTED 级别出现了不重复读
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 | 
    |    2 |
    +------+
    2 rows in set (0.00 sec)
    

    原因是因为session1 是 read committed (读提交)级别,在没有commit的情况下读到的两次数据不一致

    可重复读级别就不会出现上述下现象

    ############session 1(会话1):##############
    mysql> select @@session.tx_isolation;
    +------------------------+
    | @@session.tx_isolation |
    +------------------------+
    | REPEATABLE-READ        |
    +------------------------+
    1 row in set (0.00 sec)
    
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |
    |    2 |
    +------+
    2 rows in set (0.00 sec)
    
    ############session 2(会话2):##############
    
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into ttd values(3);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.03 sec)
    
    
    ############session 1(会话1):##############
    
    #和第一次的结果一样,REPEATABLE-READ级别出现了重复读,并没有读到3,直到session1 commit之后才能读到3
    mysql> select * from ttd;
    +------+
    | id   |
    +------+
    |    1 |    --------
    |    2 |
    +------+
    2 rows in set (0.00 sec)

    3、幻读

            第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

    mysql>CREATE TABLE `demo` (
        `id` bigint(20) NOT NULL default '0',
        `value` varchar(32) default NULL,
        PRIMARY KEY (`id`)
        
    ) ENGINE=InnoDB
    
    mysql> select @@global.tx_isolation, @@tx_isolation;
    +-----------------------+-----------------+
    | @@global.tx_isolation | @@tx_isolation  |
    +-----------------------+-----------------+
    | REPEATABLE-READ       | REPEATABLE-READ |
    +-----------------------+-----------------+

    实验一:

    Session A

    Session B

    START TRANSACTION;

    START TRANSACTION;

    SELECT * FROM demo;

    empty set

    INSERT INTO demo VALUES (1, 'a');

    SELECT * FROM demo;

    empty set

    COMMIT;

    SELECT * FROM demo;

    empty set

    INSERT INTO demo VALUES (1, 'a');

    ERROR 1062 (23000):

    Duplicate entry '1' for key 1

    刚刚明明告诉我没有这条记录的,在REPEATABLE-READ(可重复读)级别下出现了幻读

    实验二:

    Session A

    Session B

    START TRANSACTION;

    START TRANSACTION;

    SELECT * FROM demo;

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

    | | id | value |

    | +------+-------+

    | | 1 | a |

    | +------+-------+

    INSERT INTO demo VALUES (2, 'b');

    | SELECT * FROM demo;

    | +------+-------+

    | | id | value |

    | +------+-------+

    | | 1 | a |

    | +------+-------+

    COMMIT;

    | SELECT * FROM demo;

    | +------+-------+

    | | id | value |

    | +------+-------+

    | | 1 | a |

    | +------+-------+

    | UPDATE t_bitfly SET value='z';

    | Rows matched: 2 Changed: 2 Warnings: 0

    | SELECT * FROM demo;

    | +------+-------+

    | | id | value |

    | +------+-------+

    | | 1 | z |

    | | 2 | z |

    | +------+-------+

    | (怎么多出来一行)

    本事务中第一次读取出一行,做了一次更新后,另一个事务里提交的数据就出现了。也可以看做是一种幻读。

    当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-key locks可以避免幻读。

    4、在可重复读级别出现前后数据不一致现象

    Session A

    Session B

    START TRANSACTION;

    START TRANSACTION;

    | SELECT * FROM demo;

    | +----+-------+

    | | id | value |

    | +----+-------+

    | | 1 | a |

    | +----+-------+

    INSERT INTO demo VALUES (2, 'b');

    COMMIT;

    | SELECT * FROM demo;

    | +----+-------+

    | | id | value |

    | +----+-------+

    | | 1 | a |

    | +----+-------+

    | SELECT * FROM demo LOCK IN SHARE MODE;

    | +----+-------+

    | | id | value |

    | +----+-------+

    | | 1 | a |

    | | 2 | b |

    | +----+-------+

    | SELECT * FROM demo FOR UPDATE;

    | +----+-------+

    | | id | value |

    | +----+-------+

    | | 1 | a |

    | | 2 | b |

    | +----+-------+

    | SELECT * FROM demo;

    | +----+-------+

    | | id | value |

    | +----+-------+

    | | 1 | a |

    | +----+-------+

    如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。

    本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。

    六、总结

            InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是next-key locks。

    四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用

    更多相关内容
  • 数据库隔离级别有四种,应用《高性能mysql》一书中的说明:然后说说修改事务隔离级别的方法:1.全局修改,修改mysql.ini配置文件,在最后加上#可选参数有:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,...
  • 数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏...
  • 查看事务隔离级别 在 MySQL 中,可以通过show variables like ‘%tx_isolation%’或select @@tx_isolation;语句来查看当前事务隔离级别。 查看当前事务隔离级别的 SQL 语句和运行结果如下: mysql> show variables ...
  • 锁机制是用来保证在并发情况下数据的准确性,而要保证数据准确通常需要事务的支持,而mysql存储引擎innodb是通过锁机制来巧妙地实现事务的隔离特性中的4种隔离级别。 事务ACID特性,其中I代表隔离性(Isolation)。...
  • 事务隔离级别:一个事务对数据库的修改与并行的另一个事务的隔离程度
  • 上面我们只是简单的使用事务,这里将讨论Spring事务机制中最重要的两个配置项,即隔离级别与传播行为。毫无疑问本节内容是本章的核心内容,也是互联网企业最关注的内容之一,因此他十分重要,值得花费大的篇幅去讨论...
  •  事务的四个属性:原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。  1.原子性(Atomic)  重要的原则,也是容易理解的原则。被事务管理的所有方法,要么一起被提交,...
  • 隔离级别: 隔离性其实比想象的要复杂。 在SQL标准中定义了四种隔离级别, 每一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。 下面...
  • oracle 事务隔离级别 事务不同引发的状况: 脏读(Dirty reads) 一个事务读取另一个事务尚未提交的修改时,产生脏读 很多数据库允许脏读以避免排它锁的竞争。 不可重复读(Nonrepeatable reads) 同一查询在同一事务中...
  • 说说事务的隔离级别吧? 老实说,事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!因为他们对可重复读(Repeatable Read)和串行化...
  • mysql的四个事务隔离级别 之前在网上查询mysql事务隔离相关资料,一直只是脑子里有一个印象,久而久之还是会忘记,忘记后又要到网上查找相关资料,但是没实践过就对mysql事务隔离级别理解不是特别的深入,现在自己...
  • (详细)事务特性和隔离级别 事务特性和隔离级别 一.数据库事务特性 数据库事务transaction是组合一系列数据库操作(增删查改)作为一个逻辑处理单元的操作。单元内存ACID四大特性。 原子性(Atomicity):一个事务...
  • (3)隔离性:代表的是一个事务执行的过程当中,不应该受到其他事务的干扰。 (4)持久性:代表一个事务执行完成后数据就持久到数据库当中(提交或回滚)。 3、如果我们不考虑事务的隔离性,就会引发一些安全性的...
  • 查询:默认事务隔离级别 mysql> select @@tx_isolation;当前会话的默认事务隔离级别 mysql> select @@session.tx_isolation;当前会话的默认事务隔离级别 mysql> select @@global.tx_isolation;全局的事务隔离级别
  • 好久没碰数据库了,只是想起自己当时在搞数据库的时候在事务隔离级别这块老是卡,似懂非懂的。现在想把这块整理出来,尽量用最简洁的语言描述出来,供新人参考。 首先创建一个表 account。创建表的过程略过(由于 ...
  • 在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。数据库事务的隔离级别有4个,下面话不多说了,来一起看看详细的介绍吧。 数据库事务有四种隔离级别: 未提交读(Read Uncommitted):允许脏...
  • 主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 这篇数据库教程SQLServer 事务的隔离级别,教程操作方法:  数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。  更新丢失(Lost update)  两个事务都同时更新一行数据...
  • 隔离性可以通过不一样的配置达到不一样的隔离级别。 持久性(Durability):持久性指对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。 2. 隔离级别 Serializable(串行化):可...
  • MySQL的四种事务隔离级别:Read-uncommitted、Read-committed、Repeatable-read、Seriailizable,相信大家都清楚各自异同,不清楚的朋友可以查看另外一篇技术文章:MySQL_InnoDB之事务与锁详解。但是对于第二类、第...
  • 今天小编就为大家分享一篇关于数据库的四种隔离级别,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 4种事务的隔离级别

    2018-08-30 08:21:13
    • InnoDB实现了SQL92标准中的四种隔离级别 (1)读未提交:select不加锁,可能出现读脏; (2)读提交(RC):普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读; (3)可重复读(RR):普通...
  • 今天小编就为大家分享一篇关于Spring中的事务隔离级别的介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 检查当前默认的隔离级别别,可以发现都是 Repeatable Read –可重复读-(在当前事务内,重复读取第一次读取过的数据就叫可重复读。) 3、修改A端的隔离级别为readuncommitted –读未提交。意思是可以读取别人没有...
  • MYSQL标准定义了4类隔离级别,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低的隔离级一般支持更高的并发处理,并拥有更低的系统开销。下面通过这篇文章我们来一起深入理解Mysql中的四种隔离级别
  • GBase 8s隔离级别

    2021-12-30 10:24:24
    GBase 8s隔离级别
  • Spring事务的传播特性和隔离级别 事务的几种传播特性详细介绍
  • 本篇文章给大家详细介绍了一下关于Mysql隔离级别、锁与MVCC的相关知识,有这方面兴趣的朋友参考下。
  • 主要介绍了Spring事务隔离级别简介及实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 253,533
精华内容 101,413
关键字:

隔离级别