当前位置 博文首页 > A_art_xiang的博客:MySQL事务隔离级别详解

    A_art_xiang的博客:MySQL事务隔离级别详解

    作者:[db:作者] 时间:2021-07-14 10:00

    目录

    写在前面

    先贴出一张汇总图

    然后开始解释一些名词:

    好了,洗脑部分结束了,开始认真的分析四种隔离级别到底是什么意思

    读未提交(Read uncommitted)

    读已提交(Read committed)

    可重复读(Repeatable read)

    串行化(Serializable )

    总结:


    写在前面

    ? ? 相信工作中使用数据库的小伙伴,经常会听到大佬们提起“数据库的事务隔离级别”。

    ? ? 甚至在面试中也经常会被提问到,每次问到这个问题,虽然大体能够明白是什么意思,但是还是迷迷糊糊一知半解。

    ? ? 今天,就让你彻底搞懂,什么是“MySQL事务隔离级别”!

    ? ? SQL标准中支持4种事务隔离级别,READ_UNCOMMITTED(读未提交),READ_COMMITTED(读已提交),REPEATABLE_READ(可重复读),SERIALIZABLE(串行读),MySQL innodb引擎支持全部这4种事务隔离级别。

    先贴出一张汇总图

    隔离级别脏读(Dirty Read)不可重复读(NonRepeatable Read)幻读(Phantom Read)
    读未提交(Read uncommitted)可能可能可能
    读已提交(Read committed)不可能可能可能
    可重复读(Repeatable read)不可能不可能可能
    串行化(Serializable )不可能不可能不可能

    ?

    ?

    ?

    ?

    ?

    ?

    然后开始解释一些名词:

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

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

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

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

    脏读:?脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

    不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

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

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

    好了,洗脑部分结束了,开始认真的分析四种隔离级别到底是什么意思

    -- 初始建表语句贴这里了~
    CREATE TABLE `student`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20),
      `age` int(11),
      `address` varchar(25),
      `crtdate` timestamp,
      PRIMARY KEY (`id`) USING BTREE
    );
     
    insert into student(name,age,address,crtdate) values('zhangsan', 15, 'qingdao', now());
    insert into student(name,age,address,crtdate) values('lisi', 17, 'jinan', now());
    insert into student(name,age,address,crtdate) values('wangwu', 18, 'weifang', now());

    我使用的mysql版本为:5.5.40。

    查看一下mysql默认的事务隔离级别-是可重复读(Repeatable read)

    show variables like 'tx_isolation';

    修改隔离级别的语句是: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} 这四种,不区分大小写

    读未提交(Read uncommitted)

    设置隔离级别为读未提交(Read uncommitted):

    set global transaction isolation level READ UNCOMMITTED;

    注意:使用global设置全局的事务隔离级别,只会在新打开的回话生效。

    show variables like 'tx_isolation';(注意需要新开一个会话)

    开始验证:

    ? ? 我们可以得出结论,MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。读未提交(Read uncommitted)隔离级别下,是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。但有利就有弊,这基本上就相当于裸奔,所以它连脏读的问题都没办法解决。

    ? ??读未提交(Read uncommitted)隔离级别,其他事务中修改过但是尚未提交的数据也会被查询到,也就是会造成脏读

    读已提交(Read committed)

    设置隔离级别为读已提交(Read committed):

    set global transaction isolation level READ COMMITTED;

    show variables like 'tx_isolation';(注意需要新开一个会话)

    开始验证:

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

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

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

    可重复读(Repeatable read)

    设置隔离级别为可重复读(Repeatable read):

    set global transaction isolation level REPEATABLE READ;

    show variables like 'tx_isolation';(注意需要新开一个会话)

    开始验证:

    ? ??可重复读(Repeatable read)可以解决update的幻读现象,以下是update的幻读验证:

    (这里不提insert,因为两个事务同时insert同一个条件的数据,会造成数据锁定,这里不提)


    ?

    串行化(Serializable )

    ? ? 串行化(Serializable )是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束。

    总结:

    四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境可重复读(Repeatable read) 可以用.记住4个隔离级别的特点(上面的例子);

    ?

    ?

    ?

    ?

    cs