文档主页
MySQL 9.0 参考手册
相关文档 下载本手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 9.0 参考手册  /  ...  /  与 NDB 集群中事务处理相关的限制

25.2.7.3 与 NDB 集群中事务处理相关的限制

NDB 集群在处理事务方面存在一些限制。 这些限制包括以下内容:

  • 事务隔离级别。  NDBCLUSTER 存储引擎仅支持 READ COMMITTED 事务隔离级别。 (例如,InnoDB 支持 READ COMMITTEDREAD UNCOMMITTEDREPEATABLE READSERIALIZABLE。)您应该记住,NDB 是在每行的基础上实现 READ COMMITTED 的;当读取请求到达存储该行的数据库节点时,返回的是该行在该时间点的最后提交版本。

    未提交的数据永远不会返回,但是当修改多行的 事务与读取相同行的 事务并发提交时,执行读取的 事务可以观察到这些行中不同行的 “之前” 值、“之后” 值或两者兼有,这是因为给定的行读取请求可以在另一个 事务提交之前或之后处理。

    要确保给定 事务仅读取之前或之后的值,可以使用 SELECT ... LOCK IN SHARE MODE 来施加行锁。 在这种情况下,锁会一直保持到拥有锁的 事务提交。 使用行锁还会导致以下问题:

    • 锁等待超时错误的频率增加,并发性降低

    • 由于读取需要提交阶段,因此 事务处理开销增加

    • 可能耗尽可用的并发锁数量,该数量受 MaxNoOfConcurrentOperations 限制

    NDB 对所有读取使用 READ COMMITTED,除非使用了 LOCK IN SHARE MODEFOR UPDATE 等修饰符。 LOCK IN SHARE MODE 会导致使用共享行锁;FOR UPDATE 会导致使用独占行锁。 唯一键读取的锁会被 NDB 自动升级,以确保自洽读取;BLOB 读取也采用额外的锁定来确保一致性。

    有关 NDB 集群的事务隔离级别实现如何影响 NDB 数据库的备份和恢复的信息,请参见 第 25.6.8.4 节 “NDB 集群备份故障排除”

  • 事务与 BLOB 或 TEXT 列。  NDBCLUSTER 仅将使用 MySQL 的任何 BLOBTEXT 数据类型的列值的一部分存储在 MySQL 可见的表中;BLOBTEXT 的其余部分存储在 MySQL 无法访问的单独内部表中。 这会导致两个相关问题,在对包含这些类型列的表执行 SELECT 语句时,您应该注意这两个问题:

    1. 对于从 NDB 集群表执行的任何 SELECT:如果 SELECT 包含 BLOBTEXT 列,则 READ COMMITTED 事务隔离级别将转换为使用读锁进行读取。 这样做是为了保证一致性。

    2. 对于任何使用唯一键查找来检索使用任何 BLOBTEXT 数据类型的任何列并在 事务中执行的 SELECT,在 事务期间(即,直到 事务提交或中止),该表上都会持有共享读锁。

      对于使用索引或表扫描的查询,即使针对具有 BLOBTEXT 列的 NDB 表的查询,也不会出现此问题。

      例如,考虑由以下 CREATE TABLE 语句定义的表 t

      CREATE TABLE t (
          a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
          b INT NOT NULL,
          c INT NOT NULL,
          d TEXT,
          INDEX i(b),
          UNIQUE KEY u(c)
      ) ENGINE = NDB,

      以下对 t 的查询会导致共享读锁,因为它使用唯一键查找:

      SELECT * FROM t WHERE c = 1;

      但是,此处显示的四个查询都不会导致共享读锁:

      SELECT * FROM t WHERE b = 1;
      
      SELECT * FROM t WHERE d = '1';
      
      SELECT * FROM t;
      
      SELECT b,c WHERE a = 1;

      这是因为,在这四个查询中,第一个使用索引扫描,第二个和第三个使用表扫描,而第四个虽然使用主键查找,但不检索任何 BLOBTEXT 列的值。

      您可以通过避免使用检索 BLOBTEXT 列的唯一键查找的查询来帮助最大限度地减少共享读锁问题,或者,在无法避免此类查询的情况下,尽快提交之后的事务。

  • 唯一键查找和事务隔离。  唯一索引是在 NDB 中使用内部维护的隐藏索引表实现的。 当使用唯一索引访问用户创建的 NDB 表时,首先会读取隐藏索引表以查找主键,然后使用该主键读取用户创建的表。 为了避免在此双重读取操作期间修改索引,隐藏索引表中找到的行会被锁定。 当更新用户创建的 NDB 表中唯一索引引用的行时,执行更新的 事务会对隐藏索引表进行独占锁定。 这意味着对同一个(用户创建的)NDB 表的任何读取操作都必须等待更新完成。 即使读取操作的事务级别为 READ COMMITTED,也是如此。

    一种可以用来绕过潜在阻塞读取的解决方法是强制 SQL 节点在执行读取时忽略唯一索引。 这可以通过在读取表的 SELECT 语句中使用 IGNORE INDEX 索引提示来完成(请参见 第 10.9.4 节 “索引提示”)。 因为 MySQL 服务器会为 NDB 中创建的每个唯一索引创建一个影子有序索引,所以这可以让有序索引被读取,并避免唯一索引访问锁定。 生成的读取与通过主键进行的提交读取一样一致,返回读取行时最后提交的值。

    通过有序索引读取对集群中的资源使用效率较低,并且可能会导致更高的延迟。

    还可以通过查询范围而不是唯一值来避免使用唯一索引进行访问。

  • 回滚。  没有部分事务,也没有事务的部分回滚。 重复键或类似错误会导致整个 事务回滚。

    此行为与其他事务性存储引擎(例如 InnoDB)的行为不同,后者可能会回滚单个语句。

  • 事务和内存使用。  正如本章其他地方所述,NDB 集群不能很好地处理大型 事务;最好执行多个小 事务(每个 事务只有几个操作),而不是尝试执行包含大量操作的单个大型 事务。 除其他考虑因素外,大型 事务需要非常大的内存。 因此,许多 MySQL 语句的事务行为会受到影响,如下面的列表所述:

    • NDB 表上使用时,TRUNCATE TABLE 不是事务性的。 如果 TRUNCATE TABLE 未能清空表,则必须重新运行,直到成功为止。

    • DELETE FROM(即使没有 WHERE 子句) 事务性的。 对于包含大量行的表,您可能会发现使用多条 DELETE FROM ... LIMIT ... 语句来“分块”删除操作可以提高性能。 如果您的目标是清空表,则您可能希望改用 TRUNCATE TABLE

    • LOAD DATA 语句。 NDB 表上使用时,LOAD DATA 不是事务性的。

      重要

      执行 LOAD DATA 语句时,NDB 引擎会以不规则的间隔执行提交,从而更好地利用通信网络。 无法提前知道何时进行此类提交。

    • ALTER TABLE 和事务。  当复制 NDB 表作为 ALTER TABLE 的一部分时,副本的创建是非事务性的。 (在任何情况下,删除副本时,此操作都会回滚。)

  • 事务和 COUNT() 函数。 使用 NDB 集群复制时,无法保证副本上 COUNT() 函数的事务一致性。换句话说,当在源上执行一系列语句(INSERTDELETE 或两者兼有)以在单个事务中更改表中的行数时,在副本上执行 SELECT COUNT(*) FROM table 查询可能会产生中间结果。这是因为 SELECT COUNT(...) 可能会执行脏读,并且不是 NDB 存储引擎中的错误。(有关更多信息,请参见错误 #31321。)