16.4.1.10 在源和副本上的 table 定义不同的复制

复制的源 table 和目标 table 不必相同。源上的 table 可以具有比 table 的副本副本更多或更少的列。此外,在一定条件下,源和副本上的相应 table 列可以使用不同的数据类型。

Note

不支持在分区彼此不同的 table 之间进行复制。参见第 16.4.1.23 节,“复制和分区”

在源 table 和目标 table 没有相同定义的所有情况下,源 table 和副本 table 上的数据库名和 table 名必须相同。在以下两个部分中,将通过示例讨论其他条件。

16.4.1.10.1 在源或副本上具有更多列的复制

您可以将 table 从源复制到副本,以使 table 的源副本和副本副本具有不同的列数,但要满足以下条件:

  • table 的两个版本共有的列必须在源和副本上以相同 Sequences 定义。

(即使两个 table 具有相同的列数,也是如此.)

  • 必须在任何其他列之前定义 table 的两个版本共有的列。

这意味着在两个 table 共有的列范围内在 table 上插入新列的副本上执行ALTER TABLE语句会导致复制失败,如以下示例所示:

假设存在于源和副本上的 tablet由以下CREATE TABLE语句定义:

CREATE TABLE t (
    c1 INT,
    c2 INT,
    c3 INT
);

假设在副本上执行此处显示的ALTER TABLE语句:

ALTER TABLE t ADD COLUMN cnew1 INT AFTER c3;

副本上允许使用以前的ALTER TABLE,因为 tablet的两个版本所共有的列c1c2c3在 table 的两个版本中均保持组合在一起,然后保留任何不同的列。

但是,以下ALTER TABLE语句不能在副本上执行而不会导致复制中断:

ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;

在刚刚显示的ALTER TABLE语句的副本上执行之后,复制失败,因为新列cnew2介于t的两个版本通用的列之间。

  • 该 table 版本中的每个“额外”列都有更多列,必须具有默认值。

列的默认值由多种因素决定,包括其类型,是否使用DEFAULT选项定义,是否声明为NULL以及创建时有效的服务器 SQL 模式。有关更多信息,请参阅第 11.6 节“数据类型默认值”

此外,当副本的 table 副本的列数多于源副本的列数时,两个 table 共用的每一列都必须使用相同的数据类型。

示例. 以下示例说明了一些有效和无效的 table 定义:

源上的更多列. 下 table 定义有效且可以正确复制:

source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT);

下 table 的 table 定义会引发错误,因为 table 的两个版本共同的列的定义在副本上的 Sequences 与在源上的 Sequences 不同:

source> CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
replica>  CREATE TABLE t1 (c2 INT, c1 INT);

下列 table 定义也将引发错误,因为源上额外列的定义出现在该 table 的两个版本共同的列的定义之前:

source> CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT);

副本上有更多列. 下 table 定义有效且可以正确复制:

source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);

以下定义引起错误,因为在 table 的两个版本中公用的列在源和副本上的定义 Sequences 都不相同:

source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c2 INT, c1 INT, c3 INT);

下列 table 定义也会引发错误,因为副本的 table 版本中的额外列的定义出现在 table 的两个版本所共有的列的定义之前:

source> CREATE TABLE t1 (c1 INT, c2 INT);
replica>  CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);

以下 table 定义失败,因为 table 的副本版本与源版本相比具有更多列,并且 table 的两个版本对公共列c2使用不同的数据类型:

source> CREATE TABLE t1 (c1 INT, c2 BIGINT);
replica>  CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
16.4.1.10.2 复制具有不同数据类型的列

理想情况下,同一 table 的源副本和副本副本上的对应列应具有相同的数据类型。但是,只要满足某些条件,这并不总是严格执行的。

通常可以从给定数据类型的列复制到同一类型,相同大小或宽度(如果适用)或更大的另一列。例如,您可以从CHAR(10)列复制到另一个CHAR(10),或从CHAR(10)列复制到CHAR(25)列而没有任何问题。在某些情况下,还可以从一种数据类型的列(在源上)复制到另一种数据类型的列(在副本上);当列的源版本的数据类型在副本上提升为相同大小或更大的类型时,这称为属性提升。

属性升级可与基于语句的复制和基于行的复制一起使用,并且不依赖于源或副本使用的存储引擎。但是,日志记录格式的选择确实会影响允许的类型转换。具体细节将在本节后面讨论。

Important

无论您是使用基于语句的复制还是基于行的复制,如果您希望使用属性提升,table 的副本副本所包含的列不能超过源副本的列。

基于语句的复制. 当使用基于语句的复制时,遵循的一条简单经验法则是:“如果在源上运行的语句也将在副本上成功执行,那么它也应成功复制”。换句话说,如果该语句使用的值与副本上给定列的类型兼容,则可以复制该语句。例如,您也可以将适合TINYINT列的任何值插入到BIGINT列;因此,即使将 table 的副本副本中的TINYINT列的类型更改为BIGINT,成功地在源上对该列进行的任何插入也应在副本上成功,因为不可能有合法的TINYINT该值足够大以超过BIGINT列。

在 MySQL 5.7.1 之前,使用基于语句的复制时,源和副本上的AUTO_INCREMENT列必须相同;否则,更新可能会应用于副本上的错误 table。错误 12669186)

基于行的复制:属性提升和降级. 基于行的复制支持较小数据类型和较大类型之间的属性提升和降级。也可以指定是否允许降级的列值进行有损(截断)或无损转换,如本节后面所述。

有损和无损转换. 如果目标类型不能代 table 要插入的值,则必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现“拟合”,则我们进行称为有损转换的操作。不需要截断或类似修改即可将源列值适合目标列的转换是无损转换。

类型转换模式(slave_type_conversions 变量). slave_type_conversions全局服务器变量的设置控制副本上使用的类型转换模式。该变量采用下 table 中的一组值,该 table 显示了每种模式对副本的类型转换行为的影响:

ModeEffect
ALL_LOSSY在这种模式下,允许进行意味着信息丢失的类型转换。


这并不意味着允许进行无损转换,仅 table 示仅要求有损转换或完全不进行转换的情况;例如,启用* only *(仅)此模式可将INT列转换为TINYINT(有损转换),但不能将TINYINT列转换为INT列(无损)。在这种情况下尝试进行后一种转换将导致复制停止,并在副本上出现错误。
| ALL_NON_LOSSY |此模式允许不需要截断或对源值进行其他特殊处理的转换;也就是说,它允许目标类型比源类型具有更大范围的转换。
设置此模式与是否允许有损转换无关。这是通过ALL_LOSSY模式控制的。如果仅设置了ALL_NON_LOSSY而不设置了ALL_LOSSY,则尝试进行转换将导致数据丢失(例如INTTINYINTCHAR(25)VARCHAR(20)),将导致副本停止并显示错误。
| ALL_LOSSY,ALL_NON_LOSSY |设置此模式后,将允许所有受支持的类型转换,无论它们是否是有损转换。
| ALL_SIGNED |将提升的整数类型视为有符号值(默认行为)。
| ALL_UNSIGNED |将提升的整数类型视为无符号值。
| ALL_SIGNED,ALL_UNSIGNED |如果可能的话,将提升的整数类型视为带符号,否则为无符号。
| [] |未设置slave_type_conversions时,不允许属性提升或降级。这意味着源 table 和目标 table 中的所有列都必须具有相同的类型。
此模式是默认模式。

提升整数类型时,不会保留其有符号性。默认情况下,副本将所有此类值视为已签名。从 MySQL 5.7.2 开始,您可以使用ALL_SIGNEDALL_UNSIGNED或同时使用两者来控制此行为。 (错误号 15831300)ALL_SIGNED通知副本将所有提升的整数类型视为带符号; ALL_UNSIGNED指示其将它们视为未签名。如果同时指定两者,则副本将在可能的情况下将值视为有符号,否则将其视为无符号。列出的 Sequences 并不重要。如果未同时使用ALL_LOSSYALL_NONLOSSY中的至少一个,则ALL_SIGNEDALL_UNSIGNED均无效。

更改类型转换模式需要使用新的slave_type_conversions设置重新启动副本。

支持的转换. 下 table 列出了不同但相似的数据类型之间支持的转换:

这包括这些类型的带符号和无符号版本之间的转换。

通过将源值截断为目标列允许的最大值(或最小值)来进行有损转换。为了确保从无符号类型到带符号类型的无损转换,目标列必须足够大以容纳源列中的值范围。例如,您可以无损地将TINYINT UNSIGNED降级为SMALLINT,但不能降级为TINYINT

FLOATDOUBLE是无损转换; DOUBLEFLOAT只能有损处理。从DECIMAL(M,D)DECIMAL(M',D')的转换(其中D' >= D(M'-D') >= (M-D)是无损的;对于M' < MD' < D或两者皆有的任何情况,只能进行有损转换。

对于任何十进制类型,如果要存储的值不能适合目标类型,则该值将根据文档中其他位置为服务器定义的舍入规则进行舍入。有关如何对十进制类型执行此操作的信息,请参见第 12.22.4 节“舍弃行为”

  • 在任何字符串类型CHARVARCHARTEXT之间,包括不同宽度之间的转换。

CHARVARCHARTEXT转换为相同大小或更大的CHARVARCHARTEXT列永远不会造成损失。通过仅在副本上插入字符串的前* N 个字符(其中 N *是目标列的宽度)来处理有损转换。

Important

不支持在使用不同字符集的列之间进行复制。

  • 在任何二进制数据类型BINARYVARBINARYBLOB之间,包括不同宽度之间的转换。

BINARYVARBINARYBLOB转换为相同大小或更大的BINARYVARBINARYBLOB列永远不会造成损失。通过仅在副本上插入字符串的前* N 个字节来处理有损转换,其中 N *是目标列的宽度。

  • 在任意 2 个大小的任意 2 个BIT列之间。

BIT(M)列的值插入BIT(M')列(其中M' > M)时,会清除BIT(M')列的最高有效位(设置为零),并将BIT(M)值的* M *位设置为BIT(M)值的最低有效位。 BIT(M')列。

将来自源BIT(M)列的值插入到目标BIT(M')列(其中M' < M)时,将指定BIT(M')列的最大可能值;换句话说,“全部设置”值分配给目标列。

不允许在上一个列 table 以外的类型之间进行转换。