在使用涉及多个源(包括循环复制)的复制设置时,不同源可能会尝试使用不同的数据更新副本上的同一行。NDB Cluster Replication中的冲突解决提供了一种解决此类冲突的方法,通过允许用户定义的解决列来确定是否应该在副本上应用给定源上的更新。
NDB集群支持的一些冲突解决类型(旧NDB $ ()
,NDB MAX()美元
,NDB MAX_DELETE_WIN美元()
)将这个用户定义的列实现为a”时间戳”列(尽管其类型不能为时间戳
,如本节稍后解释)。这些类型的冲突解决总是逐行应用,而不是以事务为基础。基于时代的冲突解决功能NDB美元时代()
而且NDB EPOCH_TRANS美元()
比较复制epoch的顺序(因此这些函数是事务性的)。当冲突发生时,可以使用不同的方法来比较副本上的分辨率列值,本节稍后将解释;所使用的方法可以在每个表的基础上设置。
您还应该记住,应用程序有责任确保解析列正确地填充相关值,以便解析函数在确定是否应用更新时能够做出适当的选择。
必须在源和副本上都为解决冲突做准备。这些任务描述如下:
在写入二进制日志的源上,必须确定发送哪些列(所有列或仅发送已更新的列)。对于整个MySQL服务器,这是通过应用mysqld启动选项
——ndb-log-updated-only
(本节稍后将描述)或在每个表的基础上mysql.ndb_replication
表(参见ndb_replication表).请注意如果您正在复制具有非常大的列的表(例如
文本
或团
列),——ndb-log-updated-only
还可以用于减少二进制日志的大小,并避免可能因超过而导致的复制失败max_allowed_packet
.看到第17.5.1.20节,复制和max_allowed_packet,以获得有关此问题的更多信息。
在副本上,必须确定应用哪种类型的冲突解决方案(”最新时间戳胜出”,”相同的时间戳获胜”,”主胜”,”主导者获胜,交易完成”,或没有)。这是使用
mysql.ndb_replication
系统表,在每个表的基础上(见ndb_replication表).NDB Cluster还支持读冲突检测,即检测一个集群中给定行的读操作与另一个集群中相同行的更新或删除操作之间的冲突。这需要通过设置获得独占读锁
ndb_log_exclusive_reads
在副本上等于1。冲突的读操作读取的所有行都记录在异常表中。有关更多信息,请参见读取冲突检测和解决.
使用函数时旧NDB $ ()
,NDB MAX()美元
,NDB MAX_DELETE_WIN美元()
对于基于时间戳的冲突解决,我们通常将用于确定更新的列称为”时间戳”列。但是,此列的数据类型从来不是时间戳
;相反,它的数据类型应该是INT
(整数
)或长整型数字
.的”时间戳”列也应该是无符号
而且非空
.
的NDB美元时代()
而且NDB EPOCH_TRANS美元()
本节后面讨论的函数是通过比较应用在主NDB集群和备用NDB集群上的复制周期的相对顺序来工作的,而不使用时间戳。
我们可以看到更新操作”之前”而且”后”映像—即应用更新前后表的状态。通常,在更新带有主键的表时,”之前”图像不是很有趣;但是,当我们需要在每次更新的基础上确定是否在副本上使用更新后的值时,我们需要确保两个映像都被写入源的二进制日志。这是用——ndb-log-update-as-write
选择mysqld,如本节后面所述。
记录全部或部分行。属性的设置决定了记录行的两种基本方法,以解决冲突——ndb-log-updated-only
选择mysqld:
记录完整的行(选项设置为
在
)只记录已更新的列数据—即已设置值的列数据,而不管该值是否实际更改。这是默认行为(选项设置为
从
).
通常只记录更新的列就足够了,而且更有效;但是,如果需要记录完整的行,可以通过设置——ndb-log-updated-only
来0
或从
.
将更改的数据记录为更新。MySQL服务器的设置——ndb-log-update-as-write
选项确定是否使用”之前”的形象。因为冲突解决是在MySQL服务器的更新处理程序中完成的,所以有必要控制由复制源执行的日志记录,以便更新是更新而不是写入;也就是说,更新被视为对现有行的更改,而不是写入新行,即使这些新行替换了现有行。默认情况下,该选项是打开的;换句话说,更新被视为写入。也就是说,更新在默认情况下被写入write_row
事件在二进制日志中,而不是作为update_row
事件。
若要禁用该选项,请启动源mysqld与——ndb-log-update-as-write = 0
或——ndb-log-update-as-write =了
.当从NDB表复制到使用不同存储引擎的表时,必须这样做;看到从NDB复制到其他存储引擎,从NDB复制到非事务性存储引擎,以获取更多资料。
冲突解决通常在可能发生冲突的服务器上启用。与日志记录方法选择一样,它由mysql.ndb_replication
表格
NBT_UPDATED_ONLY_MINIMAL
而且NBT_UPDATED_FULL_MINIMAL
可与NDB美元时代()
,NDB EPOCH2美元()
,NDB EPOCH_TRANS美元()
,因为这些不需要”之前”不是主键的列的值。冲突解决算法需要旧值,例如NDB MAX()美元
而且旧NDB $ ()
,不能正确使用这些binlog_type
值。
下面几段将详细介绍NDB Replication用于冲突检测和解决的功能。
NDB老美元(column_name)。如果的值column_name
在源和副本上都是相同的,则应用更新;否则,更新不会应用于副本,并将异常写入日志。下面的伪代码说明了这一点:
如果(source_old_column_value= =replica_current_column_value) apply_update ();其他log_exception ();
此函数可用于”相同的值获胜”解决冲突的方法。这种类型的冲突解决方案确保不会从错误的源对副本应用更新。
源的列值”之前”Image被这个函数使用。
NDB马克斯(column_name)美元。如果”时间戳”如果来自源的给定行的列值高于副本上的列值,则应用该列值;否则它不会应用于副本。下面的伪代码说明了这一点:
如果(source_new_column_value>replica_current_column_value) apply_update ();
此函数可用于”时间戳最大的赢家”解决冲突的方法。这种类型的冲突解决方案确保,在发生冲突时,最近更新的行版本是持续存在的版本。
源的列值”后”Image被这个函数使用。
NDB MAX_DELETE_WIN美元()。这是关于NDB MAX()美元
.由于删除操作没有可用的时间戳,因此使用NDB MAX()美元
实际上是处理为美元NDB老
,但对于某些用例,这不是最优的。为NDB MAX_DELETE_WIN美元()
,如果”时间戳”如果添加或更新来自源的现有行的给定行的列值高于副本上的列值,则应用它。但是,删除操作总是被视为具有较高的值。下面的伪代码说明了这一点:
如果((source_new_column_value>replica_current_column_value) | |operation.type== "delete") apply_update();
此函数可用于”时间戳最大,删除胜出”解决冲突的方法。这种类型的冲突解决方案确保,在发生冲突时,被删除或(以其他方式)最近更新的行版本是持续存在的版本。
与NDB MAX()美元
,即源的列值”后”Image是这个函数使用的值。
NDB美元时代()。的NDB美元时代()
函数跟踪在副本集群上应用复制的epoch相对于在副本上发起的更改的顺序。这种相对顺序用于确定源自副本的更改是否与源自本地的任何更改是并发的,因此可能存在冲突。
下面描述的大部分内容NDB美元时代()
也适用于NDB EPOCH_TRANS美元()
.任何例外都在文本中注明。
NDB美元时代()
是不对称的,在一个双向复制配置(有时被称为”active - active”复制)。这里我们指的是集群,它在其中作为主集群,另一个作为辅助集群。主服务器上的副本负责检测和处理冲突,而辅助服务器上的副本不涉及任何冲突检测或处理。
当主服务器上的副本检测到冲突时,它将事件注入到自己的二进制日志中以弥补这些冲突;这确保了备用NDB集群最终与主db集群重新对齐,从而防止主db集群和备用db集群分道扬镳。这种补偿和重新调整机制要求主NDB集群总是能够在与备用集群的冲突中获胜——也就是说,在发生冲突时,总是使用主集群的更改,而不是备用集群的更改。这”初选总是赢家”规则含义如下:
一旦在主服务器上提交了更改数据的操作,则是完全持久的,不会通过冲突检测和解决来撤销或回滚。
从主节点读取的数据完全一致。在Primary上提交的任何更改(本地的或从副本提交的)稍后都不会恢复。
如果主服务器认为更改次要服务器上的数据的操作发生冲突,那么稍后可能会恢复这些操作。
在辅助服务器上读取的各个行在任何时候都是自一致的,每一行总是反映辅助服务器提交的状态或主服务器提交的状态。
在辅助服务器上读取的行集在给定的单个时间点上不一定是一致的。为
NDB EPOCH_TRANS美元()
,这是一个暂态;为NDB美元时代()
,它可以是一个持久的状态。假设一段足够长的时间内没有任何冲突,备用NDB集群上的所有数据(最终)都与主节点的数据一致。
NDB美元时代()
而且NDB EPOCH_TRANS美元()
不需要修改任何用户模式,也不需要更改应用程序来提供冲突检测。但是,必须仔细考虑所使用的模式和所使用的访问模式,以验证整个系统是否在指定的限制内运行。
每一个NDB美元时代()
而且NDB EPOCH_TRANS美元()
函数可以带一个可选参数;这是用来表示epoch的低32位的比特数,并且应该设置为不小于如下所示的计算值:
CEIL(LOG2(timebetweenglobalcheckpoint / TimeBetweenEpochs), 1)
对于这些配置参数的默认值(分别为2000和100毫秒),这给出了5位的值,因此默认值(6)应该足够了,除非使用其他值TimeBetweenGlobalCheckpoints
,TimeBetweenEpochs
,或者两者都有。值太小可能导致误报,而值太大可能导致数据库中浪费过多的空间。
这两个NDB美元时代()
而且NDB EPOCH_TRANS美元()
将冲突行的条目插入到相关的异常表中,前提是这些表是根据本节其他地方描述的相同的异常表模式规则定义的(参见NDB老美元(column_name)).在创建将与异常表一起使用的数据表之前,必须创建异常表。
与本节中讨论的其他冲突检测函数一样,NDB美元时代()
而且NDB EPOCH_TRANS美元()
通过在?中包含相关条目来激活mysql.ndb_replication
表(参见ndb_replication表).在此场景中,主、备NDB集群的角色完全由mysql.ndb_replication
表条目。
因为冲突检测算法所采用的NDB美元时代()
而且NDB EPOCH_TRANS美元()
是不对称的,必须使用不同的值server_id
主副本和从副本的条目。
两者之间的冲突删除
操作本身不足以触发冲突NDB美元时代()
或NDB EPOCH_TRANS美元()
,在epoch内的相对位置并不重要。(错误# 18459944)
有关更多信息,请参见NDB$EPOCH()的限制.
NDB EPOCH_TRANS美元()。NDB EPOCH_TRANS美元()
扩展了NDB美元时代()
函数。类以相同的方式检测和处理冲突”初选胜出”规则(见NDB美元时代()),但附加的条件是,在发生冲突的同一事务中更新的任何其他行也被视为冲突。换句话说,在哪里NDB美元时代()
重新排列次要节点上相互冲突的行,NDB EPOCH_TRANS美元()
重新对齐冲突的事务。
此外,任何可检测到依赖于冲突事务的事务也被视为处于冲突中,这些依赖关系由次要集群的二进制日志的内容决定。由于二进制日志只包含数据修改操作(插入、更新和删除),因此只使用重叠的数据修改来确定事务之间的依赖关系。
NDB EPOCH_TRANS美元()
是否受到相同的条件和限制NDB美元时代()
,并且还要求所有事务id都记录在二级服务器的二进制日志中——ndb-log-transaction-id
选项),这会增加一个变量开销(每行最多13个字节)。弃用的log_bin_use_v1_row_events
系统变量,默认为从
,不能设置为在
与NDB EPOCH_TRANS美元()
.
看到NDB美元时代().
NDB EPOCH2美元()。的NDB EPOCH2美元()
函数类似于NDB美元时代()
,除了NDB EPOCH2美元()
使用双向复制拓扑提供删除-删除处理。属性将主要角色和次要角色分配给两个源ndb_slave_conflict_role
系统变量要适当的值在每个源上(通常各一个主要的
,二次
).完成此操作后,由次要程序所做的修改将由主要程序反射回次要程序,然后由次要程序有条件地应用这些修改。
NDB EPOCH2_TRANS美元()。NDB EPOCH2_TRANS美元()
扩展了NDB EPOCH2美元()
函数。以同样的方式检测和处理冲突,并将主要和次要角色分配给复制的集群,但是附加的条件是,在发生冲突的同一事务中更新的任何其他行也被视为冲突。也就是说,NDB EPOCH2美元()
重新排列次要节点上相互冲突的行,而NDB EPOCH_TRANS美元()
重新对齐冲突的事务。
在哪里NDB美元时代()
而且NDB EPOCH_TRANS美元()
使用每个行、每个最后修改的epoch指定的元数据,在主节点上确定来自辅助节点的传入复制行更改是否与本地提交的更改并发;并发的更改被认为是冲突的,后续的异常表更新和次要的重新排列。当在主节点上删除一行时,问题就出现了,因此不再有任何最后修改的epoch可用来确定复制的操作是否发生冲突,这意味着无法检测到冲突的删除操作。这可能会导致分歧,例如一个集群上的删除操作与另一个集群上的删除和插入操作是并发的;这就是为什么在使用时删除操作只能路由到一个集群的原因NDB美元时代()
而且NDB EPOCH_TRANS美元()
.
NDB EPOCH2美元()
通过忽略任何删除-删除冲突,以及通过避免任何潜在的结果分歧,绕过了刚才描述的问题——在primary上存储关于已删除行的信息。这是通过将成功应用于辅助服务器并从辅助服务器复制的任何操作反射回辅助服务器来实现的。当它返回到辅助服务器时,可以使用它在辅助服务器上重新应用被源自主服务器的操作删除的操作。
当使用NDB EPOCH2美元()
时,您应该记住,辅助服务器从主服务器应用删除操作,删除新行,直到通过反射操作恢复它。理论上,在辅助服务器上的后续插入或更新与从主服务器上的删除操作相冲突,但在这种情况下,我们选择忽略这一点,并允许辅助服务器这样做”赢得”,以防止组别之间出现分歧。换句话说,在删除之后,主进程不会检测冲突,而是立即采用从进程的后续更改。因此,在发展到最终(稳定)状态时,次要进程的状态可以重新访问多个先前提交的状态,其中一些状态可能是可见的。
您还应该意识到,将所有操作从辅助服务器反射回主服务器会增加主服务器的日志二进制日志大小,还会增加对带宽、CPU使用和磁盘I/O的需求。
在辅助设备上反射操作的应用取决于辅助设备上目标行的状态。属性可以跟踪次要服务器上是否应用了反映的更改Ndb_conflict_reflected_op_prepare_count
而且Ndb_conflict_reflected_op_discard_count
状态变量。应用的更改数量就是这两个值之间的差值(请注意Ndb_conflict_reflected_op_prepare_count
总是大于等于Ndb_conflict_reflected_op_discard_count
).
当且仅当以下两个条件都为真时,事件将被应用:
行是否存在(即它是否存在)与事件的类型一致。对于删除和更新操作,行必须已经存在。对于插入操作,行必须不存在。
该行最后一次被主服务器修改。修改可能是通过执行反射操作完成的。
如果这两个条件都不满足,反射的操作将被次要操作丢弃。
NDB$EPOCH()的限制。使用时,当前适用以下限制NDB美元时代()
执行冲突检测:
使用NDB集群epoch边界检测冲突,粒度与
TimeBetweenEpochs
(默认值:100毫秒)。最小冲突窗口是对两个集群上相同数据的并发更新总是报告冲突的最短时间。这总是一个非零的时间长度,并且大致与2 *(延迟+排队+ timebetweenepoch)
.这意味着,假设默认为TimeBetweenEpochs
并且忽略集群之间的任何延迟(以及任何队列延迟)—最小冲突窗口大小约为200毫秒。在查看预期的应用程序时,应该考虑这个最小窗口”比赛”模式。属性的表需要额外的存储空间
NDB美元时代()
而且NDB EPOCH_TRANS美元()
函数;每行需要额外的1到32位空间,这取决于传递给函数的值。删除操作之间的冲突可能导致主要和次要操作之间的分歧。当在两个集群上同时删除一行时,可以检测到冲突,但不会记录冲突,因为该行已被删除。这意味着在任何后续重新排列操作的传播过程中不会检测到进一步的冲突,这可能导致分歧。
删除应该在外部序列化,或者只路由到一个集群。或者,应该使用这样的删除和随后的插入以事务方式更新一个单独的行,这样就可以跨行删除跟踪冲突。这可能需要在应用程序中进行更改。
双向只能有两个新开发银行集群”active - active”使用时当前支持的配置
NDB美元时代()
或NDB EPOCH_TRANS美元()
用于冲突检测。
使用旧NDB $ ()
冲突解决功能中,还需要创建一个对应的异常表NDB
这种类型的冲突解决将被采用的表。使用时也是如此NDB美元时代()
或NDB EPOCH_TRANS美元()
.此表的名称是要应用冲突解决的表的名称,带有字符串美元的前女友
附加。(例如,如果原始表的名称为mytable
,对应异常表名的名称应为mytable美元交货
)。创建异常表的语法如下所示:
创建表original_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老美元|orig_table_column新的美元][additional_columns,] PRIMARY KEY([NDB$]server_id, [NDB$]source_server_id, [NDB$]source_epoch, [NDB$]count))
前四列是必需的。前四列的名称和与原始表的主键列相匹配的列的名称并不重要;但是,出于清晰性和一致性的考虑,我们建议您使用此处所示的名称server_id
,source_server_id
,source_epoch
,数
列,并且对于与原始表的主键中的列匹配的列使用与原始表中相同的名称。
如果异常表使用一个或多个可选列NDB OP_TYPE美元
,NDB CFT_CAUSE美元
,或NDB ORIG_TRANSID美元
本节稍后将讨论,那么每个必需的列也必须使用前缀命名NDB美元
.如果需要,可以使用NDB美元
即使没有定义任何可选列,也可以使用前缀来命名必需的列,但在本例中,所有四个必需的列都必须使用前缀命名。
在这些列之后,应该按照用于定义原始表的主键的顺序复制构成原始表主键的列。复制原始表的主键列的列的数据类型应该与原始列的数据类型相同(或大于)。可以使用主键列的子集。
异常表必须使用NDB
存储引擎。(一个例子使用旧NDB $ ()
本节稍后将显示一个例外表。)
附加列可以在复制的主键列之后定义,但不能在任何主键列之前定义;任何这样的额外列都不能被删除非空
.NDB集群支持三个额外的预定义可选列NDB OP_TYPE美元
,NDB CFT_CAUSE美元
,NDB ORIG_TRANSID美元
,我们将在接下来的几段中介绍。
NDB OP_TYPE美元
:通过该列可以获取引起冲突的操作类型。如果你使用这个列,定义如下所示:
Ndb $ op_type enum (' write_row ', ' update_row ', ' delete_row ', ' refresh_row ', ' read_row ')不为空
的WRITE_ROW
,UPDATE_ROW
,DELETE_ROW
操作类型表示用户发起的操作。REFRESH_ROW
操作是在补偿从检测到冲突的集群发送回原始集群的事务时通过冲突解决生成的操作。READ_ROW
操作是使用排他行锁定义的用户发起的读跟踪操作。
NDB CFT_CAUSE美元
:可选列NDB CFT_CAUSE美元
它提供了注册冲突的原因。这个列,如果使用,定义如下:
Ndb $ cft_cause enum (' row_does_not_exist ', ' row_already_exists ', ' data_in_conflict ', ' trans_in_conflict ')不为空
ROW_DOES_NOT_EXIST
可以报告为原因UPDATE_ROW
而且WRITE_ROW
操作;ROW_ALREADY_EXISTS
可报告为WRITE_ROW
事件。DATA_IN_CONFLICT
当基于行的冲突函数检测到冲突时报告;TRANS_IN_CONFLICT
当事务冲突函数拒绝属于一个完整事务的所有操作时报告。
NDB ORIG_TRANSID美元
:NDB ORIG_TRANSID美元
列(如果使用)包含原始事务的ID。这一列的定义如下:
Ndb $ orig_transid bigint unsigned not null
NDB ORIG_TRANSID美元
是否生成64位值NDB
.此值可用于关联来自相同或不同异常表的属于同一冲突事务的多个异常表项。
可以命名不属于原始表主键的其他引用列
或colname
美元的老
.colname
新的美元
在更新和删除操作中引用旧值,即包含colname
美元的老DELETE_ROW
事件。
可用于在插入和更新操作中引用新值—换句话说,使用colname
新的美元WRITE_ROW
事件,UPDATE_ROW
事件,或者两种类型的事件。如果冲突的操作没有为给定的不是主键的参考列提供一个值,异常表行包含其中之一零
,或为该列定义的默认值。
的mysql.ndb_replication
表是在建立数据表进行复制时读取的,因此必须插入要复制的表对应的行mysql.ndb_replication
之前完成复制表的创建。
可以使用几个状态变量来监视冲突检测。您可以看到在冲突中找到了多少行NDB美元时代()
的当前值重新启动此副本Ndb_conflict_fn_epoch
系统状态变量。
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_max
提供未在当前SQL节点上应用某行的次数的计数”时间戳最大的赢家”自上次解决冲突以来mysqld就开始了。
的结果未应用行的次数”相同的时间戳获胜”冲突解决方案mysqld因为它最后一次重新启动是由全局状态变量给出的Ndb_conflict_fn_old
.除了递增Ndb_conflict_fn_old
时,将未使用的行的主键插入到异常表,如本节其他地方所述。
下面的示例假设您已经有一个工作的NDB集群复制设置,如中所述第23.6.5节,“为复制准备NDB集群”,第23.6.6节“启动NDB集群复制(单复制区域通道)”.
NDB $ MAX()例子。假设您希望启用”时间戳最大的赢家”冲突解决方案test.t1
,使用列mycol
随着”时间戳”.这可以通过以下步骤来完成:
确保您已经启动了源代码mysqld与
——ndb-log-update-as-write =了
.在源上执行此操作
插入
声明:插入mysql。ndb_replicationVALUES ('test', 't1', 0, NULL, 'NDB$MAX(mycol)');
插入0到
server_id
表示所有访问该表的SQL节点都应该使用冲突解决。如果你想在一个特定的问题上使用冲突解决mysqld请使用实际的服务器ID。插入
零
到binlog_type
列的效果与插入0 (NBT_DEFAULT
);使用服务器默认值。创建
test.t1
表:创建表测试。t1 (列mycol INT UNSIGNED,列)引擎= NDB;
现在,当对该表执行更新时,将应用冲突解决方案,具有最大值的行的版本
mycol
写入副本。
其他binlog_type
等选项,NBT_UPDATED_ONLY_USE_UPDATE
应用于使用?控件控制源上的日志记录ndb_replication
表,而不是使用命令行选项。
旧NDB $()的例子。假设一个NDB
表(如此处定义的表)正在被复制,您希望启用”相同的时间戳获胜”该表更新的冲突解决方法:
创建表测试。2 (a INT UNSIGNED NOT NULL, b CHAR(25) NOT NULL,列, mycol INT UNSIGNED NOT NULL,列,主键pk (a, b)) ENGINE=NDB;
以下步骤是必需的,顺序如下所示:
第一之前创建
test.t2
属性中插入一行mysql.ndb_replication
表,如下所示:插入mysql。ndb_replicationVALUES ('test', 't2', 0, NULL, 'NDB$OLD(mycol)');
的可能值
binlog_type
列在本节前面所示。的值“美元NDB老(mycol)”
应该插进去吗conflict_fn
列。创建一个适当的异常表
test.t2
.这里显示的表创建语句包括所有必需的列;任何附加列都必须声明在这些列之后,在表的主键定义之前。创建表测试。2$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))
我们可以包含关于给定冲突的类型、原因和原始事务ID的信息的附加列。我们也不需要为原始表中的所有主键列提供匹配的列。这意味着你可以像这样创建异常表:
创建表测试。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,]主键(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count))
请注意的
NDB美元
四个必需的列都需要前缀,因为我们至少包含了其中一列NDB OP_TYPE美元
,NDB CFT_CAUSE美元
,或NDB ORIG_TRANSID美元
在表定义中。创建表
test.t2
如前所述。
对于希望用于执行冲突解决的每个表,都必须执行这些步骤旧NDB $ ()
.对于每个这样的表,必须有相应的行mysql.ndb_replication
,并且在与被复制的表相同的数据库中必须有一个异常表。
读取冲突检测和解决。NDB集群还支持读取操作的跟踪,这使得在循环复制设置中管理一个集群中给定行的读取与另一个集群中相同行的更新或删除之间的冲突成为可能。这个例子使用了员工
而且部门
表来模拟一个场景,在这个场景中,一名员工从一个部门移动到源集群上的另一个部门(下面我们称之为集群)一个),而副本集群(此后B)在交错的事务中更新该雇员原部门的雇员数目。
数据表已经使用以下SQL语句创建:
#雇员表CREATE table雇员(id INT主键,name VARCHAR(2000), dept INT NOT NULL)#部门表CREATE table部门(id INT PRIMARY KEY, name VARCHAR(2000), member INT)
这两个表的内容包括以下输出(部分)中显示的行选择
声明:
mysql> SELECT id, name, dept FROM employee+---------------+------+ | id | |部门名称 | +------+--------+------+ ...| 998 |迈克| 3 | | 999 |乔| 3 | | 1000 |玛丽| 3 |…+------+--------+------+ mysql >选择id、名称、部门成员;+-----+-------------+---------+ | id | |成员名称 | +-----+-------------+---------+ ...| 3 |旧项目| 24 |…+-----+-------------+---------+
我们假设我们已经在使用一个异常表,其中包括四个必需的列(这些用于该表的主键),用于操作类型和原因的可选列,以及使用如下SQL语句创建的原始表的主键列:
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,主密钥(NDB$server_id, NDB$source_server_id, NDB$source_epoch, NDB$count))
假设在两个集群上同时发生两个事务。在集群一个,我们创建一个新部门,然后将编号999的员工移动到该部门,使用以下SQL语句:
开始;INSERT INTO部门VALUES(4, "新项目",1);SET dept = 4 WHERE id = 999;提交;
同时,在集群上B,另一个事务从中读取员工
,如图所示:
开始;SELECT name FROM employee WHERE id = 999UPDATE department SET members = member - 1 WHERE id = 3;提交;
冲突解决机制通常不会检测到冲突事务,因为冲突发生在读取(选择
)和更新操作。您可以通过执行集
ndb_log_exclusive_reads
= 1
在复制集群上。以这种方式获取排他读锁会导致在源上读取的任何行都被标记为需要在复制集群上解决冲突。如果在记录这些事务之前以这种方式启用独占读取,则集群上的读取B被跟踪并发送到集群一个决议;随后检测到员工行上的冲突,并在集群上检测到事务B将中止。
冲突在异常表中注册(在集群上)一个)作为READ_ROW
操作(见冲突解决例外表,以说明操作类型),如下所示:
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 | +-------+-------------+-------------------+
在读操作中发现的任何现有行都将被标记。这意味着由同一冲突导致的多行可能被记录在异常表中,如检查集群上更新之间冲突的影响所示一个以及在集群上读取多行B同时进行的事务中的同一个表。在集群上执行的事务一个如图所示:
开始;INSERT INTO部门VALUES(4, "新项目",0);SET dept = 4 WHERE dept = 3;SELECT COUNT(*) FROM employee WHERE dept = 4;更新部门SET成员= @count WHERE id = 4;提交;
同时,包含这里所示语句的事务在集群上运行B:
SET ndb_log_exclusive_reads = 1;#必须设置,如果尚未启用…开始;SELECT COUNT(*) INTO @count FROM employee WHERE dept = 3更新部门SET成员= @count WHERE id = 3;提交;
在本例中,所有三行都匹配在哪里
第二个事务中的条件选择
被读取,因此在异常表中被标记,如下所示:
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集群的单个实例中执行排他行锁定。