事务的隔离级别
事务的隔离级别
Read Uncommited(未提交读) | Read Commited (提交读) | Repeatable Read (可重复读) | Serializable (可串行化) | |
脏读 | Possible | Not Possible | Not Possible | Not Possible |
不可重复读 | Possible | Possible | Not Possible | Not Possible |
幻行 | Possible | Possible | Possible | Not Possible |
(从上往下依次增加,较低的隔离级别通常支持较高的并发,系统的开销也更低。因为隔离级别是通过影响锁策略和其他机制去实现效果的,上锁是需要开销的)
事务隔离是数据库处理的基础之一。隔离级别是在多个事务同时进行更改和执行查询时,对结果的性能和可靠性,一致性和可重复性之间的平衡进行微调的设置。
用户可以使用SET TRANSACTION语句更改单个会话或所有后续连接的隔离级别。
InnoDB使用不同的锁策略支持每个隔离级别。您可以使用默认的REPEATABLE READ级别强制执行高度一致性操作。或者可以放宽一致性规则,即READ COMMITTED或READ UNCOMMITTED。在这种情况下,能最小化锁的开销,但是会降低精确的一致性。 SERIALIZABLE强制执行比REPEATABLE READ更严格的规则,主要用于特殊情况,例如XA事务以及排除并发和死锁问题。
以下列表描述了MySQL如何支持不同的事务级别。
该列表从最常用的级别到最少使用的级别。
可重复读 REPEATABLE READ
这是InnoDB的默认隔离级别。在同一事务中,读取由第一次读取建立的快照。这意味着如果在同一个事务中发出几个纯的(非锁定的)SELECT语句,重复执行同样的查询结果相同。
- 对于具有唯一搜索条件的唯一索引,InnoDB只锁定找到的索引记录,而不是锁定之前的间隔。
- 对于其他搜索条件,InnoDB锁定扫描的索引范围,使用gap lock或next-key lock阻止其他会话的插入到范围覆盖的间隙
InnoDB会为您的事务提供一个时间点,根据该时间点查询数据库。如果另一个删除一行的事务在您的时间点之后提交,则不会将该行视为已被删除。插入和更新也是如此。
例子:
Session A Session B
SET autocommit=0; SET autocommit=0;
time
| SELECT * FROM t;
| empty set
| INSERT INTO t VALUES (1, 2);
|
v SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
---------------------
| 1 | 2 |
---------------------
如果要查看数据库的“最新”状态,请使用READ COMMITTED隔离级别
提交读 READ COMMITTED
即使在同一事务中,每次查询都会读取自己的新快照。
UPDATE语句和DELETE语句,InnoDB只锁定索引记录,而不锁定之前的间隔。允许在锁定记录旁边自由插入新记录。
因为禁用了gap lock,所以可能会出现“幻行”,因为其他事务可以将新行插入到间隙中。
- 对于UPDATE或DELETE语句,InnoDB仅为更新或删除的行保存锁。在MySQL评估WHERE条件之后,释放对不匹配行的记录锁定。大大降低了死锁的可能性。
例子:
插入数据:
CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;
第一个update语句:
SET autocommit = 0;
UPDATE t SET b = 5 WHERE b = 3;
第二个:
SET autocommit = 0;
UPDATE t SET b = 4 WHERE b = 2;
当使用默认的REPEATABLE READ隔离级别时,第一个UPDATE会把5条记录全部锁上X锁:
x-lock(1,2); retain x-lock
x-lock(2,3); update(2,3) to (2,5); retain x-lock
x-lock(3,2); retain x-lock
x-lock(4,3); update(4,3) to (4,5); retain x-lock
x-lock(5,2); retain x-lock
第二个update在这种情况下必须等待第一个update执行完毕,并且释放锁之后,才能获取它的锁。
如果是 READ COMMITTED 级别,第一个UPDATE获取x锁之后,会释放那些不修改的行:
x-lock(1,2); unlock(1,2)
x-lock(2,3); update(2,3) to (2,5); retain x-lock
x-lock(3,2); unlock(3,2)
x-lock(4,3); update(4,3) to (4,5); retain x-lock
x-lock(5,2); unlock(5,2)
脏读 READ UNCOMMITTED
事务可以读取未提交的数据,也称脏读。非常可怕,基本不会使用这一级别。
可串行化 Serializable
最高的隔离级别,它强制事务串行执行。会导致大量的超时和锁争用问题。虽然数据一致性好,但是并发能力很弱,一般也很少使用。