脏读
脏读,是指在不同的事务下,当前事务可以读到其他事物未提交的数据,即读到脏数据。
举个简单的例子,事务A对数据 num
进行了更新,使其从1变为2,随后,事务B读取了 num
,读取到的值为2,由于某种原因,事务A回滚了当前事务,此时,事务B读到的,就是事务A没有提交的数据,这就是脏读。
事务A | 事务B | 结果 |
---|---|---|
SELECT t.num FROM t WHERE t.id = 1 | 1 | |
BEGIN | ||
UPDATE t SET t.num = 2 WHERE t.id = 1 | ||
SELECT t.num FROM t WHERE t.id = 1 | 2 | |
ROLLBACK | ||
SELECT t.num FROM t WHERE t.id = 1 | 1 |
不可重复读
不可重复读,是指在一个事务中多次读取同一数据集合。在这个事务没有结束时,在另一个事务中同样访问了这个数据集合,并且进行了一些DML操作。在第一个事务中,两次读取分别在另一个事务进行DML操作之前和之后,两次读取的数据不一致,此时这种情况被称为不可重复读
举个例子,事务A第一次读取数据 num
,此时为1,随后事务B将 num
的值修改为2,事务A再次读取 num
,读取到的值为2,此时出现了两次读取不一致的情况,这就是不可重复读。
事务A | 事务B | 结果 |
---|---|---|
SELECT t.num FROM t WHERE t.id = 1 | 1 | |
UPDATE t SET t.num = 2 WHERE t.id = 1 | ||
SELECT t.num FROM t WHERE t.id = 1 | 2 |
幻读
幻读有点不好描述,直接举个例子吧,事务A需要查询两次同一范围的数据,比如数据量 count(*)
,在两次查询之间,事务B插入了一条数据,导致事务A两次查询的结果不一致,后一次读到了前一次没有看到的行,就像幻觉那样,这种情况成为幻读。幻读和不可重读读类似,但是幻读强调的是集合的增江,而不是单条数据的更新。
事务A | 事务B | 结果 |
---|---|---|
SELECT COUNT(*) FROM t | 3 | |
INSERT INTO t (num) VALUES (1) | ||
SELECT COUNT(*) FROM t | 4 |
四种隔离级别
读未提交
读未提交(Read Uncommitted),是最低的隔离级别,所有的事务都可以看到其他未提交的事务的执行结果。不能解决脏读,不可可重复读,幻读,所以很少应用于实际项目。
读已提交
读已提交(Read Committed), 在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。可以防止脏读,但是不能解决可重复读和幻读的问题。
可重复读
可重复读(Repeatable Read),MySQL默认的隔离级别。在该隔离级别下,一个事务多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的。可以防止脏读、不可重复读、不过还是会出现幻读。
串行化
串行化(Serializable),这是最高的隔离级别。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。在这个级别,可以解决上面提到的所有并发问题,但可能导致大量的超时现象和锁竞争,通常不会用这个隔离级别。