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

NDB 群集在事务处理方面存在许多限制。其中包括:

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

为了确保给定的事务仅读取值之前或之后,可以使用选择...锁定共享模式施加行锁。在这种情况下,锁定将一直保持到拥有事务提交为止。使用行锁也会导致以下问题:

  • 锁定 await 超时错误的频率增加,并发减少

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

    • 耗尽可用的并发锁数量(受MaxNoOfConcurrentOperations限制)

NDB使用READ COMMITTED进行所有读取,除非使用了LOCK IN SHARE MODEFOR UPDATE之类的修饰符。 LOCK IN SHARE MODE导致使用共享行锁; FOR UPDATE导致使用排他行锁。唯一键读取的锁由NDB自动升级,以确保读取前后一致。 BLOB读取还采用了额外的锁定以保持一致性。

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

  • Transaction 和 BLOB 或 TEXT 列. NDBCLUSTER仅存储使用 MySQL 可见 table 中使用 MySQL 的BLOBTEXT数据类型的列值的一部分; BLOBTEXT的其余部分存储在单独的内部 table 中,MySQL 无法访问该 table。这会引起两个相关的问题,在包含这些类型的列的 table 上执行SELECT语句时,应注意以下两个问题:

  • 对于 NDB 群集 table 中的任何SELECT:如果SELECT包含BLOBTEXT列,则READ COMMITTED事务隔离级别将转换为具有读取锁定的读取。这样做是为了保证一致性。

  • 对于使用唯一键查找来检索使用BLOBTEXT数据类型中的任何数据并且在事务内执行的任何列的SELECT,事务期间将在 table 上保留一个共享的读取锁,也就是说,直到事务被提交或中止。

使用索引或 table 扫描的查询不会发生此问题,即使对于具有BLOBTEXT列的NDBtable 也是如此。

例如,考虑以下CREATE TABLE语句定义的 tablet

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;

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

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

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

可以用来绕过潜在的阻塞读取的一种解决方法是,强制 SQL 节点在执行读取时忽略唯一索引。这可以通过将IGNORE INDEX索引提示用作读取 table 的SELECT语句的一部分来完成(请参阅第 8.9.4 节“索引提示”)。由于 MySQL 服务器会为NDB中创建的每个唯一索引创建一个 shade 排序索引,因此可以读取该排序索引,并避免了唯一索引访问锁定。结果读取与主键提交的读取保持一致,并在读取行时返回最后的提交值。

通过有序索引进行读取会使群集中的资源使用效率降低,并且可能具有更高的延迟。

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

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

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

  • 事务和内存使用情况. 如本章其他地方所述,NDB 群集不能很好地处理大型事务。与尝试包含多个操作的单个大型事务相比,执行多个具有少量操作的小型事务更好。除其他考虑因素外,大型事务需要非常大量的内存。因此,如下列 table 中所述,许多 MySQL 语句的事务行为受到影响:

  • TRUNCATE TABLE用于NDBtable 时不是事务性的。如果TRUNCATE TABLE无法清空 table,则必须重新运行它,直到成功为止。

    • DELETE FROM(即使没有WHERE子句)*是事务性的。对于包含很多行的 table,您可能会发现通过使用几个DELETE FROM ... LIMIT ...语句“分块”删除操作可以提高性能。如果您的目标是清空 table,那么您可能希望使用TRUNCATE TABLE

    • LOAD DATA 语句. LOAD DATANDBtable 上使用时不是事务性的。

Important

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

  • ALTER TABLE 和 Transactions. 当复制NDBtable 作为ALTER TABLE的一部分时,副本的创建是非事务性的。 (无论如何,删除副本后都会回滚此操作.)

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