谈到事务隔离级别,我们先回顾一下事务的基本要素。
一、事务的基本要素(ACID):
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体。
2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账200,A扣了200,B增加了200。
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
二、Mysql的事务隔离级别,如下表(Mysql默认是repeatable-read级别):
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
read-uncommitted(读未提交) | 是 | 是 | 是 |
read-committed(不可重复读) | 否 | 是 | 是 |
repeatable-read(可重复读) | 否 | 否 | 是 |
serializable(串行化) | 否 | 否 | 否 |
三、事务并发问题:
1.脏读:A事务读取了B事务update的数据,随后B事务执行了rollback,那么A事务读取的是脏数据;
2.不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据执行了update并未执行commit,导致事务A多次读取同一数据时,结果 不一致(不可重复读侧重于update操作,解决方案锁住当前行)。
3.幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读(幻读侧重于insert、delete操作,解决方案锁住当前表)。
查看MySQL事务隔离级别:SELECT @@tx_isolation;
设置MySQL事务隔离级别(GLOBAL.全局 SESSION.当前会话):
读未提交:set session transaction isolation level read uncommitted;
不可重复读:set session transaction isolation level read committed;
可重复读:set session transaction isolation level repeatable read;
串行化:set session transaction isolation level serializable;
三、举例说明各个事务隔离级别的情况:
有student表,详情如下:
1.读未提交(read uncommitted):
a.1 打开A会话,首先设置当前会话的事务隔离级别为读未提交:set SESSION transaction isolation LEVEL read committed;
a.2 查看事务隔离级别:SELECT @@tx_isolation;
a.3 关闭事务自动提交:SET @@autocommit=0; 查看事务自动提交状态:SHOW VARIABLES LIKE '%autocommit%';
a.4 开启事务:START TRANSACTION; 执行更新:UPDATE student SET age=age-20 WHERE id=5;
b.1 打开B会话,首先设置当前会话的事务隔离级别为读未提交:set SESSION transaction isolation LEVEL read committed;
b.2 查看事务隔离级别:SELECT @@tx_isolation;
b.3 关闭事务自动提交:SET @@autocommit=0; 查看事务自动提交状态:SHOW VARIABLES LIKE '%autocommit%';
b.4 开启事务:START TRANSACTION; 执行查询:SELECT * FROM student;
查询结果:
这时我们会发现,B事务能够得到A事务update未commit的结果,当A事务进行rollback时,B事务读取到的是脏数据。
2.不可重复读(read committed):设置当前会话的事务隔离级别为不可重复读,步骤同上;这时我们发现B事务读到id=5的数据age依然是120,解决了脏读的问题;当A事务执行commit,B事务前后查询的结果不一致,即产生了不可重复读的问题;
commit之前结果
commit之后的结果:
3.可重复读(repeatable read):设置当前会话的事务隔离级别为可重复读,步骤如下:
A会话:
set SESSION transaction isolation LEVEL repeatable read;
SELECT @@tx_isolation;
SET @@autocommit=0;
SHOW VARIABLES LIKE '%autocommit%';
START TRANSACTION;
SELECT * FROM student;
B会话:
set SESSION transaction isolation LEVEL repeatable read;
SELECT @@tx_isolation;
SET @@autocommit=0;
SHOW VARIABLES LIKE '%autocommit%';
START TRANSACTION;
UPDATE student SET age=age-20 WHERE id=5;
SELECT * FROM student;
commit;
接着在A会话执行:SELECT * FROM student;查询结果age=80;在客户端继续执行UPDATE student SET age=age-20 WHERE id=5;这时age=60,数据一致性没有被破坏;可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本);
重新在B会话,插入一条新数据后提交;在A会话查询表student的所有记录,没有 查出 新增数据,所以没有出现幻读(想要查询到B会话insert的数据,A事务要先commit当前事务)。
4.串行化(serializable):mysql中事务隔离级别为serializable时只要有一个事务未提交,就会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。
另外事务执行会产生相应的锁:
1.事务隔离级别为读提交时,写数据时锁住相应的行;
2.事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读;
3.事务隔离级别为串行化时,读写数据都会锁住整张表;
4.隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。