MySQL【01】事务的隔离级别、脏读、不可重复读和幻读

1、事务

事务,表示一组对数据库的操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的。经典问题,转账问题。InnoDB支持事务,MyISAM不支持事务。

2、事务控制语句

-- 0. 开启事务
START TRANSACTION;
-- 发现执行没有问题,提交事务
COMMIT;
-- 发现出问题了,回滚事务
ROLLBACK;

3、提交方式

  • 自动提交
    MySQL就是默认自动提交的, 一条DML(增删改)语句会自动提交一次事务。
  • 手动提交:
    Oracle 数据库默认手动提交事务;
    需要先开启事务START TRANSACTION,再提交COMMIT
  • 修改事务的默认提交方式:
    查看事务的默认提交方式:SELECT @@autocommit;-- 1 代表自动提交 ; 0 代表手动提交
    修改默认提交方式:set @@autocommit = 0;

4、事务的四大特征【ACID】

  1. Atomicity原子性:一个事务是不可分割的最小操作单位,要么同时成功,要么同时失败。
  2. Consistency一致性:数据库总是从一致性的一个状态转移到另一个一致性的状态。
    事务操作前后,数据总量不变,数据库的完整性没有被破坏。
  3. Isolation 隔离性:通常,一个事务所做的修改在最终提交之前,对其他的事务是不可见的。
  4. Durability持久性:当事务提交或回滚后,数据库会持久化的保存数据。

5、事务的隔离级别

  • 概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。

4.1、存在问题:

  1. 脏读 dirty read:一个事务,读取到另一个事务中没有提交的数据
  2. 不可重复读 non-repeatable read:一个事务内,多次读取到的数据不一致
  3. 幻读 phantom read : 幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

4.2、隔离级别:

  • 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
  • 查询数据库隔离级别:select @@tx_isolation;
  • 数据库设置隔离级别: set global transaction isolation level 级别字符串;
  1. read uncommitted:读未提交
    一个事务还没提交时,它做的变更就能被别的事务看到。
    产生的问题:脏读、不可重复读、幻读
  2. read committed:读提交 (Oracle)
    一个事务提交之后,它做的变更才会被其他事务看到。
    产生的问题:不可重复读、幻读
  3. repeatable read:可重复读 (MySQL默认)
    一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
    产生的问题:幻读
  4. serializable:串行化
    顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
    可以解决所有的问题

6、问题演示:

准备一个account表包含字段id、name、balabce


6.1、脏读

事务A读取到了事务B已经修改但是尚未提交的数据,这样不符合一致性。
首先设置隔离级别为 READ UNCOMMITTED: SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

顺序 事务 A命令 balance值 事务 B命令 balance值
1 BEGIN; BEGIN;
2 SELECT balance FROM account WHERE id = 1; 10
3 UPDATE account SET balance = 0 WHERE id = 1;
4 SELECT balance FROM account WHERE id = 1; 0
5 SELECT balance FROM account WHERE id = 1; 0
8 COMMIT; COMMIT;

6.2、 不可重复读

事务A读取到事务B中已经提交的数据,不符合隔离性
首先设置隔离级别为 READ COMMITTED:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
注意5、6、7步可以发现在事务B还没有提交的时候,事务 A读到的值没有变化,所以隔离级别READ COMMITTED解决了脏读的问题。但是事务B提交之后,事务 A读取到的值是事务B更新的值,产生了不可重复读问题。

顺序 事务 A命令 balance值 事务 B命令 balance值
1 BEGIN; BEGIN;
2 SELECT balance FROM account WHERE id = 1; 10
3 UPDATE account SET balance = 0 WHERE id = 1;
4 SELECT balance FROM account WHERE id = 1; 0
5 SELECT balance FROM account WHERE id = 1; 10
6 COMMIT;
7 SELECT balance FROM account WHERE id = 1; 0
8 COMMIT;

6.3、 repeatable read 隔离级别解决了脏读、 不可重复读

首先设置隔离级别为 repeatable read:SET GLOBAL TRANSACTION ISOLATION LEVEL repeatable read;

顺序 事务 A命令 balance值 事务 B命令 balance值
1 BEGIN; BEGIN;
2 SELECT balance FROM account WHERE id = 1; 10
3 UPDATE account SET balance = 0 WHERE id = 1;
4 SELECT balance FROM account WHERE id = 1; 0
5 SELECT balance FROM account WHERE id = 1; 10
6 COMMIT;
7 SELECT balance FROM account WHERE id = 1; 10
8 COMMIT;

6.4、 repeatable read 隔离级别的幻读问题,间隙锁,MVCC

在repeatable read 隔离级别下
普通的查询语句是一致性读,会根据readview和事务id追溯 版本链
带lock in share mode的查询语句,是当前读,直接读取最新值
对于普通的查询语句,因为在RR隔离级别下,由于MVCC机制,只生成一次readview,不管别的会话是否插入新的数据,普通的查询语句都是看不到的;
对于上锁的查询语句,因为在RR隔离级别下,由于间隙锁的存在,所以查询上锁的行之间的间隙是被锁住的,不允许其他会话 往这个间隙中插入一个记录,如果插入会直接阻塞。

你可能感兴趣的:(MySQL【01】事务的隔离级别、脏读、不可重复读和幻读)