创建一个表,并插入几条基础数据
CREATE TABLE `transaction_isolation` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_user_name` (`user_name`) USING BTREE,
KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
INSERT INTO transaction_isolation (`id`, `user_name`, `age`) VALUES (1, 'Ulrica', 10);
INSERT INTO transaction_isolation (`id`, `user_name`, `age`) VALUES (2, 'Adam', 20);
INSERT INTO transaction_isolation (`id`, `user_name`, `age`) VALUES (3, 'William', 18);
INSERT INTO transaction_isolation (`id`, `user_name`, `age`) VALUES (4, 'George', 25);
在为提交读级别,事物中的修改,即使没有提交,对其他事物也都是可见的。食物可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,未提交读不会比其他级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
#未提交读测试窗口1
set session transaction isolation level read uncommitted;
begin;
#Step1-当前数据库数据情况
select * from transaction_isolation;
#Step3-查询到另一个事物未提交的事物
select * from transaction_isolation;
#Step5-再次查询'uncommitted'用户已经消失,证明Step的操作属于脏读
select * from transaction_isolation;
commit;
#未提交读测试窗口2
set session transaction isolation level read uncommitted;
begin;
#Step2-插入一条数据
INSERT INTO transaction_isolation (`user_name`, `age`) VALUES ('uncommitted', 25);
#Step4-回滚当前事物操作
ROLLBACK;
按照上面的sql分别打开两个窗口进行测试,在执行Step3步骤时,会读取到事物2中插入的uncommitted的数据。
当我们把事物2回滚后再次查询,uncommitted的数据已经消失
大多数数据库系统的默认隔离级别都是读已提交(但MySQL不是)。读已提交满足隔离性的简单定义:一个事物开始时,只能“看见”已经提交的事物所做的修改。换句话说,一个事物从开始直到提交之前,所做的任何修改对其他事物都是不可见的。这个级别有时候也叫不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。
#提交读测试窗口1
set session transaction isolation level read committed;
begin;
#Step1-当前数据库数据情况
select * from transaction_isolation;
#Step4-再次执行查询数据发生变化,Adam变为了修改id为2的用户名为Alice
select * from transaction_isolation;
commit;
#提交读测试窗口2
set session transaction isolation level read committed;
begin;
#Step2-修改id为2的用户名为Alice(原为Adam)
update transaction_isolation set user_name = 'Alice' where id = 2;
#Step3-提交当前事物
commit;
窗口1在执行Step-1时查询到的数据情况如下:
当在窗口2修改了数据提交后,再在窗口1执行Step-4时查询到的结果如下:
在窗口1的同一个事物中,两次查询的结果不一致,验证了不可重复读的问题。
可重复读解决了脏读的问题。该级别保证了在同一个事物中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事物在读取某个范围内的记录时,另外一个事物又在该范围内插入了新的记录,当之前的事物再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。可重复读诗MySQL的默认事物隔离级别。
可串行化是最高的隔离级别。它通过强制事物串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,只有非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。