25.2.7.3 NDB 集群中的事务处理相关限制
NDB 集群中存在一些与事务处理相关的限制,这些限制包括以下内容:
-
事务隔离级别。 NDBCLUSTER 存储引擎仅支持
READ COMMITTED
事务隔离级别。(InnoDB
,例如,支持READ COMMITTED
、READ UNCOMMITTED
、REPEATABLE READ
和SERIALIZABLE
。)您应该注意的是,NDB 实现了基于每行的READ COMMITTED
;当读取请求到达存储该行的数据节点时,返回的是在该时间点上最后提交版本的行。未提交的数据永远不会被返回,但是当一个事务在读取同一行时与另一个事务同时提交修改该行的事务时,该事务可以观察到不同的行中“before”值、“after”值或两者,原因是对给定行的读取请求可能在其他事务提交前或后被处理。
为了确保某个事务只读取“before”或“after”值,您可以使用
SELECT ... LOCK IN SHARE MODE
来 impose row locks。在这种情况下,锁定直到所有者事务提交。使用行锁也可能会导致以下问题:-
锁定等待超时错误的频率增加和并发性降低
-
读取需要提交阶段的交易处理负载增加
-
可能会耗尽可用的并发锁定数量,这个数量是由
MaxNoOfConcurrentOperations
限制的
NDB
对所有读取使用READ COMMITTED
,除非使用修改器如LOCK IN SHARE MODE
或FOR UPDATE
。LOCK IN SHARE MODE
会导致共享行锁;FOR UPDATE
会导致独占行锁。唯一键读取的锁定将被自动升级到确保一致性读取;BLOB
读取也使用额外的锁定来确保一致性。请参阅第25.6.8.4节,“NDB 集群备份故障排除”,了解如何 NDB 集群的事务隔离级别影响
NDB
数据库的备份和恢复。 -
-
事务和 BLOB 或 TEXT 列。
NDBCLUSTER
只将使用 MySQL 的BLOB
或TEXT
数据类型的部分值存储在 MySQL 可见的表中;剩余的BLOB
或TEXT
是存储在一个不可访问的内部表中。这会导致两个相关的问题,您需要了解这些问题,以便在执行SELECT
语句时对包含这些类型列的表进行操作:-
对任何
SELECT
从 NDB 集群表:如果SELECT
包含BLOB
或TEXT
列,READ COMMITTED
事务隔离级别将被转换为读取锁定。这是为了确保一致性。 -
对任何
SELECT
使用唯一键查找来检索使用BLOB
或TEXT
数据类型的列,并且在事务中执行,该语句将持有共享读锁定直到事务被提交或终止。这不影响使用索引或表扫描的查询,即使是对
NDB
表具有BLOB
或TEXT
列的查询。例如,考虑由以下
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;
这是因为,这四个查询中的第一个使用索引扫描,第二和第三个使用表扫描,而第四个虽然使用主键查找,但不检索任何
BLOB
或TEXT
列的值。您可以通过避免使用唯一键查找检索
BLOB
或TEXT
列的查询来减少共享读锁的问题,或者在无法避免这些查询的情况下尽快提交事务。
-
-
唯一键查找和事务隔离 NDB 中实现唯一索引使用一个隐藏的索引表,该表由内部维护。当用户创建的 NDB 表通过唯一索引被访问时,首先读取隐藏索引表以找到主键,然后读取用户创建的表。为了避免在双重读操作中修改索引,找到在隐藏索引表中的行将被锁定。在更新 NDB 表中的唯一索引引用行时,隐藏索引表将受到事务中的独占锁定。这意味着,对于同一个(用户创建的) NDB 表的任何读操作都必须等待更新完成,即使读操作的事务级别为
READ COMMITTED
。可以使用以下一个 workaround 来绕过可能阻塞的读操作:强制 SQL 节点在执行读操作时忽略唯一索引。这可以通过在
SELECT
语句中使用IGNORE INDEX
索引提示来实现(见第 10.9.4 节,“索引提示”)。由于 MySQL 服务器为每个 NDB 中创建了一个 shadowing 有序索引,因此可以让有序索引被读取,而避免唯一索引访问锁定。结果的读操作与通过主键 committed 读操作相同,返回最后提交的值 ณ 时间读取行时。通过有序索引读取可能会使集群资源的使用效率降低,并且可能会增加延迟。
还可以避免使用唯一索引来访问数据,通过查询范围而不是唯一值来访问数据。
-
回滚. 不存在部分事务,也不存在部分回滚事务。重复键或类似错误将导致整个事务被回滚。
这与其他事务存储引擎,如
InnoDB
不同,它们可能会回滚单个语句。 -
事务和内存使用. 如前所述,NDB集群不善于处理大事务;更好的是执行多个小事务,每个事务包含少量操作,而不是尝试单个大事务包含大量操作。考虑到其他因素,大事务需要非常大的内存量。因此,MySQL语句的事务行为受到影响,如以下列表所述:
-
TRUNCATE TABLE
在NDB
表上不是事务性的。如果TRUNCATE TABLE
无法清空表,则必须重新运行直到成功。 -
DELETE FROM
(即使没有WHERE
子句)是事务性的。对于包含许多行的表,您可能会发现,使用多个DELETE FROM ... LIMIT ...
语句来“分块”删除操作可以提高性能。如果您的目标是清空表,那么您可能想使用TRUNCATE TABLE
语句。 -
ALTER TABLE 和事务. 当作为
ALTER TABLE
的一部分,复制NDB
表的创建是非事务性的。(在任何情况下,这个操作都会回滚到复制被删除时。)
-
-
事务和COUNT()函数 NDB集群复制中无法确保
COUNT()
函数的事务一致性。在源服务器上执行一系列语句(INSERT
、DELETE
或两者),这些语句在单个事务中更改表中的行数,然后在复制服务器上执行SELECT COUNT(*) FROM
查询可能会返回中间结果。这是因为table
SELECT COUNT(...)
可能执行脏读操作,而不是NDB存储引擎的bug。(请参阅Bug #31321以获取更多信息。)