MySQL官方文档14.5.2.1节 — 事务隔离级别译文

原文地址:https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html

14.5.2.1事务隔离级别

事务隔离是数据库处理的基础之一。隔离是首字母缩写ACID的I;隔离级别是在多个事务同时进行更改和执行查询时,对性能和可靠性之间的平衡,结果的一致性和再现性进行微调的设置。

InnoDB提供了SQL:1992标准描述的所有四种事务隔离级别:READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ和SERIALIZABLE。 InnoDB的默认隔离级别是REPEATABLE READ。

用户可以使用SET TRANSACTION语句更改单个会话或所有后续连接的隔离级别。要为所有连接设置服务器的默认隔离级别,请在命令行或选项文件中使用–transaction-isolation选项。有关隔离级别和级别设置语法的详细信息,请参见第13.3.6节“SET TRANSACTION语法”。

InnoDB支持使用不同的锁策略描述每个事务隔离级别。您可以在默认可重复读取级别下强制指定高级别的一致性,以便在ACID合规性非常重要的情况下对关键数据进行操作。或者,在批量执行等情况下,可以放宽一致性规则,即READ COMMITTED或READ UNCOMMITTED,其中精确一致性和可重复结果不如减少锁开销那么重要。SERIALIZABLE执行比REPEATABLE READ更严格的规则,主要用于特殊情况下,如XA事务和解决并发和死锁问题。

以下列表描述了MySQL如何支持不同的事务级别。该列表从最常用的级别到最少使用的级别。

  • REPEATABLE READ
    这是InnoDB的默认隔离级别。在同一事务中的一致读取读取第一次读操作建立的快照。这意味着如果您在同一事务中发出多个纯(非锁定)SELECT语句,则这些SELECT语句在彼此之间也是一致的。请参见第14.5.2.3节“一致性非锁定读取”。

    对于锁定读取(SELECT FOR WITH UPDATE或LOCK IN SHARE MODE),UPDATE和DELETE语句,锁定取决于语句是使用具有唯一搜索条件的唯一索引还是范围类型搜索条件。

    • 对于具有唯一搜索条件的唯一索引,InnoDB只锁定找到的索引记录,而不是锁定之前的间隙。
    • 对于其他搜索条件,InnoDB锁定扫描的索引范围,使用间隙锁或next-key锁来阻止其他会话插入到范围所覆盖的间隙中。有关间隙锁和next-key锁的信息,请参见第14.5.1节“InnoDB锁定”。
  • READ COMMITTED

    即使在同一个事务中,每次一致的读取都会设置并读取其自己的新快照。 有关一致读取的信息,请参见第14.5.2.3节“一致性非锁定读取”。

    对于锁定读取(SELECT FOR WITH UPDATE或LOCK IN SHARE MODE),UPDATE语句和DELETE语句,InnoDB只锁定索引记录,而不锁定它们之前的间隙,因此允许在锁定记录旁边自由插入新记录。间隙锁仅用于外键约束检查和重复键检查。

    由于禁用了间隙锁,所以可能会出现幻读问题,因为其他会话可以将新行插入到间隙中。有关幻读的信息,请参见第14.5.4节“幻读行”。
    如果您使用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;

    在这种情况下,该表没有索引,因此搜索和索引扫描使用隐藏聚簇索引进行记录锁定(请参见第14.8.2.1节“聚簇索引”和“二级索引”)而不是索引列。

    假设一个会话使用这些语句执行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-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)

    对于第二次更新,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隔离级别的效果与启用已弃用的innodb_locks_unsafe_for_binlog配置选项的效果相同,但有以下例外:

    • 启用innodb_locks_unsafe_for_binlog是一个全局设置,会影响所有会话,而隔离级别可以为所有会话全局设置,也可以单独为每个会话设置。
    • innodb_locks_unsafe_for_binlog只能在服务器启动时设置,而隔离级别可以在启动时设置或在运行时更改。

    因此,READ COMMITTED提供比innodb_locks_unsafe_for_binlog更好,更灵活的控制。

  • READ UNCOMMITTED

    SELECT语句以非锁定方式执行,但可能会使用行的早期版本。因此,使用这种隔离级别,这样的读取会不一致。这也被称为脏读。否则,这个隔离级别就像READ COMMITTED一样。

  • SERIALIZABLE

    此级别与REPEATABLE READ类似,但如果自动提交被禁用,则InnoDB会将所有普通SELECT语句隐式转换为SELECT … LOCK IN SHARE MODE。如果启用自动提交,则SELECT是它自己的事务。因此,它被认为是只读的,并且如果作为一致(非锁定)读取执行,则可以被序列化,并且不需要阻塞其他事务。(强制普通SELECT阻塞如果其他事务已修改选定的行,禁用自动提交。)

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/79632286