在使用涉及多个源(包括循环复制)的复制设置时,不同的源可能会尝试用不同的数据更新副本上的同一行。NDB 集群复制中的冲突解决提供了一种解决此类冲突的方法,通过允许使用用户定义的解决列来确定是否应该在副本上应用给定源的更新。When using a replication setup involving multiple sources (including circular replication), it is possible that different sources may try to update the same row on the replica with different data. Conflict resolution in NDB Cluster Replication provides a means of resolving such conflicts by permitting a user-defined resolution column to be used to determine whether or not an update on a given source should be applied on the replica.
NDB 集群支持的某些类型的冲突解决方法(NDB$OLD()、NDB$MAX() 和 NDB$MAX_DELETE_WIN();NDB$MAX_INS() 和 NDB$MAX_DEL_WIN_INS())将此用户定义的列实现为 “timestamp” 列(尽管其类型不能是 TIMESTAMP,如本节稍后所述)。这些类型的冲突解决总是按行应用,而不是按事务应用。基于纪元的冲突解决函数 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 比较纪元复制的顺序(因此这些函数是事务性的)。发生冲突时,可以采用不同的方法在副本上比较解决列的值,如本节稍后所述;所用方法可以设置为针对单个表、数据库或服务器,或者针对使用模式匹配的一个或多个表集合。有关在 mysql.ndb_replication 表的 db、table_name 和 server_id 列中使用模式匹配的信息,请参见 使用通配符匹配。Some types of conflict resolution supported by NDB Cluster
(NDB$OLD(), NDB$MAX(), and
NDB$MAX_DELETE_WIN();
NDB$MAX_INS() and
NDB$MAX_DEL_WIN_INS()) implement this
user-defined column as a “timestamp” column (although
its type cannot be TIMESTAMP, as
explained later in this section). These types of conflict
resolution are always applied a row-by-row basis rather than a
transactional basis. The epoch-based conflict resolution functions
NDB$EPOCH() and
NDB$EPOCH_TRANS() compare the order in which
epochs are replicated (and thus these functions are
transactional). Different methods can be used to compare
resolution column values on the replica when conflicts occur, as
explained later in this section; the method used can be set to act
on a single table, database, or server, or on a set of one or more
tables using pattern matching. See
Matching with wildcards, for information about
using pattern matches in the db,
table_name, and server_id
columns of the mysql.ndb_replication table.
您还应记住,确保“解析”列正确填充相关值是应用程序的责任,以便在确定是否应用更新时,解析功能能够做出适当的选择。You should also keep in mind that it is the application's responsibility to ensure that the resolution column is correctly populated with relevant values, so that the resolution function can make the appropriate choice when determining whether to apply an update.
必须在源端和副本端为冲突解析做好准备。这些任务在以下列表中描述:Preparations for conflict resolution must be made on both the source and the replica. These tasks are described in the following list:
在源端写入二进制日志时,您必须确定发送哪些列(所有列或仅更新过的列)。这可以通过为整个 MySQL 服务器应用 mysqld 启动选项
--ndb-log-updated-only(本节稍后描述)来完成,或者通过在一个或多个特定表中在mysql.ndb_replication表中放置适当条目(参见 ndb_replication 表)来完成。On the source writing the binary logs, you must determine which columns are sent (all columns or only those that have been updated). This is done for the MySQL Server as a whole by applying the mysqld startup option--ndb-log-updated-only(described later in this section), or on one or more specific tables by placing the proper entries in themysql.ndb_replicationtable (see ndb_replication Table).Note如果您正在复制具有非常大列(例如
TEXT或BLOB列)的表,--ndb-log-updated-only也可以帮助减少二进制日志的大小,并避免因超过max_allowed_packet而可能导致的复制失败。If you are replicating tables with very large columns (such asTEXTorBLOBcolumns),--ndb-log-updated-onlycan also be useful for reducing the size of the binary logs and avoiding possible replication failures due to exceedingmax_allowed_packet.有关此问题的更多信息,请参见 第 19.5.1.20 节,“复制与 max_allowed_packet”。See Section 19.5.1.20, “Replication and max_allowed_packet”, for more information about this issue.
在副本上,您必须确定要应用哪种类型的冲突解决(“最新时间戳获胜”、“相同时间戳获胜”、“主节点获胜”、“主节点获胜,完成事务”,或没有)。这是通过使用
mysql.ndb_replication系统表来完成的,并适用于一个或多个特定表(参见 ndb_replication 表)。On the replica, you must determine which type of conflict resolution to apply (“latest timestamp wins”, “same timestamp wins”, “primary wins”, “primary wins, complete transaction”, or none). This is done using themysql.ndb_replicationsystem table, and applies to one or more specific tables (see ndb_replication Table).NDB 集群还支持读取冲突检测,即检测一个集群中对某行的读取与另一个集群中对同一行的更新或删除之间的冲突。这需要通过在副本上将
ndb_log_exclusive_reads设置为 1 来获取独占读取锁。所有被冲突读取读取的行都会记录在异常表中。更多信息,请参见 读取冲突检测与解决。NDB Cluster also supports read conflict detection, that is, detecting conflicts between reads of a given row in one cluster and updates or deletes of the same row in another cluster. This requires exclusive read locks obtained by settingndb_log_exclusive_readsequal to 1 on the replica. All rows read by a conflicting read are logged in the exceptions table. For more information, see Read conflict detection and resolution.当使用
NDB$MAX_INS()或NDB$MAX_DEL_WIN_INS()时,NDB可以幂等地应用WRITE_ROW事件,如果传入的行尚不存在,则将此事件映射为插入;如果已存在,则映射为更新。When usingNDB$MAX_INS()orNDB$MAX_DEL_WIN_INS(),NDBcan applyWRITE_ROWevents idempotently, mapping such an event to an insert when the incoming row does not already exist, or to an update if it does.当使用除
NDB$MAX_INS()或NDB$MAX_DEL_WIN_INS()之外的任何冲突解决函数时,如果行已存在,传入的写操作总是会被拒绝。When using any conflict resolution function other thanNDB$MAX_INS()orNDB$MAX_DEL_WIN_INS(), an incoming write is always rejected if the row already exists.
在使用函数 NDB$OLD()、NDB$MAX()、NDB$MAX_DELETE_WIN()、NDB$MAX_INS() 和 NDB$MAX_DEL_WIN_INS() 进行基于时间戳的冲突解决时,我们通常将用于确定更新的列称为 “timestamp” 列。然而,这列的 数据类型从来不是 TIMESTAMP;相反,它的数据类型应为 INT (INTEGER) 或 BIGINT。“timestamp” 列还应为 UNSIGNED 且 NOT NULL。When using the functions NDB$OLD(),
NDB$MAX(),
NDB$MAX_DELETE_WIN(),
NDB$MAX_INS(), and
NDB$MAX_DEL_WIN_INS() for timestamp-based
conflict resolution, we often refer to the column used for
determining updates as a “timestamp” column.
However, the data type of this column is never
TIMESTAMP; instead, its data type
should be INT
(INTEGER) or
BIGINT. The
“timestamp” column should also be
UNSIGNED and NOT NULL.
本节后面讨论的 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 函数通过比较在主 NDB 集群和备份 NDB 集群上应用的复制纪元的相对顺序来工作,而不使用时间戳。The NDB$EPOCH() and
NDB$EPOCH_TRANS() functions discussed later
in this section work by comparing the relative order of
replication epochs applied on a primary and secondary NDB
Cluster, and do not make use of timestamps.
We can see update operations in terms of “before”以及“after” images—that is, the states of the
table before and after the update is applied. Normally, when
updating a table with a primary key, the “before”
image is not of great interest; however, when we need to
determine on a per-update basis whether or not to use the
updated values on a replica, we need to make sure that both
images are written to the source's binary log. This is done
with the
--ndb-log-update-as-write选项为mysqld, as described later in this
section.
We can see update operations in terms of “before”
and “after” images—that is, the states of the
table before and after the update is applied. Normally, when
updating a table with a primary key, the “before”
image is not of great interest; however, when we need to
determine on a per-update basis whether or not to use the
updated values on a replica, we need to make sure that both
images are written to the source's binary log. This is done
with the
--ndb-log-update-as-write option
for mysqld, as described later in this
section.
是否记录完整行或仅更新的列,是在 MySQL 服务器启动时决定的,并且无法在在线状态下更改;您必须要么重新启动 mysqld,要么使用不同的日志选项启动一个新的 mysqld 实例。Whether logging of complete rows or of updated columns only is done is decided when the MySQL server is started, and cannot be changed online; you must either restart mysqld, or start a new mysqld instance with different logging options.
冲突解决通常在可能发生冲突的服务器上启用。像日志方法选择一样,它是通过在 mysql.ndb_replication 表中的条目来启用的。
Conflict resolution is usually enabled on the server where
conflicts can occur. Like logging method selection, it is
enabled by entries in the
mysql.ndb_replication table.
NBT_UPDATED_ONLY_MINIMAL 和 NBT_UPDATED_FULL_MINIMAL 可以与 NDB$EPOCH()、NDB$EPOCH2() 和 NDB$EPOCH_TRANS() 一起使用,因为这些不需要非主键列的 “before” 值。需要旧值的冲突解决算法,例如 NDB$MAX() 和 NDB$OLD(),无法与这些 binlog_type 值正确工作。NBT_UPDATED_ONLY_MINIMAL and
NBT_UPDATED_FULL_MINIMAL can be used with
NDB$EPOCH(), NDB$EPOCH2(),
and NDB$EPOCH_TRANS(), because these do not
require “before” values of columns which are not
primary keys. Conflict resolution algorithms requiring the old
values, such as NDB$MAX() and
NDB$OLD(), do not work correctly with these
binlog_type values.
本节提供了有关可用于 NDB 复制的冲突检测和解决的函数的详细信息。This section provides detailed information about the functions which can be used for conflict detection and resolution with NDB Replication.
NDB$旧()NDB$OLD()
如果 column_name 的值在源和副本上相同,则会应用更新;否则,更新不会在副本上应用,并且异常会写入日志。如下面的伪代码所示:
If the value of column_name is the
same on both the source and the replica, then the update is
applied; otherwise, the update is not applied on the replica and
an exception is written to the log. This is illustrated by the
following pseudocode:
if (source_old_column_value==replica_current_column_value) apply_update(); else log_exception();
该函数可用于“相同值获胜”冲突解决。这种类型的冲突解决确保不会从错误的源应用更新到副本。 This function can be used for “same value wins” conflict resolution. This type of conflict resolution ensures that updates are not applied on the replica from the wrong source.
该函数使用来自源的“before”镜像的列值。The column value from the source's “before” image is used by this function.
NDB$MAX()
对于更新或删除操作,如果来自源的某一行的 “timestamp” 列值高于副本上的值,则会应用该操作;否则不会在副本上应用该操作。如下伪代码所示: For an update or delete operation, if the “timestamp” column value for a given row coming from the source is higher than that on the replica, it is applied; otherwise it is not applied on the replica. This is illustrated by the following pseudocode:
if (source_new_column_value>replica_current_column_value) apply_update();
该函数可用于 “最大时间戳优先” 冲突解决。此类冲突解决方式确保在发生冲突时,最近更新的行版本将是最终保留的版本。 This function can be used for “greatest timestamp wins” conflict resolution. This type of conflict resolution ensures that, in the event of a conflict, the version of the row that was most recently updated is the version that persists.
此函数对写操作之间的冲突没有影响,唯一例外是:如果写操作的主键与之前的写操作相同,则该写操作总是被拒绝;只有在不存在使用相同主键的写操作时,它才会被接受并应用。您可以使用 NDB$MAX_INS() 来处理写入操作之间的冲突解决。This function has no effects on conflicts between write
operations, other than that a write operation with the same
primary key as a previous write is always rejected; it is
accepted and applied only if no write operation using the same
primary key already exists. You can use
NDB$MAX_INS()
to handle conflict resolution between writes.
此函数使用的是源的 “after” 镜像中的列值。The column value from the sources's “after” image is used by this function.
NDB$MAX_DELETE_WIN()
这是 NDB$MAX() 的一种变体。由于删除操作没有可用的时间戳,因此使用 NDB$MAX() 的删除实际上被处理为 NDB$OLD,但是对于某些用例,这并不是最佳的。对于 NDB$MAX_DELETE_WIN(),如果来自源的数据新增或更新现有行的某行的 “timestamp” 列值高于副本上的值,则会应用该更新。然而,删除操作总是被视为具有更高值。其说明如下伪代码:
This is a variation on NDB$MAX(). Due to the
fact that no timestamp is available for a delete operation, a
delete using NDB$MAX() is in fact processed
as NDB$OLD, but for some use cases, this is
not optimal. For NDB$MAX_DELETE_WIN(), if the
“timestamp” column value for a given row adding or
updating an existing row coming from the source is higher than
that on the replica, it is applied. However, delete operations
are treated as always having the higher value. This is
illustrated by the following pseudocode:
if ( (source_new_column_value>replica_current_column_value) ||operation.type== "delete") apply_update();
此函数可用于 “最大时间戳,删除优先” 冲突解决。这种类型的冲突解决确保在发生冲突时,被删除或(其他方式)最近更新的行版本是最终保留的版本。 This function can be used for “greatest timestamp, delete wins” conflict resolution. This type of conflict resolution ensures that, in the event of a conflict, the version of the row that was deleted or (otherwise) most recently updated is the version that persists.
与 NDB$MAX() 一样,源的 “after” 图像中的列值是该函数使用的值。As with NDB$MAX(), the column value from
the source's “after” image is the value used
by this function.
NDB$MAX_INS()
此函数支持解决冲突的写操作。此类冲突由 “NDB$MAX_INS()” 处理,如下所示: This function provides support for resolution of conflicting write operations. Such conflicts are handled by “NDB$MAX_INS()” as follows:
如果没有冲突的写入,则应用此写入(这与
NDB$MAX()相同)。If there is no conflicting write, apply this one (this is the same asNDB$MAX()).否则,应用“时间戳最大者获胜”冲突解决,具体如下:Otherwise, apply “greatest timestamp wins” conflict resolution, as follows:
如果传入写入的时间戳大于冲突写入的时间戳,则应用传入操作。If the timestamp for the incoming write is greater than that of the conflicting write, apply the incoming operation.
如果传入写入的时间戳不大于,则拒绝传入写入操作。If the timestamp for the incoming write is not greater, reject the incoming write operation.
处理插入操作时,NDB$MAX_INS() 将比较源和副本的时间戳,如以下伪代码所示:When handling an insert operation,
NDB$MAX_INS() compares timestamps from the
source and replica as illustrated by the following pseudocode:
if (source_new_column_value > replica_current_column_value) apply_insert(); else log_exception();
对于更新操作,将源的更新时间戳列值与副本的时间戳列值进行比较,如下所示:For an update operation, the updated timestamp column value from the source is compared with the replica's timestamp column value, as shown here:
if (source_new_column_value > replica_current_column_value) apply_update(); else log_exception();
这与 NDB$MAX() 执行的操作相同。This is the same as performed by
NDB$MAX().
对于删除操作,处理方式也与 NDB$MAX() 相同(因此与 NDB$OLD() 相同),其操作如下:For delete operations, the handling is also the same as that
performed by NDB$MAX() (and thus the same as
NDB$OLD()), and is done like this:
if (source_new_column_value == replica_current_column_value) apply_delete(); else log_exception();
NDB$MAX_DEL_WIN_INS()
此功能提供对冲突写操作的解决支持,以及类似 “删除优先” 的解决方案,如 NDB$MAX_DELETE_WIN()。写冲突由 NDB$MAX_DEL_WIN_INS() 处理,如下所示:
This function provides support for resolution of conflicting
write operations, along with “delete wins”
resolution like that of
NDB$MAX_DELETE_WIN().
Write conflicts are handled by
NDB$MAX_DEL_WIN_INS() as shown here:
如果没有冲突写操作,则应用此操作(这与
NDB$MAX_DELETE_WIN()相同)。If there is no conflicting write, apply this one (this is the same asNDB$MAX_DELETE_WIN()).否则,应用“时间戳最大者获胜”冲突解决,具体如下:Otherwise, apply “greatest timestamp wins” conflict resolution, as follows:
如果传入写入的时间戳大于冲突写入的时间戳,则应用传入操作。If the timestamp for the incoming write is greater than that of the conflicting write, apply the incoming operation.
如果传入写入的时间戳不大于,则拒绝传入写入操作。If the timestamp for the incoming write is not greater, reject the incoming write operation.
由 NDB$MAX_DEL_WIN_INS() 执行的插入操作处理可用以下伪代码表示:Handling of insert operations as performed by
NDB$MAX_DEL_WIN_INS() can be represented in
pseudocode as shown here:
if (source_new_column_value > replica_current_column_value) apply_insert(); else log_exception();
对于更新操作,源的更新时间戳列值会与副本的时间戳列值进行比较,如下所示(再次使用伪代码):For update operations, the source's updated timestamp column value is compared with replica's timestamp column value, like this (again using pseudocode):
if (source_new_column_value > replica_current_column_value) apply_update(); else log_exception();
删除操作使用“删除优先”策略处理(与NDB$MAX_DELETE_WIN()相同);DELETE总是被应用,不考虑任何时间戳值,如下伪代码所示:Deletes are handled using a “delete always wins”
strategy (the same as NDB$MAX_DELETE_WIN());
a DELETE is always applied without any regard
to any timestamp values, as illustrated by this pseudocode:
if (operation.type == "delete") apply_delete();
对于更新和删除操作之间的冲突,此函数的行为与NDB$MAX_DELETE_WIN()完全相同。For conflicts between update and delete operations, this
function behaves identically to
NDB$MAX_DELETE_WIN().
NDB$EPOCH()
NDB$EPOCH() 函数跟踪在副本集群上应用复制周期的顺序,相对于源自副本的更改。此相对顺序用于确定源自副本的更改是否与任何本地产生的更改同时发生,因此可能存在冲突。
The NDB$EPOCH() function tracks the order in
which replicated epochs are applied on a replica cluster
relative to changes originating on the replica. This relative
ordering is used to determine whether changes originating on the
replica are concurrent with any changes that originate locally,
and are therefore potentially in conflict.
接下来的大部分关于 NDB$EPOCH() 的描述也适用于 NDB$EPOCH_TRANS()。任何例外情况在文本中另行说明。Most of what follows in the description of
NDB$EPOCH() also applies to
NDB$EPOCH_TRANS(). Any exceptions are noted
in the text.
NDB$EPOCH() is asymmetric, operating on one
NDB Cluster in a bidirectional replication configuration
(sometimes referred to as “active-active”
replication). We refer here to cluster on which it operates as
the primary, and the other as the secondary. The replica on the
primary is responsible for detecting and handling conflicts,
while the replica on the secondary is not involved in any
conflict detection or handling.
当主节点上的副本检测到冲突时,它会将事件注入到自己的二进制日志中以进行补偿;这确保了次级 NDB 集群最终能够与主节点重新对齐,从而防止主从节点出现分歧。这种补偿和重新对齐机制要求主 NDB 集群在与次级节点发生冲突时始终获胜——也就是说,在发生冲突时,总是使用主节点的更改而非次级节点的更改。这个“主节点永远胜出”规则具有以下含义:When the replica on the primary detects conflicts, it injects events into its own binary log to compensate for these; this ensures that the secondary NDB Cluster eventually realigns itself with the primary and so keeps the primary and secondary from diverging. This compensation and realignment mechanism requires that the primary NDB Cluster always wins any conflicts with the secondary—that is, that the primary's changes are always used rather than those from the secondary in event of a conflict. This “primary always wins” rule has the following implications:
一旦在主节点上提交的更改数据操作是完全持久的,不会被冲突检测和解决撤销或回滚。Operations that change data, once committed on the primary, are fully persistent and are not undone or rolled back by conflict detection and resolution.
从主节点读取的数据是完全一致的。任何在主节点提交的更改(无论是本地提交还是来自副本的提交)都不会在之后被恢复原状。Data read from the primary is fully consistent. Any changes committed on the Primary (locally or from the replica) are not reverted later.
在辅助节点上修改数据的操作,如果主节点确定它们存在冲突,可能会被随后撤销。Operations that change data on the secondary may later be reverted if the primary determines that they are in conflict.
在辅助节点上读取的单个行始终是自洽的,每行始终反映辅助节点提交的状态,或主节点提交的状态。Individual rows read on the secondary are self-consistent at all times, each row always reflecting either a state committed by the secondary, or one committed by the primary.
在辅助节点上读取的行集合在某一时刻可能不一定是一致的。对于
NDB$EPOCH_TRANS(),这是一个短暂状态;对于NDB$EPOCH(),它可以是一个持久状态。Sets of rows read on the secondary may not necessarily be consistent at a given single point in time. ForNDB$EPOCH_TRANS(), this is a transient state; forNDB$EPOCH(), it can be a persistent state.假设在没有任何冲突的足够长时间内,辅助 NDB 集群上的所有数据最终会与主节点的数据保持一致。Assuming a period of sufficient length without any conflicts, all data on the secondary NDB Cluster (eventually) becomes consistent with the primary's data.
NDB$EPOCH() 和 NDB$EPOCH_TRANS() 不需要任何用户模式修改,也不需要对应用程序进行更改即可提供冲突检测。然而,需要仔细考虑所使用的模式和访问模式,以确保整个系统在规定的范围内运行。NDB$EPOCH() and
NDB$EPOCH_TRANS() do not require any user
schema modifications, or application changes to provide conflict
detection. However, careful thought must be given to the schema
used, and the access patterns used, to verify that the complete
system behaves within specified limits.
每个 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 函数都可以接受一个可选参数;该参数是用来表示纪元低 32 位的位数,且应设置为不低于此处计算的值:Each of the NDB$EPOCH() and
NDB$EPOCH_TRANS() functions can take an
optional parameter; this is the number of bits to use to
represent the lower 32 bits of the epoch, and should be set to
no less than the value calculated as shown here:
CEIL( LOG2(TimeBetweenGlobalCheckpoints/TimeBetweenEpochs), 1)
对于这些配置参数的默认值(分别为 2000 和 100 毫秒),这给出了 5 位的值,因此默认值(6)应该足够,除非使用了其他值来设置TimeBetweenGlobalCheckpoints、TimeBetweenEpochs,或两者。值过小可能导致误报,而值过大可能导致数据库中空间浪费过多。For the default values of these configuration parameters (2000
and 100 milliseconds, respectively), this gives a value of 5
bits, so the default value (6) should be sufficient, unless
other values are used for
TimeBetweenGlobalCheckpoints,
TimeBetweenEpochs, or
both. A value that is too small can result in false positives,
while one that is too large could lead to excessive wasted space
in the database.
如果已根据本节其他地方所述的相同异常表模式规则定义了相关表(参见 NDB$OLD()),那么 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 都会将冲突行的条目插入到相关的异常表中。在创建要与之使用的表之前,必须先创建任何异常表。Both NDB$EPOCH() and
NDB$EPOCH_TRANS() insert entries for
conflicting rows into the relevant exceptions tables, provided
that these tables have been defined according to the same
exceptions table schema rules as described elsewhere in this
section (see
NDB$OLD()). You must
create any exceptions table before creating the data table with
which it is to be used.
与本节讨论的其他冲突检测函数一样,NDB$EPOCH() 和 NDB$EPOCH_TRANS() 是通过在 mysql.ndb_replication 表中包含相关条目来激活的(请参见 ndb_replication 表)。在此场景中,主 NDB Cluster 和从 NDB Cluster 的角色完全由 mysql.ndb_replication 表中的条目确定。As with the other conflict detection functions discussed in this
section, NDB$EPOCH() and
NDB$EPOCH_TRANS() are activated by including
relevant entries in the mysql.ndb_replication
table (see ndb_replication Table).
The roles of the primary and secondary NDB Clusters in this
scenario are fully determined by
mysql.ndb_replication table entries.
由于 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 使用的冲突检测算法是非对称的,因此必须为主副本和从副本的 server_id 条目使用不同的值。Because the conflict detection algorithms employed by
NDB$EPOCH() and
NDB$EPOCH_TRANS() are asymmetric, you must
use different values for the server_id
entries of the primary and secondary replicas.
仅 DELETE 操作之间的冲突不足以使用 NDB$EPOCH() 或 NDB$EPOCH_TRANS() 触发冲突,并且在纪元中的相对顺序无关紧要。A conflict between DELETE operations alone is
not sufficient to trigger a conflict using
NDB$EPOCH() or
NDB$EPOCH_TRANS(), and the relative placement
within epochs does not matter.
NDB$EPOCH() 的限制 Limitations on NDB$EPOCH()
使用 NDB$EPOCH() 进行冲突检测时,目前适用以下限制:
The following limitations currently apply when using
NDB$EPOCH() to perform conflict detection:
冲突使用 NDB 集群纪元边界检测,其粒度与
TimeBetweenEpochs成比例(默认值:100 毫秒)。最小冲突窗口是指在此时间段内,对两个集群上的相同数据进行并发更新时总会报告冲突的最短时间。这个时间总是非零长度,并且大致与2 * (latency + queueing + TimeBetweenEpochs)成正比。这意味着——假设TimeBetweenEpochs使用默认值,并忽略集群之间的任何延迟(以及任何排队延迟)——最小冲突窗口大小大约为 200 毫秒。在考虑预期应用的 “竞态” 模式时,应考虑此最小窗口。Conflicts are detected using NDB Cluster epoch boundaries, with granularity proportional toTimeBetweenEpochs(default: 100 milliseconds). The minimum conflict window is the minimum time during which concurrent updates to the same data on both clusters always report a conflict. This is always a nonzero length of time, and is roughly proportional to2 * (latency + queueing + TimeBetweenEpochs). This implies that—assuming the default forTimeBetweenEpochsand ignoring any latency between clusters (as well as any queuing delays)—the minimum conflict window size is approximately 200 milliseconds. This minimum window should be considered when looking at expected application “race” patterns.使用
NDB$EPOCH()和NDB$EPOCH_TRANS()函数的表需要额外的存储空间;每行需要额外 1 到 32 位空间,具体取决于传递给函数的值。Additional storage is required for tables using theNDB$EPOCH()andNDB$EPOCH_TRANS()functions; from 1 to 32 bits extra space per row is required, depending on the value passed to the function.删除操作之间的冲突可能导致主副本之间出现差异。当一行在两个集群上同时被删除时,可以检测到冲突,但不会被记录,因为该行已被删除。这意味着在任何后续重排操作传播过程中进一步的冲突无法被检测到,可能导致差异。Conflicts between delete operations may result in divergence between the primary and secondary. When a row is deleted on both clusters concurrently, the conflict can be detected, but is not recorded, since the row is deleted. This means that further conflicts during the propagation of any subsequent realignment operations are not detected, which can lead to divergence.
删除操作应在外部进行串行化,或仅路由到一个集群。或者,可以使用一个单独的行与这些删除操作以及随后进行的任何插入操作进行事务性更新,以便可以跨行删除跟踪冲突。这可能需要修改应用程序。Deletes should be externally serialized, or routed to one cluster only. Alternatively, a separate row should be updated transactionally with such deletes and any inserts that follow them, so that conflicts can be tracked across row deletes. This may require changes in applications.
当使用
NDB$EPOCH()或NDB$EPOCH_TRANS()进行冲突检测时,目前仅支持双向 “active-active” 配置中的两个 NDB 集群。Only two NDB Clusters in a bidirectional “active-active” configuration are currently supported when usingNDB$EPOCH()orNDB$EPOCH_TRANS()for conflict detection.具有
BLOB或TEXT列的表目前不支持使用NDB$EPOCH()或NDB$EPOCH_TRANS()。Tables havingBLOBorTEXTcolumns are not currently supported withNDB$EPOCH()orNDB$EPOCH_TRANS().
NDB$EPOCH_TRANS()
NDB$EPOCH_TRANS() 扩展了 NDB$EPOCH() 函数。冲突的检测和处理方式相同,使用 “主库全胜” 规则(见 NDB$EPOCH()),但增加了额外条件:在发生冲突的同一事务中更新的任何其他行也被视为冲突。换句话说,NDB$EPOCH() 是在从库上重新对齐单个冲突行,而 NDB$EPOCH_TRANS() 是重新对齐冲突事务。
NDB$EPOCH_TRANS() extends the
NDB$EPOCH() function. Conflicts are detected
and handled in the same way using the “primary wins
all” rule (see
NDB$EPOCH()) but with
the extra condition that any other rows updated in the same
transaction in which the conflict occurred are also regarded as
being in conflict. In other words, where
NDB$EPOCH() realigns individual conflicting
rows on the secondary, NDB$EPOCH_TRANS()
realigns conflicting transactions.
此外,任何明显依赖于冲突事务的事务也被视为冲突事务,这些依赖关系是通过辅助集群的二进制日志内容来确定的。由于二进制日志仅包含数据修改操作(插入、更新和删除),因此仅使用数据修改的重叠来确定事务之间的依赖关系。In addition, any transactions which are detectably dependent on a conflicting transaction are also regarded as being in conflict, these dependencies being determined by the contents of the secondary cluster's binary log. Since the binary log contains only data modification operations (inserts, updates, and deletes), only overlapping data modifications are used to determine dependencies between transactions.
NDB$EPOCH_TRANS() 遵循与 NDB$EPOCH() 相同的条件和限制,此外还要求所有事务 ID 都记录在辅助节点的二进制日志中,需要将 --ndb-log-transaction-id 设置为 ON。这会增加一定量的开销(每行最多 13 字节)。NDB$EPOCH_TRANS() is subject to the same
conditions and limitations as NDB$EPOCH(),
and in addition requires that all transaction IDs are recorded
in the secondary's binary log, using
--ndb-log-transaction-id set to
ON. This adds a variable amount of overhead
(up to 13 bytes per row).
参见 NDB$EPOCH()。See NDB$EPOCH().
NDB$EPOCH2()
NDB$EPOCH2() 函数类似于 NDB$EPOCH(),只是 NDB$EPOCH2() 提供了在双向复制拓扑下的删除-删除处理。在这种情况下,可以通过在每个源上将 ndb_conflict_role 系统变量设置为适当的值(通常为一个 PRIMARY,一个 SECONDARY)来分配两个源的主/从角色。完成此操作后,二级源所做的修改将由主源返回到二级源,然后二级源有条件地应用这些修改。
The NDB$EPOCH2() function is similar to
NDB$EPOCH(), except that
NDB$EPOCH2() provides for delete-delete
handling with a bidirectional replication topology. In this
scenario, primary and secondary roles are assigned to the two
sources by setting the
ndb_conflict_role system
variable to the appropriate value on each source (usually one
each of PRIMARY,
SECONDARY). When this is done, modifications
made by the secondary are reflected by the primary back to the
secondary which then conditionally applies them.
NDB$EPOCH2_TRANS()
NDB$EPOCH2_TRANS() 扩展了 NDB$EPOCH2() 函数。冲突的检测和处理方式相同,并且为复制集群分配主角色和次角色,但增加了额外条件,即在发生冲突的同一事务中更新的任何其他行也被视为存在冲突。也就是说,NDB$EPOCH2() 在从节点上重新调整单个冲突行,而 NDB$EPOCH_TRANS() 则重新调整冲突事务。
NDB$EPOCH2_TRANS() extends the
NDB$EPOCH2() function. Conflicts are detected
and handled in the same way, and assigning primary and secondary
roles to the replicating clusters, but with the extra condition
that any other rows updated in the same transaction in which the
conflict occurred are also regarded as being in conflict. That
is, NDB$EPOCH2() realigns individual
conflicting rows on the secondary, while
NDB$EPOCH_TRANS() realigns conflicting
transactions.
当 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 使用按行指定的元数据,即按最后修改的纪元来确定在主节点上来自从节点的传入复制行更改是否与本地提交的更改同时发生时;同时发生的更改被视为冲突,并随后更新异常表并重新对齐从节点。当主节点上的某一行被删除时,就无法再获取最后修改的纪元以确定任何复制操作是否冲突,这会导致冲突删除操作无法被检测到。这可能导致数据偏离,例如在一个集群上的删除操作与另一个集群上的删除和插入操作同时发生;这就是为什么在使用 NDB$EPOCH() 和 NDB$EPOCH_TRANS() 时,删除操作只能路由到一个集群的原因。Where NDB$EPOCH() and
NDB$EPOCH_TRANS() use metadata that is
specified per row, per last modified epoch, to determine on the
primary whether an incoming replicated row change from the
secondary is concurrent with a locally committed change;
concurrent changes are regarded as conflicting, with subsequent
exceptions table updates and realignment of the secondary. A
problem arises when a row is deleted on the primary so there is
no longer any last-modified epoch available to determine whether
any replicated operations conflict, which means that conflicting
delete operations are not detected. This can result in
divergence, an example being a delete on one cluster which is
concurrent with a delete and insert on the other; this why
delete operations can be routed to only one cluster when using
NDB$EPOCH() and
NDB$EPOCH_TRANS().
NDB$EPOCH2() 通过忽略任何删除-删除冲突,并避免任何可能产生的差异,从而绕过了刚才描述的问题——在主节点上存储已删除行的信息。这是通过将任何成功应用并从次节点复制过来的操作再反映回次节点来实现的。当操作返回到次节点时,它可以用于重新应用在主节点发起的操作删除掉的次节点操作。NDB$EPOCH2() bypasses the issue just
described—storing information about deleted rows on the
PRIMARY—by ignoring any delete-delete conflict, and by
avoiding any potential resultant divergence as well. This is
accomplished by reflecting any operation successfully applied on
and replicated from the secondary back to the secondary. On its
return to the secondary, it can be used to reapply an operation
on the secondary which was deleted by an operation originating
from the primary.
在使用 NDB$EPOCH2() 时,你应当记住,辅助节点会将主节点的删除操作应用到自身,移除新的行,直到通过反射操作恢复它。理论上,随后在辅助节点上的插入或更新操作会与主节点的删除操作冲突,但在这种情况下,我们选择忽略这种冲突,并允许辅助节点“胜出”,以防止集群之间产生差异。换句话说,在删除之后,主节点不会检测冲突,而是立即采用辅助节点随后的更改。正因为如此,辅助节点的状态在发展到最终(稳定)状态的过程中,可能会回到多个先前提交的状态,其中一些状态可能是可见的。When using NDB$EPOCH2(), you should keep in
mind that the secondary applies the delete from the primary,
removing the new row until it is restored by a reflected
operation. In theory, the subsequent insert or update on the
secondary conflicts with the delete from the primary, but in
this case, we choose to ignore this and allow the secondary to
“win”, in the interest of preventing divergence
between the clusters. In other words, after a delete, the
primary does not detect conflicts, and instead adopts the
secondary's following changes immediately. Because of this,
the secondary's state can revisit multiple previous
committed states as it progresses to a final (stable) state, and
some of these may be visible.
你还应该注意,将所有操作从次要反映回主服务器会增加主服务器日志二进制日志的大小,并增加对带宽、CPU 使用和磁盘 I/O 的需求。You should also be aware that reflecting all operations from the secondary back to the primary increases the size of the primary's logbinary log, as well as demands on bandwidth, CPU usage, and disk I/O.
在 secondary 上应用反射操作取决于 secondary 上目标行的状态。是否在 secondary 上应用反射更改可以通过检查 Ndb_conflict_reflected_op_prepare_count 和 Ndb_conflict_reflected_op_discard_count 状态变量来跟踪。应用的更改数量只是这两个值之间的差异(注意 Ndb_conflict_reflected_op_prepare_count 始终大于或等于 Ndb_conflict_reflected_op_discard_count)。Application of reflected operations on the secondary depends on
the state of the target row on the secondary. Whether or not
reflected changes are applied on the secondary can be tracked by
checking the
Ndb_conflict_reflected_op_prepare_count
and
Ndb_conflict_reflected_op_discard_count
status variables. The number of changes applied is simply the
difference between these two values (note that
Ndb_conflict_reflected_op_prepare_count is
always greater than or equal to
Ndb_conflict_reflected_op_discard_count).
只有在以下两个条件都为真时,事件才会被应用:Events are applied if and only if both of the following conditions are true:
行的存在——也就是说,它是否存在——取决于事件的类型。对于删除和更新操作,行必须已经存在。对于插入操作,行必须不存在。The existence of the row—that is, whether or not it exists—is in accordance with the type of event. For delete and update operations, the row must already exist. For insert operations, the row must not exist.
该行最后一次由主节点修改。该修改可能是通过执行反射操作完成的。The row was last modified by the primary. It is possible that the modification was accomplished through the execution of a reflected operation.
如果这两个条件都不满足,反射操作将被从节点丢弃。If both of these conditions are not met, the reflected operation is discarded by the secondary.
要使用 NDB$OLD() 冲突解决函数,还需要为每个打算使用此类冲突解决的 NDB 表创建一个对应的例外表。在使用 NDB$EPOCH() 或 NDB$EPOCH_TRANS() 时也是如此。此表的名称为要应用冲突解决的表名,并在其后加上字符串 $EX。(例如,如果原始表的名称是 mytable,则对应的例外表名称应为 mytable$EX。)创建例外表的语法如下所示:
To use the NDB$OLD() conflict resolution
function, it is also necessary to create an exceptions table
corresponding to each NDB table for
which this type of conflict resolution is to be employed. This
is also true when using NDB$EPOCH() or
NDB$EPOCH_TRANS(). The name of this table is
that of the table for which conflict resolution is to be
applied, with the string $EX appended. (For
example, if the name of the original table is
mytable, the name of the corresponding
exceptions table name should be mytable$EX.)
The syntax for creating the exceptions table is as shown here:
CREATE TABLEoriginal_table$EX ( [NDB$]server_id INT UNSIGNED, [NDB$]source_server_id INT UNSIGNED, [NDB$]source_epoch BIGINT UNSIGNED, [NDB$]count INT UNSIGNED, [NDB$OP_TYPE ENUM('WRITE_ROW','UPDATE_ROW', 'DELETE_ROW', 'REFRESH_ROW', 'READ_ROW') NOT NULL,] [NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS', 'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,] [NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL,]original_table_pk_columns, [orig_table_column|orig_table_column$OLD|orig_table_column$NEW,] [additional_columns,] PRIMARY KEY([NDB$]server_id, [NDB$]source_server_id, [NDB$]source_epoch, [NDB$]count) ) ENGINE=NDB;
前四列是必需的。前四列的名称以及与原始表主键列匹配的列的名称并不关键;然而,出于清晰性和一致性的考虑,我们建议您为 server_id、source_server_id、source_epoch 和 count 列使用此处显示的名称,并且对于与原始表主键列匹配的列,使用与原始表中相同的名称。The first four columns are required. The names of the first four
columns and the columns matching the original table's
primary key columns are not critical; however, we suggest for
reasons of clarity and consistency, that you use the names shown
here for the server_id,
source_server_id,
source_epoch, and count
columns, and that you use the same names as in the original
table for the columns matching those in the original
table's primary key.
如果异常表使用了本节后面讨论的一个或多个可选列 NDB$OP_TYPE、NDB$CFT_CAUSE 或 NDB$ORIG_TRANSID,则每个必需列也必须使用前缀 NDB$ 来命名。如果需要,即使不定义任何可选列,也可以使用 NDB$ 前缀来命名必需列,但在这种情况下,所有四个必需列都必须使用该前缀命名。If the exceptions table uses one or more of the optional columns
NDB$OP_TYPE,
NDB$CFT_CAUSE, or
NDB$ORIG_TRANSID discussed later in this
section, then each of the required columns must also be named
using the prefix NDB$. If desired, you can
use the NDB$ prefix to name the required
columns even if you do not define any optional columns, but in
this case, all four of the required columns must be named using
the prefix.
在这些列之后,原始表的主键组成列应按照定义原始表主键的顺序复制。用于复制原始表主键列的列的数据类型应与原始列相同(或更大)。可以使用主键列的子集。Following these columns, the columns making up the original table's primary key should be copied in the order in which they are used to define the primary key of the original table. The data types for the columns duplicating the primary key columns of the original table should be the same as (or larger than) those of the original columns. A subset of the primary key columns may be used.
异常表必须使用 NDB 存储引擎。(在本节后面将展示一个使用 NDB$OLD() 与异常表的示例。)The exceptions table must use the
NDB storage engine. (An example
that uses NDB$OLD() with an exceptions table
is shown later in this section.)
在复制的主键列之后可以选择性地定义额外的列,但不能在任何主键列之前定义;任何此类额外列都不能为 NOT NULL。NDB Cluster 支持三个额外的预定义可选列 NDB$OP_TYPE、NDB$CFT_CAUSE 和 NDB$ORIG_TRANSID,它们将在接下来的几段中进行描述。Additional columns may optionally be defined following the
copied primary key columns, but not before any of them; any such
extra columns cannot be NOT NULL. NDB Cluster
supports three additional, predefined optional columns
NDB$OP_TYPE,
NDB$CFT_CAUSE, and
NDB$ORIG_TRANSID, which are described in the
next few paragraphs.
NDB$OP_TYPE:此列可用于获取导致冲突的操作类型。如果使用此列,请按如下方式定义:
NDB$OP_TYPE: This column can be used to
obtain the type of operation causing the conflict. If you use
this column, define it as shown here:
NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
'REFRESH_ROW', 'READ_ROW') NOT NULL
WRITE_ROW、UPDATE_ROW 和 DELETE_ROW 操作类型表示用户发起的操作。REFRESH_ROW 操作是由补偿事务中的冲突解决生成并从检测到冲突的集群发送回原始集群的操作。READ_ROW 操作是用户发起的只读跟踪操作,定义了独占行锁。The WRITE_ROW, UPDATE_ROW,
and DELETE_ROW operation types represent
user-initiated operations. REFRESH_ROW
operations are operations generated by conflict resolution in
compensating transactions sent back to the originating cluster
from the cluster that detected the conflict.
READ_ROW operations are user-initiated read
tracking operations defined with exclusive row locks.
NDB$CFT_CAUSE:您可以定义一个可选列 NDB$CFT_CAUSE,用于提供已注册冲突的原因。如果使用此列,其定义如下所示:
NDB$CFT_CAUSE: You can define an optional
column NDB$CFT_CAUSE which provides the cause
of the registered conflict. This column, if used, is defined as
shown here:
NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL
ROW_DOES_NOT_EXIST 可以作为 UPDATE_ROW 和 WRITE_ROW 操作的原因报告; ROW_ALREADY_EXISTS 可以在 WRITE_ROW 事件中被报告。DATA_IN_CONFLICT 会在基于行的冲突函数检测到冲突时被报告; TRANS_IN_CONFLICT 会在事务冲突函数拒绝属于完整事务的所有操作时被报告。ROW_DOES_NOT_EXIST can be reported as the
cause for UPDATE_ROW and
WRITE_ROW operations;
ROW_ALREADY_EXISTS can be reported for
WRITE_ROW events.
DATA_IN_CONFLICT is reported when a row-based
conflict function detects a conflict;
TRANS_IN_CONFLICT is reported when a
transactional conflict function rejects all of the operations
belonging to a complete transaction.
NDB$ORIG_TRANSID:如果使用,NDB$ORIG_TRANSID 列包含源事务的 ID。此列应定义如下:
NDB$ORIG_TRANSID: The
NDB$ORIG_TRANSID column, if used, contains
the ID of the originating transaction. This column should be
defined as follows:
NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL
NDB$ORIG_TRANSID 是一个由 NDB 生成的 64 位值。该值可用于关联属于同一冲突事务的来自相同或不同异常表的多个异常表条目。NDB$ORIG_TRANSID is a 64-bit value generated
by NDB. This value can be used to correlate
multiple exceptions table entries belonging to the same
conflicting transaction from the same or different exceptions
tables.
不属于原始表主键的附加引用列可以命名为 或 colname$OLD。colname$NEW 在更新和删除操作中引用旧值——也就是说,包含 colname$OLDDELETE_ROW 事件的操作。 可用于在插入和更新操作中引用新值——换句话说,使用 colname$NEWWRITE_ROW 事件、UPDATE_ROW 事件或这两类事件的操作。如果冲突的操作未为某个非主键的引用列提供值,则异常表行包含 NULL,或该列定义的默认值。Additional reference columns which are not part of the original
table's primary key can be named
or
colname$OLD.
colname$NEW
references old values in update and delete operations—that
is, operations containing colname$OLDDELETE_ROW events.
can be
used to reference new values in insert and update
operations—in other words, operations using
colname$NEWWRITE_ROW events,
UPDATE_ROW events, or both types of events.
Where a conflicting operation does not supply a value for a
given reference column that is not a primary key, the exceptions
table row contains either NULL, or a defined
default value for that column.
该mysql.ndb_replication table is read
when a data table is set up for replication, so the row
corresponding to a table to be replicated must be inserted
into mysql.ndb_replication
before the table to be replicated is
created.The mysql.ndb_replication table is read
when a data table is set up for replication, so the row
corresponding to a table to be replicated must be inserted
into mysql.ndb_replication
before the table to be replicated is
created.
可以使用多个状态变量来监控冲突检测。您可以通过查看自从本副本上次重启以来 NDB$EPOCH() 的值,与当前 Ndb_conflict_fn_epoch 系统状态变量的值,了解有多少行被发现存在冲突。
Several status variables can be used to monitor conflict
detection. You can see how many rows have been found in conflict
by NDB$EPOCH() since this replica was last
restarted from the current value of the
Ndb_conflict_fn_epoch system
status variable.
Ndb_conflict_fn_epoch_trans 提供了通过 NDB$EPOCH_TRANS() 直接发现冲突的行数。Ndb_conflict_fn_epoch2 和 Ndb_conflict_fn_epoch2_trans 分别显示由 NDB$EPOCH2() 和 NDB$EPOCH2_TRANS() 发现冲突的行数。实际重新对齐的行数,包括那些因为与其他冲突行属于同一事务或依赖于同一事务而受影响的行,由 Ndb_conflict_trans_row_reject_count 给出。Ndb_conflict_fn_epoch_trans
provides the number of rows that have been found directly in
conflict by NDB$EPOCH_TRANS().
Ndb_conflict_fn_epoch2 and
Ndb_conflict_fn_epoch2_trans
show the number of rows found in conflict by
NDB$EPOCH2() and
NDB$EPOCH2_TRANS(), respectively. The number
of rows actually realigned, including those affected due to
their membership in or dependency on the same transactions as
other conflicting rows, is given by
Ndb_conflict_trans_row_reject_count.
另一个服务器状态变量 Ndb_conflict_fn_max 提供了自上次 mysqld 启动以来,由于 “最大时间戳获胜” 冲突解决,某行未在当前 SQL 节点上应用的次数。Ndb_conflict_fn_max_del_win 提供了基于 NDB$MAX_DELETE_WIN() 结果应用的冲突解决次数的计数。Another server status variable
Ndb_conflict_fn_max provides a
count of the number of times that a row was not applied on the
current SQL node due to “greatest timestamp wins”
conflict resolution since the last time that
mysqld was started.
Ndb_conflict_fn_max_del_win
provides a count of the number of times that conflict resolution
based on the outcome of NDB$MAX_DELETE_WIN()
has been applied.
Ndb_conflict_fn_max_ins tracks
the number of times that “greater timestamp wins”
handling has been applied to write operations (using
NDB$MAX_INS()); a count of the number of
times that “same timestamp wins” handling of writes
has been applied (as implemented by
NDB$MAX_DEL_WIN_INS()), is provided by the
status variable
Ndb_conflict_fn_max_del_win_ins.
The number of times that a row was not applied as the result of
“same timestamp wins” conflict resolution on a
given mysqld since the last time it was
restarted is given by the global status variable
Ndb_conflict_fn_old. In
addition to incrementing
Ndb_conflict_fn_old, the
primary key of the row that was not used is inserted into an
异常表, as
explained elsewhere in this section.The number of times that a row was not applied as the result of
“same timestamp wins” conflict resolution on a
given mysqld since the last time it was
restarted is given by the global status variable
Ndb_conflict_fn_old. In
addition to incrementing
Ndb_conflict_fn_old, the
primary key of the row that was not used is inserted into an
exceptions table, as
explained elsewhere in this section.
另请参见 第 25.4.3.9.3 节,“NDB 集群状态变量”。See also Section 25.4.3.9.3, “NDB Cluster Status Variables”.
以下示例假设您已经有一个正常工作的 NDB 集群复制设置,如 第 25.7.5 节,“为复制准备 NDB 集群” 中所述,以及 第 25.7.6 节,“启动 NDB 集群复制(单复制通道)” 中所述。The following examples assume that you have already a working NDB Cluster replication setup, as described in Section 25.7.5, “Preparing the NDB Cluster for Replication”, and Section 25.7.6, “Starting NDB Cluster Replication (Single Replication Channel)”.
NDB$MAX() 示例。 假设您希望在表 test.t1 上启用 “最新时间戳优先” 冲突解决,并使用列 mycol 作为 “时间戳”。这可以通过以下步骤完成:NDB$MAX() example.
Suppose you wish to enable “greatest timestamp
wins” conflict resolution on table
test.t1, using column
mycol as the “timestamp”. This
can be done using the following steps:
确保您已经使用 mysqld 启动了源服务器,并启用了
--ndb-log-update-as-write=OFF。Make sure that you have started the source mysqld with--ndb-log-update-as-write=OFF.在源服务器上,执行以下
INSERT语句:On the source, perform thisINSERTstatement:INSERT INTO mysql.ndb_replication VALUES ('test', 't1', 0, NULL, 'NDB$MAX(mycol)');Note如果
ndb_replication表尚不存在,则必须创建它。参见 ndb_replication 表。If thendb_replicationtable does not already exist, you must create it. See ndb_replication Table.在
server_id列中插入 0 表示所有访问此表的 SQL 节点都应使用冲突解决。如果您只想在特定的 mysqld 上使用冲突解决,请使用实际的服务器 ID。Inserting a 0 into theserver_idcolumn indicates that all SQL nodes accessing this table should use conflict resolution. If you want to use conflict resolution on a specific mysqld only, use the actual server ID.在
binlog_type列中插入NULL的效果与插入 0 (NBT_DEFAULT) 相同;将使用服务器默认值。InsertingNULLinto thebinlog_typecolumn has the same effect as inserting 0 (NBT_DEFAULT); the server default is used.创建
test.t1表:Create thetest.t1table:CREATE TABLE test.t1 (columnsmycol INT UNSIGNED,columns) ENGINE=NDB;现在,当对此表进行更新时,将应用冲突解决,并且具有
mycol最大值的行版本将写入副本。Now, when updates are performed on this table, conflict resolution is applied, and the version of the row having the greatest value formycolis written to the replica.
其他 binlog_type 选项,例如 NBT_UPDATED_ONLY_USE_UPDATE (6),应该用于通过 ndb_replication 表控制源上的日志记录,而不是通过命令行选项。Other binlog_type options such as
NBT_UPDATED_ONLY_USE_UPDATE
(6) should be used to control logging on
the source using the ndb_replication table
rather than by using command-line options.
NDB$OLD() 示例。 假设像此处定义的 NDB 表正在被复制,并且您希望为此表的更新启用 “相同时间戳获胜” 冲突解决:NDB$OLD() example.
Suppose an NDB table such as the
one defined here is being replicated, and you wish to enable
“same timestamp wins” conflict resolution for
updates to this table:
CREATE TABLE test.t2 (
a INT UNSIGNED NOT NULL,
b CHAR(25) NOT NULL,
columns,
mycol INT UNSIGNED NOT NULL,
columns,
PRIMARY KEY pk (a, b)
) ENGINE=NDB;
需要按以下顺序执行以下步骤:The following steps are required, in the order shown:
First—and prior to creating
test.t2—you must insert a row into themysql.ndb_replicationtable, as shown here:INSERT INTO mysql.ndb_replication VALUES ('test', 't2', 0, 0, 'NDB$OLD(mycol)');binlog_type列的可能值在本节前面已显示;在这种情况下,我们使用0指定使用服务器的默认日志记录行为。值'NDB$OLD(mycol)'应插入到conflict_fn列中。Possible values for thebinlog_typecolumn are shown earlier in this section; in this case, we use0to specify that the server default logging behavior be used. The value'NDB$OLD(mycol)'should be inserted into theconflict_fncolumn.为
test.t2创建一个适当的异常表。此处显示的表创建语句包含所有必需的列;任何附加列必须在这些列之后声明,并在表的主键定义之前。Create an appropriate exceptions table fortest.t2. The table creation statement shown here includes all required columns; any additional columns must be declared following these columns, and before the definition of the table's primary key.CREATE TABLE test.t2$EX ( server_id INT UNSIGNED, source_server_id INT UNSIGNED, source_epoch BIGINT UNSIGNED, count INT UNSIGNED, a INT UNSIGNED NOT NULL, b CHAR(25) NOT NULL, [additional_columns,] PRIMARY KEY(server_id, source_server_id, source_epoch, count) ) ENGINE=NDB;我们可以包括关于给定冲突的类型、原因和源事务 ID 的附加信息列。我们也不需要为原表中的所有主键列提供匹配的列。这意味着您可以像这样创建异常表:We can include additional columns for information about the type, cause, and originating transaction ID for a given conflict. We are also not required to supply matching columns for all primary key columns in the original table. This means you can create the exceptions table like this:
CREATE TABLE test.t2$EX ( NDB$server_id INT UNSIGNED, NDB$source_server_id INT UNSIGNED, NDB$source_epoch BIGINT UNSIGNED, NDB$count INT UNSIGNED, a INT UNSIGNED NOT NULL, NDB$OP_TYPE ENUM('WRITE_ROW','UPDATE_ROW', 'DELETE_ROW', 'REFRESH_ROW', 'READ_ROW') NOT NULL, NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS', 'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL, NDB$ORIG_TRANSID BIGINT UNSIGNED NOT NULL, [additional_columns,] PRIMARY KEY(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count) ) ENGINE=NDB;Note由于我们在表定义中至少包含了
NDB$OP_TYPE、NDB$CFT_CAUSE或NDB$ORIG_TRANSID其中之一,因此四个必需列需要使用NDB$前缀。TheNDB$prefix is required for the four required columns since we included at least one of the columnsNDB$OP_TYPE,NDB$CFT_CAUSE, orNDB$ORIG_TRANSIDin the table definition.按照前面显示的方式创建表
test.t2。Create the tabletest.t2as shown previously.
要对希望使用 NDB$OLD() 进行冲突解决的每个表都必须遵循这些步骤。对于每个这样的表,必须在 mysql.ndb_replication 中有一个对应的行,并且在被复制的表所在的同一个数据库中必须有一个异常表。These steps must be followed for every table for which you wish
to perform conflict resolution using
NDB$OLD(). For each such table, there must be
a corresponding row in mysql.ndb_replication,
and there must be an exceptions table in the same database as
the table being replicated.
读取冲突检测与解决。 NDB Cluster 还支持跟踪读取操作,这使得在环形复制设置中,可以管理一个集群中对某行的读取与另一个集群中对同一行的更新或删除之间的冲突。此示例使用 employee 和 department 表来模拟这样一个场景:在源集群(下文称为集群 A)中,将一名员工从一个部门调动到另一个部门,而在副本集群(下文称为 B)中,以交错事务的方式更新该员工原部门的员工数量。Read conflict detection and resolution.
NDB Cluster also supports tracking of read operations, which
makes it possible in circular replication setups to manage
conflicts between reads of a given row in one cluster and
updates or deletes of the same row in another. This example
uses employee and
department tables to model a scenario in
which an employee is moved from one department to another on
the source cluster (which we refer to hereafter as cluster
A) while the replica cluster (hereafter
B) updates the employee count of the
employee's former department in an interleaved
transaction.
表已使用以下 SQL 语句创建:The data tables have been created using the following SQL statements:
# Employee table
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(2000),
dept INT NOT NULL
) ENGINE=NDB;
# Department table
CREATE TABLE department (
id INT PRIMARY KEY,
name VARCHAR(2000),
members INT
) ENGINE=NDB;
这两个表的内容包括以下 SELECT 语句的(部分)输出中显示的行:The contents of the two tables include the rows shown in the
(partial) output of the following
SELECT statements:
mysql>SELECT id, name, dept FROM employee;+---------------+------+ | id | name | dept | +------+--------+------+ ... | 998 | Mike | 3 | | 999 | Joe | 3 | | 1000 | Mary | 3 | ... +------+--------+------+ mysql>SELECT id, name, members FROM department;+-----+-------------+---------+ | id | name | members | +-----+-------------+---------+ ... | 3 | Old project | 24 | ... +-----+-------------+---------+
我们假设我们已经在使用一个异常表,它包括四个必需的列(并且这些列用于该表的主键)、操作类型和原因的可选列,以及原始表的主键列,该表是使用此处显示的 SQL 语句创建的:We assume that we are already using an exceptions table that includes the four required columns (and these are used for this table's primary key), the optional columns for operation type and cause, and the original table's primary key column, created using the SQL statement shown here:
CREATE TABLE employee$EX (
NDB$server_id INT UNSIGNED,
NDB$source_server_id INT UNSIGNED,
NDB$source_epoch BIGINT UNSIGNED,
NDB$count INT UNSIGNED,
NDB$OP_TYPE ENUM( 'WRITE_ROW','UPDATE_ROW', 'DELETE_ROW',
'REFRESH_ROW','READ_ROW') NOT NULL,
NDB$CFT_CAUSE ENUM( 'ROW_DOES_NOT_EXIST',
'ROW_ALREADY_EXISTS',
'DATA_IN_CONFLICT',
'TRANS_IN_CONFLICT') NOT NULL,
id INT NOT NULL,
PRIMARY KEY(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count)
) ENGINE=NDB;
假设在两个集群上同时发生两个事务。在集群 A 上,我们创建一个新部门,然后将编号为 999 的员工调入该部门,使用以下 SQL 语句:Suppose there occur the two simultaneous transactions on the two clusters. On cluster A, we create a new department, then move employee number 999 into that department, using the following SQL statements:
BEGIN;INSERTINTO department VALUES (4, "New project", 1);UPDATEemployee SET dept = 4 WHERE id = 999; COMMIT;
同时,在集群 B 上,另一个事务从 employee 表读取,如下所示:At the same time, on cluster B, another
transaction reads from employee, as shown
here:
BEGIN;
SELECT name FROM employee WHERE id = 999;
UPDATE department SET members = members - 1 WHERE id = 3;
commit;
冲突事务通常不会被冲突解决机制检测到,因为冲突发生在读取操作(SELECT)与更新操作之间。您可以通过在副本集群上执行 SET ndb_log_exclusive_reads = 1 来规避此问题。以这种方式获取独占读取锁会导致在源端读取的任何行都被标记为需要在副本集群上进行冲突解决。如果我们在记录这些事务之前以这种方式启用独占读取,集群 B 的读取将被跟踪并发送到集群 A 进行解决;随后将检测到员工行的冲突,并且集群 B 上的事务将被中止。The conflicting transactions are not normally detected by the
conflict resolution mechanism, since the conflict is between a
read (SELECT) and an update operation. You
can circumvent this issue by executing
SET
ndb_log_exclusive_reads
= 1 on the replica cluster. Acquiring
exclusive read locks in this way causes any rows read on the
source to be flagged as needing conflict resolution on the
replica cluster. If we enable exclusive reads in this way prior
to the logging of these transactions, the read on cluster
B is tracked and sent to cluster
A for resolution; the conflict on the
employee row is subsequently detected and the transaction on
cluster B is aborted.
冲突会在异常表中注册(在集群 A 上)作为 READ_ROW 操作(参见 冲突解决异常表,了解操作类型的描述),如下所示:The conflict is registered in the exceptions table (on cluster
A) as a READ_ROW
operation (see
Conflict Resolution Exceptions Table, for a
description of operation types), as shown here:
mysql> SELECT id, NDB$OP_TYPE, NDB$CFT_CAUSE FROM employee$EX;
+-------+-------------+-------------------+
| id | NDB$OP_TYPE | NDB$CFT_CAUSE |
+-------+-------------+-------------------+
...
| 999 | READ_ROW | TRANS_IN_CONFLICT |
+-------+-------------+-------------------+
在读取操作中发现的任何现有行都会被标记。这意味着,由同一冲突引起的多行可能会记录在异常表中,如通过检查来自同一表在同时事务中,集群 A 的更新与集群 B 上多个行的读取之间冲突的影响所示。集群 A 上执行的事务如下所示:Any existing rows found in the read operation are flagged. This means that multiple rows resulting from the same conflict may be logged in the exception table, as shown by examining the effects a conflict between an update on cluster A and a read of multiple rows on cluster B from the same table in simultaneous transactions. The transaction executed on cluster A is shown here:
BEGIN;
INSERT INTO department VALUES (4, "New project", 0);
UPDATE employee SET dept = 4 WHERE dept = 3;
SELECT COUNT(*) INTO @count FROM employee WHERE dept = 4;
UPDATE department SET members = @count WHERE id = 4;
COMMIT;
同时,包含此处显示语句的事务在集群 B 上运行:Concurrently a transaction containing the statements shown here runs on cluster B:
SET ndb_log_exclusive_reads = 1; # Must be set if not already enabled
...
BEGIN;
SELECT COUNT(*) INTO @count FROM employee WHERE dept = 3 FOR UPDATE;
UPDATE department SET members = @count WHERE id = 3;
COMMIT;
在这种情况下,第二个事务的 SELECT 语句中 WHERE 条件匹配的所有三行都会被读取,因此在异常表中被标记,如下所示:In this case, all three rows matching the
WHERE condition in the second
transaction's SELECT are read, and are
thus flagged in the exceptions table, as shown here:
mysql> SELECT id, NDB$OP_TYPE, NDB$CFT_CAUSE FROM employee$EX;
+-------+-------------+-------------------+
| id | NDB$OP_TYPE | NDB$CFT_CAUSE |
+-------+-------------+-------------------+
...
| 998 | READ_ROW | TRANS_IN_CONFLICT |
| 999 | READ_ROW | TRANS_IN_CONFLICT |
| 1000 | READ_ROW | TRANS_IN_CONFLICT |
...
+-------+-------------+-------------------+
读取跟踪仅基于现有行执行。基于给定条件的读取跟踪只会检查 找到 的行是否存在冲突,而不会检查在交错事务中插入的任何行。这类似于在单实例 NDB 集群中执行排他行锁的方式。Read tracking is performed on the basis of existing rows only. A read based on a given condition track conflicts only of any rows that are found and not of any rows that are inserted in an interleaved transaction. This is similar to how exclusive row locking is performed in a single instance of NDB Cluster.
插入冲突检测和解决示例。 下列示例说明了插入冲突检测函数的使用。我们假设正在复制数据库 test 中的两个表 t1 和 t2,并希望对 t1 使用 NDB$MAX_INS() 进行插入冲突检测,对 t2 使用 NDB$MAX_DEL_WIN_INS() 进行插入冲突检测。这两个表在后续设置过程中才创建。Insert conflict detection and resolution example.
The following example illustrates the use of insert conflict
detection functions. We assume that we are replicating two
tables t1 and t2 in
database test, and that we wish to use
insert conflict detection with
NDB$MAX_INS() for t1 and
NDB$MAX_DEL_WIN_INS() for
t2. The two data tables are not created
until later in the setup process.
设置插入冲突解决与设置其他冲突检测和解决算法类似,如前面的示例所示。如果用于配置二进制日志记录和冲突解决的 mysql.ndb_replication 表尚不存在,则首先需要创建它,如下所示:Setting up insert conflict resolution is similar to setting up
other conflict detection and resolution algorithms as shown in
the previous examples. If the
mysql.ndb_replication table used to configure
binary logging and conflict resolution, does not already exist,
it is first necessary to create it, as shown here:
CREATE TABLE mysql.ndb_replication (
db VARBINARY(63),
table_name VARBINARY(63),
server_id INT UNSIGNED,
binlog_type INT UNSIGNED,
conflict_fn VARBINARY(128),
PRIMARY KEY USING HASH (db, table_name, server_id)
) ENGINE=NDB
PARTITION BY KEY(db,table_name);
ndb_replication 表是按表进行操作的;也就是说,我们需要为每个要设置的表插入一行,包含表信息、binlog_type 值、要使用的冲突解决函数,以及时间戳列的名称 (X),如下所示:The ndb_replication table acts on a per-table
basis; that is, we need to insert a row containing table
information, a binlog_type value, the
conflict resolution function to be employed, and the name of the
timestamp column (X) for each table to be set
up, like this:
INSERT INTO mysql.ndb_replication VALUES ("test", "t1", 0, 7, "NDB$MAX_INS(X)");
INSERT INTO mysql.ndb_replication VALUES ("test", "t2", 0, 7, "NDB$MAX_DEL_WIN_INS(X)");
这里我们将 binlog_type 设置为 NBT_FULL_USE_UPDATE(7),这意味着始终记录完整的行。其他可能的值请参见 ndb_replication 表。Here we have set the binlog_type as
NBT_FULL_USE_UPDATE (7)
which means that full rows are always logged. See
ndb_replication Table, for other
possible values.
您还可以为每个需要使用冲突解决的 NDB 表创建一个异常表。异常表记录给定表被冲突解决函数拒绝的所有行。用于表 t1 和 t2 的复制冲突检测的异常表可以使用以下两个 SQL 语句创建:You can also create an exceptions table corresponding to each
NDB table for which conflict resolution is to
be employed. An exceptions table records all rows rejected by
the conflict resolution function for a given table. Exceptions
tables for replication conflict detection for tables
t1 and t2 can be created
using the following two SQL statements:
CREATE TABLE `t1$EX` (
NDB$server_id INT UNSIGNED,
NDB$source_server_id INT UNSIGNED,
NDB$source_epoch BIGINT UNSIGNED,
NDB$count INT UNSIGNED,
NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
'REFRESH_ROW', 'READ_ROW') NOT NULL,
NDB$CFT_CAUSE ENUM('ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,
a INT NOT NULL,
PRIMARY KEY(NDB$server_id, NDB$source_server_id,
NDB$source_epoch, NDB$count)
) ENGINE=NDB;
CREATE TABLE `t2$EX` (
NDB$server_id INT UNSIGNED,
NDB$source_server_id INT UNSIGNED,
NDB$source_epoch BIGINT UNSIGNED,
NDB$count INT UNSIGNED,
NDB$OP_TYPE ENUM('WRITE_ROW', 'UPDATE_ROW', 'DELETE_ROW',
'REFRESH_ROW', 'READ_ROW') NOT NULL,
NDB$CFT_CAUSE ENUM( 'ROW_DOES_NOT_EXIST', 'ROW_ALREADY_EXISTS',
'DATA_IN_CONFLICT', 'TRANS_IN_CONFLICT') NOT NULL,
a INT NOT NULL,
PRIMARY KEY(NDB$server_id, NDB$source_server_id,
NDB$source_epoch, NDB$count)
) ENGINE=NDB;
最后,在创建了上面显示的异常表之后,你可以使用以下两个 SQL 语句创建要复制的表并受冲突解决控制:Finally, after creating the exception tables just shown, you can create the data tables to be replicated and subject to conflict resolution control, using the following two SQL statements:
CREATE TABLE t1 (
a INT PRIMARY KEY,
b VARCHAR(32),
X INT UNSIGNED
) ENGINE=NDB;
CREATE TABLE t2 (
a INT PRIMARY KEY,
b VARCHAR(32),
X INT UNSIGNED
) ENGINE=NDB;
对于每个表,X 列用作时间戳列。For each table, the X column is used as the
timestamp column.
在源上创建后,t1 和 t2 会被复制,并且可以认为它们存在于源和副本上。在本例的其余部分,我们使用 mysqlS> 来表示连接到源的 mysql 客户端,使用 mysqlR> 来表示在副本上运行的 mysql 客户端。Once created on the source, t1 and
t2 are replicated and can be assumed to exist
on both the source and the replica. In the remainder of this
example, we use mysqlS> to indicate a
mysql client connected to the source, and
mysqlR> to indicate a
mysql client running on the replica.
首先,我们在源上的表中各插入一行,如下所示:First we insert one row each into the tables on the source, like this:
mysqlS>INSERT INTO t1 VALUES (1, 'Initial X=1', 1);Query OK, 1 row affected (0.01 sec) mysqlS>INSERT INTO t2 VALUES (1, 'Initial X=1', 1);Query OK, 1 row affected (0.01 sec)
我们可以确定这两行在复制时不会引起任何冲突,因为在对源执行 INSERT 语句之前,副本上的表中没有任何数据。我们可以通过如下方式从副本上的表中进行查询来验证这一点:We can be certain that these two rows are replicated without
causing any conflicts, since the tables on the replica did not
contain any rows prior to issuing the
INSERT statements on the source.
We can verify this by selecting from the tables on the replica
as shown here:
mysqlR>TABLE t1 ORDER BY a;+---+-------------+------+ | a | b | X | +---+-------------+------+ | 1 | Initial X=1 | 1 | +---+-------------+------+ 1 row in set (0.00 sec) mysqlR>TABLE t2 ORDER BY a;+---+-------------+------+ | a | b | X | +---+-------------+------+ | 1 | Initial X=1 | 1 | +---+-------------+------+ 1 row in set (0.00 sec)
接下来,我们向副本上的表中插入新行,如下所示:Next, we insert new rows into the tables on the replica, like this:
mysqlR>INSERT INTO t1 VALUES (2, 'Replica X=2', 2);Query OK, 1 row affected (0.01 sec) mysqlR>INSERT INTO t2 VALUES (2, 'Replica X=2', 2);Query OK, 1 row affected (0.01 sec)
现在,我们在源表中插入具有更大时间戳(X)列值的冲突行,使用如下语句:Now we insert conflicting rows into the tables on the source
having greater timestamp (X) column values,
using the statements shown here:
mysqlS>INSERT INTO t1 VALUES (2, 'Replica X=20', 20);Query OK, 1 row affected (0.01 sec) mysqlS>INSERT INTO t2 VALUES (2, 'Replica X=20', 20);Query OK, 1 row affected (0.01 sec)
现在我们通过再次从副本上的两个表中进行查询来观察结果,如下所示:Now we observe the results by selecting (again) from both tables on the replica, as shown here:
mysqlR>TABLE t1 ORDER BY a;+---+-------------+-------+ | a | b | X | +---+-------------+-------+ | 1 | Initial X=1 | 1 | +---+-------------+-------+ | 2 | Source X=20 | 20 | +---+-------------+-------+ 2 rows in set (0.00 sec) mysqlR>TABLE t2 ORDER BY a;+---+-------------+-------+ | a | b | X | +---+-------------+-------+ | 1 | Initial X=1 | 1 | +---+-------------+-------+ | 1 | Source X=20 | 20 | +---+-------------+-------+ 2 rows in set (0.00 sec)
在源端插入的行,其时间戳大于副本中冲突行的时间戳,因此已替换了这些行。在副本上,我们接下来插入两行新数据,这些数据与 t1 或 t2 中现有的任何行都不冲突,如下所示:The rows inserted on the source, having greater timestamps than
those in the conflicting rows on the replica, have replaced
those rows. On the replica, we next insert two new rows which do
not conflict with any existing rows in t1 or
t2, like this:
mysqlR>INSERT INTO t1 VALUES (3, 'Replica X=30', 30);Query OK, 1 row affected (0.01 sec) mysqlR>INSERT INTO t2 VALUES (3, 'Replica X=30', 30);Query OK, 1 row affected (0.01 sec)
在源端插入更多具有相同主键值(3)的行会像以前一样引发冲突,但这次我们使用的时间戳列的值小于副本中冲突行的对应列的值。Inserting more rows on the source with the same primary key
value (3) brings about conflicts as before,
but this time we use a value for the timestamp column less than
that in same column in the conflicting rows on the replica.
mysqlS>INSERT INTO t1 VALUES (3, 'Source X=3', 3);Query OK, 1 row affected (0.01 sec) mysqlS>INSERT INTO t2 VALUES (3, 'Source X=3', 3);Query OK, 1 row affected (0.01 sec)
通过查询表可以看到,副本已拒绝源端的两个插入,并且之前在副本上插入的行没有被覆盖,如下所示,在副本上的 mysql 客户端中:We can see by querying the tables that both inserts from the source were rejected by the replica, and the rows inserted on the replica previously have not been overwritten, as shown here in the mysql client on the replica:
mysqlR>TABLE t1 ORDER BY a;+---+--------------+-------+ | a | b | X | +---+--------------+-------+ | 1 | Initial X=1 | 1 | +---+--------------+-------+ | 2 | Source X=20 | 20 | +---+--------------+-------+ | 3 | Replica X=30 | 30 | +---+--------------+-------+ 3 rows in set (0.00 sec) mysqlR>TABLE t2 ORDER BY a;+---+--------------+-------+ | a | b | X | +---+--------------+-------+ | 1 | Initial X=1 | 1 | +---+--------------+-------+ | 2 | Source X=20 | 20 | +---+--------------+-------+ | 3 | Replica X=30 | 30 | +---+--------------+-------+ 3 rows in set (0.00 sec)
您可以在异常表中查看被拒绝的行的信息,如下所示:You can see information about the rows that were rejected in the exception tables, as shown here:
mysqlR>SELECT NDB$server_id, NDB$source_server_id, NDB$count,>NDB$OP_TYPE, NDB$CFT_CAUSE, a>FROM t1$EX>ORDER BY NDB$count\G*************************** 1. row *************************** NDB$server_id : 2 NDB$source_server_id: 1 NDB$count : 1 NDB$OP_TYPE : WRITE_ROW NDB$CFT_CAUSE : DATA_IN_CONFLICT a : 3 1 row in set (0.00 sec) mysqlR>SELECT NDB$server_id, NDB$source_server_id, NDB$count,>NDB$OP_TYPE, NDB$CFT_CAUSE, a>FROM t2$EX>ORDER BY NDB$count\G*************************** 1. row *************************** NDB$server_id : 2 NDB$source_server_id: 1 NDB$count : 1 NDB$OP_TYPE : WRITE_ROW NDB$CFT_CAUSE : DATA_IN_CONFLICT a : 3 1 row in set (0.00 sec)
正如我们之前所看到的,源上插入的其他行没有被副本拒绝,只有那些时间戳值小于副本冲突行的行被拒绝。As we saw earlier, no other rows inserted on the source were rejected by the replica, only those rows having a lesser timestamp value than the rows in conflict on the replica.