MySQL 四种隔离级别
我们知道事务的四大特性分别为:
原子性 A(Atomicity)
一致性 C(Consistency)
隔离性 I(Isolation)
持久性 D(Durability)
既然存在隔离性,为什么又有隔离级别。
事务执行过程中不受其他事务影响,这个是事务的隔离性;
但是多事务操作的数据是同一份,就有冲突存在的可能,为了避免这种问题,我们使用隔离级别。
隔离级别应用场景
Read Uncommitted(读取未提交内容) -- 导致:脏读、不可重复读 、幻读or虚读
一个事务读取其他事务未提交的内容,一旦其他事务回滚,就会导致读取到脏数据;
实际场景很少用,因为性能提升不大(可以切换存储引擎例如MyISAM,性能极佳,不支持事务)
Read Committed(读取提交内容) -- 导致:不可重复读 、幻读or虚读
大多数数据库系统的默认隔离级别(但不是MySQL默认的);只能读取其他事务已经提交的数据;
一个事务读取一个数据后,再次读取的时候,该数据可能会被其他事务更新提交,造成两次读取不一致(乐观锁?)。
Repeatable Read(可重读) -- 导致:幻读or虚读
MySQL默认隔离级别,同一个事务在多次读取数据时,会看到同样结果;
读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化) -- 导致:超时、死锁
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
测试隔离级别
创建test表
id | money |
---|---|
1 | 100 |
Read UNCOMMITTED
Session A
set session transaction isolation level read UNCOMMITTED; -- 设置会话隔离级别为读未提交
select @@tx_isolation -- select @@transaction_isolation (mysql版本 8.0 以后)
start TRANSACTION;
Session B
start TRANSACTION;
update test set money = 1001 where id = 1;
Session A
select * from test;
结果:Session A 读取到了Session B未提交的脏数据
Read COMMITTED
Session A
set session transaction isolation level read COMMITTED; -- 设置会话隔离级别为读已提交
select @@tx_isolation
start TRANSACTION;
Session B
start TRANSACTION;
update test set money = 1001 where id = 1;
Session A
select * from test;
Session B
COMMIT;
Session A
select * from test;
结果:Session A 分别读取了Session B提交事务前后两个不同的数据
Repeatable Read (深入了解MVCC)
Session A
set session transaction isolation level Repeatable Read;
select @@tx_isolation;
start TRANSACTION;
Session B
start TRANSACTION;
update test set money = 1001 where id = 1;
Session A
select * from test;
Session B
COMMIT;
Session A
select * from test;
结果:Session A 两次读取结果一致
SERIALIZABLE (锁)
select * from information_schema.innodb_trx; -- 可以使用本语句查询未提交事务的基本信息,只有执行了sql语句的事务,才会存在该表;下文简称事务列表
kill trx_mysql_thread_id(上行语句返回的字段) --强制关闭某个事务
Session A
set session transaction isolation level SERIALIZABLE;
select @@tx_isolation;
start TRANSACTION;
-- 事务列表0条
Session B
start TRANSACTION;
update test set money = 1001 where id = 1;
-- 事务列表1条
Session A
select * from test;
-- 事务列表2条,本事务进入阻塞状态 如下表所示
Session B
COMMIT;
-- 事务列表1 条
-- Session A解除阻塞,获取更新后的结果
Session A
COMMIT;
-- 事务列表0条
trx_id | trx_state | tx_mysql_thread_id | trx_query | trx_isolation_level |
---|---|---|---|---|
4AC | LOCK WAIT | 485 | select * from test | REPEATABLE READ |
4AB | RUNNING | 486 | null | SERIALIZABLE |