zoukankan      html  css  js  c++  java
  • Mysql隔离级别 sql示例理解

     

    前言

    事务要解决的是多线程并发修改数据库的问题。Mysql innodb 引擎支持事务。类似 Java 中的各种锁,例如乐观锁(CAS),读写锁,悲观锁。事务也有很多级别。

    每个隔离级别要解决的问题都是不同的。

    一张表格来看看  Spring 事务每个隔离级别能够解决的问题。。

    再说说脏读,不可重复读,幻读的解释。

    脏读场景:       1.事务 A 读取数据   2.事务 B 修改数据(未提交)    3.事务 A 读取数据已和第一次读的不同

    不可重复读场景: 1.事务 A 读取数据   2.事务 B 修改数据(提交)   3.事务 A 读取数据已和第一次读的不同

    幻读场景:       1.事务 A 读取数据  2.事务 B 新增数据       3.事务 A 再次读取数据已和第一次不同

    再解释一下 4 个隔离级别:

    1. 未提交读:表示另一个事务修改了数据,还没有提交,这个事务就可以读到了。
    2. 已提交读:表示另一个事务修改了数据,同时提交了,这个事务就可以读到了,如果没提交,就读不到。
    3. 可重复读:表示另一个事务即使修改了数据(已提交),这个事务也是看不到的,因此这个事务每次读到的数据都是一样的。这叫可重复读。
    4. 可串行化:可以想象成 Java 语言的锁。一个个执行。毫无并发性。性能令人发指。

    其中关于可重复读需要解释一下在 mysql 场景下的幻读问题,按照标准,可重复读应该会导致幻读,但 mysql 如果在一个事务中,第二次读取的数据使用的是第一次的结果,因此不会产生幻读。

    关于默认的级别,很多文章说是 “已提交读”,但经过详细的测试,应该是可重复读。

    因此,大部分时候,使用默认的级别,就能得到和串行化相同的目的。而串行的成本则是非常的高昂,类似悲观锁。还有一点,mysql 的事务是借助行锁来实现的。

    above from : http://thinkinjava.cn/2018/05/Spring-%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1-%E4%B8%80-%E9%9A%94%E7%A6%BB%E5%B1%9E%E6%80%A7/

    under from: www.jianshu.com/p/db334404d909

    一、Mysql的四个隔离级别

    预备工作:

    • 先创建一个test数据库及account表

    create database test;
    use test;
    create table account(
       id int not null,
       balance float not null,
       PRIMARY KEY ( id)
    )
    INSERT INTO table(id,balance)
    VALUES (1,1000);
    INSERT INTO table(id,balance)
    VALUES (2,1000);
     

    开启两个控制台窗口,当做两个用户(A和B)

    1.1.  read unCommited (未提交读)

    也即RU,在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

    A用户操作如下:

    set session transaction isolation level read uncommitted;
    start transaction;
    select * from account;

    结果如下:

    id    balance

    1    1000

    2    1000

    B用户操作如下:

    set session transaction isolation level read uncommitted;
    start transaction;
    update account set balance=balance+200 where id = 1;

    随后在A用户终端中查询数据,结果如下:

    id    balance

    1    1200

    2    1000

     可以看到B用户并未提交事务,但是A用户却能读到未提交的数据,这就是脏读

    1.2.  read commited (提交读)

    即RC,大多数数据库系统为了避免 脏读 采用的默认隔离级别都是read commited(但MySQL不是,Mysql的默认隔离级别是repeatable read )

    READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能”看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。

    这个级别有时候叫做不可重复读(nonrepeatble read),因为两次执行同样的查询,可能会得到不一样的结果。以例子说明:

    我们将用户B所在的会话当前事务隔离级别设置为read commited。

    set session transaction isolation level read committed;

    在A所在的会话中执行

    update account set balance=balance-200 where id = 1;

    在B用户的会话中查询:

    select * from account;

    结果如下:

    id    balance

    1    1000

    2    1000

    发现数据没有变,还是1000,说明可以避免脏读了。


    接着A用户会话中将事务提交:

    commit;

    再次在B中查询,结果如下:

    id    balance

    1      800

    2    1000

    可以看到,B用户读取到了A用户提交的数据。这么做有什么问题么?那就是我们在会话B同一个事务中,读取到两次不同的结果。这就造成了不可重复读,就是两次读取的结果不同。

    1.3 . repeatable read(可重复读)

    REPEATABLE READ解决了脏读的问题。该隔离级别保证了在同一个事务中多次读取同样记录结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。

    所谓 幻读指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)

    Mysql的RR是由“行排它锁+MVCC”一起实现的。

    我们将用户B所在的会话当前事务隔离级别设置为repeatable read。

    set session transaction isolation level repeatable read;
    start transaction;

    接着在B中查询数据:

    id    balance

    1      1000

    2      1000

    两条。
    然后我们到A用户会话中插入一条数据:

    insert into account(id,balance) value(3,1000);

    在A中查看是否添加成功:

    ————————————————————————

    insert into account(id, balance) value (3, 1000);

     select * from account;

    id    balance

    1      1000

    2      1000

    3      1000

    ————————————————————————

    成功,有三条数据。
    回到用户B会话中,再次查询:

    id    balance

    1      1000

    2      1000

    发现没有变还是两条。这时,用户B想插入一条id=3,balance=1000的数据:

    insert into account(id,balance) value(3,1000);

    会报错:

    ————————————————————————————————

      mysql> insert into account(id,balance) value(3,1000);

      ERROR 1062(23000): Duplicate entry '3' for key 'primary'

      mysql>

    ————————————————————————————————

    说是主键重复了,可是B用户刚刚查询并没有id=3的记录。这就是幻读现象。
    我的理解是:不可重复读指的是update操作,而幻读指的是insert或delete操作。

    1.4 SERIALIZABLE(串行化)

    SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取每一行数据都加锁,所以可能导致大量的超时和锁争用问题。

    实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

  • 相关阅读:
    浅谈Java中的深拷贝和浅拷贝(转载)
    浅析Java中的final关键字
    Java内部类详解
    那些年震撼我们心灵的音乐
    深入理解Java的接口和抽象类
    Java:类与继承
    Java中的static关键字解析
    Java垃圾回收机制
    java 字节流和字符流的区别 转载
    Java 输入输出流 转载
  • 原文地址:https://www.cnblogs.com/hahajava/p/9812940.html
Copyright © 2011-2022 走看看