赞
踩
事务隔离是数据库处理的基础之一。隔离是缩写ACID中的I;当多个事务同时进行更改和查询时,隔离级别是微调性能与可靠性、一致性和结果再现性之间平衡的设置。
InnoDB提供SQL:1992标准描述的所有四种事务隔离级别: READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
和SERIALIZABLE
。
InnoDB 的默认隔离级别是 REPEATABLE READ
。
用户可以使用 SET TRANSACTION
语句更改单个会话或所有后续连接的隔离级别。
要为所有连接设置服务器的默认隔离级别,可以在命令行或选项文件中使用 --transaction-isolation
选项。
InnoDB使用不同的锁定策略支持这里描述的每一种事务隔离级别。
你可以使用默认的REPEATABLE READ
级别来执行高度的一致性,用于对ACID合规性非常重要的关键数据的操作。
在批量报告等情况下,精确的一致性和结果的可重复性没有最大限度减少锁定开销那么重要,这时可以使用 READ COMMITTED
或甚至 READ UNCOMMITTED
放宽一致性规则。
SERIALIZABLE
执行比 REPEATABLE READ
更严格的规则,主要用于特殊情况,如 XA 事务以及并发和死锁问题的故障诊断。
下面的列表描述了 MySQL 如何支持不同的事务级别。该列表从最常用的级别到最不常用的级别依次排列。
这是 InnoDB 的默认隔离级别。同一事务中的一致读取会读取第一次读取建立的快照。这意味着,如果在同一事务中发出多条普通(非锁定)SELECT 语句,这些 SELECT 语句之间也是一致的。
对于锁定读取(带有 FOR UPDATE 或 FOR SHARE 的 SELECT)、UPDATE 和 DELETE 语句,锁定取决于语句是使用带有唯一搜索条件的唯一索引,还是使用范围类型的搜索条件。
即使在同一个事务中,每次一致读取都会设置并读取自己的新快照。
对于锁定读取(带有 FOR UPDATE 或 FOR SHARE 的 SELECT)、UPDATE 语句和 DELETE 语句,InnoDB 只锁定索引记录,而不锁定它们之前的间隙,因此允许在锁定记录旁边自由插入新记录。间隙锁定仅用于外键约束检查和重复键检查。
由于间隙锁定被禁用,可能会出现幽灵行问题,因为其他会话可以在间隙中插入新记录。
READ COMMITTED
隔离级别仅支持基于行的二进制日志记录。如果使用带有 binlog\_format=MIXED
的 READ COMMITTED
,服务器会自动使用基于行的日志记录。
使用 READ COMMITTED
还会产生其他影响:
对于 UPDATE 或 DELETE 语句,InnoDB 只为其更新或删除的记录加锁。非匹配记录的记录锁会在MySQL评估完WHERE条件后释放。这大大降低了死锁发生的概率,但死锁仍有可能发生。
对于UPDATE语句,如果记录已经被锁定,InnoDB会执行 "半一致 "读取,向MySQL返回最新提交的版本,以便MySQL确定记录是否与UPDATE的WHERE条件相匹配。如果符合(必须更新),MySQL 会再次读取记录,这一次 InnoDB 要么锁定它,要么等待锁定。
可以这样创建和填充表:
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:
# Session A
START TRANSACTION;
UPDATE t SET b = 5 WHERE b = 3;
还假设第二个会话在第一个会话的语句之后执行这些语句,从而执行 UPDATE:
# Session B
UPDATE t SET b = 4 WHERE b = 2;
当 InnoDB 执行每次 UPDATE 时,它首先会获取每条记录的独占锁,然后决定是否修改它。如果InnoDB不修改记录,就会释放锁。否则,InnoDB 会保留锁,直到事务结束。这对事务处理的影响如下。
在使用默认的 REPEATABLE READ
隔离级别时,第一个 UPDATE 会在读取的每一条记录上获取一个 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 提交或回滚后才会继续:
x-lock(1,2); block and wait for first UPDATE to commit or roll back
如果使用 READ COMMITTED 代替,则第一次 UPDATE 会为读取的每一条记录获取 x 锁,并为未修改的记录释放 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)
对于第二次 UPDATE,InnoDB 会进行 "半一致 "读取,将读取到的每条记录的最新提交版本返回给 MySQL,以便 MySQL 确定该记录是否与 UPDATE 的 WHERE 条件相匹配:
x-lock(1,2); update(1,2) to (1,4); retain x-lock
x-lock(2,3); unlock(2,3)
x-lock(3,2); update(3,2) to (3,4); retain x-lock
x-lock(4,3); unlock(4,3)
x-lock(5,2); update(5,2) to (5,4); retain x-lock
但是,如果 WHERE 条件包含索引列,并且 InnoDB 使用了索引,那么在获取和保留记录锁时,只会考虑索引列。在下面的示例中,第一个 UPDATE 在 b = 2 的每条记录上获取并保留了一个 x 锁。第二个 UPDATE 在试图获取相同记录上的 x 锁时阻塞,因为它也使用了 b 列上定义的索引。
CREATE TABLE t (a INT NOT NULL, b INT, c INT, INDEX (b)) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2,3),(2,2,4);
COMMIT;
# Session A
START TRANSACTION;
UPDATE t SET b = 3 WHERE b = 2 AND c = 3;
# Session B
UPDATE t SET b = 4 WHERE b = 2 AND c = 4;
READ COMMITTED
隔离级别可在启动时设置,也可在运行时更改。在运行时,可以对所有会话进行全局设置,也可以对每个会话进行单独设置。
SELECT 语句是以非锁定方式执行的,但可能会使用记录的早期版本。
因此,使用这种隔离级别,这种读取是不一致的。这也被称为 “脏读”。否则,该隔离级别的工作方式与 READ COMMITTED
类似。
该级别与 REPEATABLE READ
类似,但如果禁用了自动提交,InnoDB 会将所有普通 SELECT 语句隐式转换为 SELECT ... FOR SHARE
。如果启用了自动提交,SELECT 就是它自己的事务。
因此,SELECT 被认为是只读的,如果以一致(无锁)读取的方式执行,则可以序列化,无需阻塞其他事务。(要在其他事务修改了所选行的情况下强制阻塞普通 SELECT,请禁用自动提交)。
从 MySQL 授予表(通过连接列表或子查询)读取数据但不修改数据的 DML 操作不会获取 MySQL 授予表上的读锁,无论隔离级别如何。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。