Documentation Home
MySQL 8.0 参考手册  / 第十七章复制  / 17.5 复制注意事项和技巧  / 17.5.1 复制特性和问题  /  16.4.1.10 在源和副本上使用不同的表定义进行复制

16.4.1.10 在源和副本上使用不同的表定义进行复制

用于复制的源表和目标表不必完全相同。源上的表可以比副本的表副本具有更多或更少的列。此外,根据特定条件,源和副本上的相应表列可以使用不同的数据类型。

笔记

不支持分区彼此不同的表之间的复制。请参阅 第 16.4.1.23 节,“复制和分区”

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

16.4.1.10.1 在源或副本上使用更多列进行复制

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

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

    (即使两个表的列数相同也是如此。)

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

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

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

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

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

    ALTER TABLE t ADD COLUMN cnew1 INT AFTER c3;

    前一个ALTER TABLE在副本上是允许的,因为 两个版本的表共有的列c1c2和 仍然在两个版本的表中分组在一起,在任何不同的列之前。 c3t

    ALTER TABLE但是,如果不导致复制中断,则无法在副本上执行 以下语句:

    ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;

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

  • 具有更多列的表版本中的每个额外列都必须具有默认值。

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

此外,当表的副本副本的列数多于源副本的列数时,表的每个公共列必须在两个表中使用相同的数据类型。

例子。  以下示例说明了一些有效和无效的表定义:

源上的更多专栏。  下表定义有效并正确复制:

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

以下表定义会引发错误,因为表的两个版本共有的列的定义在副本上的顺序与在源上的顺序不同:

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

以下表定义也会引发错误,因为源上额外列的定义出现在表的两个版本共有的列的定义之前:

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

副本上的更多列。  下表定义有效并正确复制:

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

以下定义引发错误,因为表的两个版本共有的列在源和副本上的定义顺序不同:

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

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

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

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

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

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

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

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

重要的

无论您使用基于语句还是基于行的复制,如果您希望使用属性提升,则副本的表副本不能包含比源副本更多的列。

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

在 MySQL 5.7.1 之前,使用基于语句的复制时, AUTO_INCREMENT要求源和副本上的列相同;否则,更新可能会应用于副本上的错误表。(漏洞 #12669186)

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

有损和无损转换。  如果目标类型不能表示插入的值,则必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现 适合,我们将进行所谓的有损转换。不需要截断或类似修改以适合目标列中的源列值的转换是 无损转换。

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

模式 影响
ALL_LOSSY

在这种模式下,允许会丢失信息的类型转换。

这并不意味着允许无损转换,只是仅允许需要有损转换或根本不需要转换的情况;例如, 启用此模式允许将 INT列转换为 TINYINT(有损转换),但不允许将TINYINT列转换为 INT列(无损转换)。在这种情况下尝试后一种转换会导致复制停止并在副本上出现错误。

ALL_NON_LOSSY

此模式允许不需要截断或对源值进行其他特殊处理的转换;也就是说,它允许在目标类型比源类型具有更宽范围的情况下进行转换。

设置此模式与是否允许有损转换无关;这是由 ALL_LOSSY模式控制的。如果只 ALL_NON_LOSSY设置了,但没有设置 ALL_LOSSY,则尝试进行会导致数据丢失的转换(例如INTto TINYINTCHAR(25)to VARCHAR(20))会导致副本因错误而停止。

ALL_LOSSY,ALL_NON_LOSSY

设置此模式后,允许所有支持的类型转换,无论它们是否是有损转换。

ALL_SIGNED

将提升的整数类型视为有符号值(默认行为)。

ALL_UNSIGNED

将提升的整数类型视为无符号值。

ALL_SIGNED,ALL_UNSIGNED

如果可能,将提升的整数类型视为有符号,否则视为无符号。

[]

未设置时slave_type_conversions,不允许属性提升或降级;这意味着源表和目标表中的所有列都必须属于相同类型。

此模式是默认模式。

提升整数类型时,不会保留其符号。默认情况下,副本将所有此类值视为已签名。从 MySQL 5.7.2 开始,您可以使用 、 或两者来控制此ALL_SIGNED行为 ALL_UNSIGNED。(错误#15831300) ALL_SIGNED告诉副本将所有提升的整数类型视为有符号; ALL_UNSIGNED指示它将这些视为未签名。如果可能,同时指定两者会导致副本将值视为有符号,否则将其视为无符号;它们列出的顺序并不重要。如果不使用or 中的至少一个,则none norALL_SIGNED也没有 任何效果。 ALL_UNSIGNEDALL_LOSSYALL_NONLOSSY

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

支持的转换。  不同但相似的数据类型之间支持的转换如下表所示:

  • 在任何整数类型 TINYINT, SMALLINT, MEDIUMINT, INT, 和 之间BIGINT

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

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

  • 在任何十进制类型 DECIMAL, FLOAT, DOUBLE, 和 之间NUMERIC

    FLOATtoDOUBLE是无损转换;DOUBLEto FLOAT只能有损处理。从 到 where和 ) 的转换是无损的;对于 、 或两者的任何情况 , 只能进行有损转换。 DECIMAL(M,D)DECIMAL(M',D')D' >= D(M'-D') >= (M-DM' < MD' < D

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

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

    CHAR将、 VARCHAR或 转换为相同大小或更大TEXTCHARVARCHARTEXT列永远不会有损。N通过仅在副本上插入字符串的第一个字符来处理有损转换,其中N 是目标列的宽度。

    重要的

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

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

    BINARY将、 VARBINARY或 转换为相同大小或更大BLOBBINARYVARBINARYBLOB 列永远不会有损。有损转换是通过仅在 N副本上插入字符串的第一个字节来处理的,其中N是目标列的宽度。

  • 在任意BIT2 种尺寸的任意 2 列之间。

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

    将源 列中的值插入目标 列时,其中,为该 列分配了最大可能值;换句话说,一个 全集值被分配给目标列。 BIT(M)BIT(M')M' < MBIT(M')

不在前面列表中的类型之间的转换是不允许的。