用于复制的源表和目标表不必完全相同。源上的表可以比副本的表副本具有更多或更少的列。此外,根据特定条件,源和副本上的相应表列可以使用不同的数据类型。
不支持分区彼此不同的表之间的复制。请参阅 第 17.5.1.24 节,“复制和分区”。
在源表和目标表没有相同定义的所有情况下,数据库和表名称在源和副本上都必须相同。在以下两节中,将通过示例讨论其他条件。
您可以将表从源复制到副本,以便表的源副本和副本副本具有不同的列数,但要满足以下条件:
表的两个版本共有的列必须在源和副本上以相同的顺序定义。(即使两个表的列数相同也是如此。)
两个版本的表共有的列必须在任何其他列之前定义。
这意味着
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
在副本上是允许的,因为 两个版本的表共有的列c1
、c2
和 仍然在两个版本的表中分组在一起,在任何不同的列之前。c3
t
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);
理想情况下,同一表的源副本和副本副本上的相应列应具有相同的数据类型。但是,只要满足某些条件,这并不总是严格执行。
通常可以从给定数据类型的列复制到相同类型和相同大小或宽度(如果适用)或更大的另一列。例如,您可以从一CHAR(10)
列复制到另一列
CHAR(10)
,或从一
CHAR(10)
列
复制到另一列,CHAR(25)
而不会出现任何问题。在某些情况下,还可以从具有一种数据类型(在源上)的列复制到具有不同数据类型(在副本上)的列;当列的源版本的数据类型被提升为与副本大小相同或更大的类型时,这称为属性提升。
属性提升可用于基于语句和基于行的复制,并且不依赖于源或副本使用的存储引擎。但是,日志记录格式的选择确实会影响允许的类型转换;详情将在本节后面讨论。
无论您使用基于语句还是基于行的复制,如果您希望使用属性提升,则副本的表副本不能包含比源副本更多的列。
基于语句的复制。
使用基于语句的复制时,要遵循的一个简单经验法则是,“如果在源上运行的语句也会在副本上成功执行,那么它也应该复制成功”。换句话说,如果该语句使用与副本上给定列的类型兼容的值,则可以复制该语句。例如,您也可以将适合一TINYINT
列的任何值插入到一
BIGINT
列中;因此,即使您将TINYINT
副本的表副本中的列类型更改为
BIGINT
,在源上成功插入该列的任何插入也应该在副本上成功,因为不可能有一个
TINYINT
大到足以超过BIGINT
列的合法值。
基于行的复制:属性提升和降级。 基于行的复制支持较小数据类型和较大类型之间的属性提升和降级。也可以指定是否允许对降级列值进行有损(截断)或无损转换,如本节后面所述。
有损和无损转换。 如果目标类型不能表示插入的值,则必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现 “适合”,我们将进行所谓的有损转换。不需要截断或类似修改以适合目标列中的源列值的转换是 无损转换。
类型转换模式。
系统变量的全局值
replica_type_conversions
(从 MySQL 8.0.26 开始)或slave_type_conversions
(在 MySQL 8.0.26 之前)控制副本上使用的类型转换模式。此变量从以下列表中获取一组值,该列表描述了每种模式对副本类型转换行为的影响:
- ALL_LOSSY
在这种模式下,允许会丢失信息的类型转换。
这并不意味着允许无损转换,只是仅允许需要有损转换或根本不需要转换的情况;例如,仅启用此模式允许将
INT
列转换为TINYINT
(有损转换),但不允许将TINYINT
列转换为INT
列(无损转换)。在这种情况下尝试后一种转换会导致复制停止并在副本上出现错误。- ALL_NON_LOSSY
此模式允许不需要截断或对源值进行其他特殊处理的转换;也就是说,它允许在目标类型比源类型具有更宽范围的情况下进行转换。
设置此模式与是否允许有损转换无关;这是由
ALL_LOSSY
模式控制的。如果只ALL_NON_LOSSY
设置了,但没有设置ALL_LOSSY
,则尝试进行会导致数据丢失的转换(例如INT
toTINYINT
或CHAR(25)
toVARCHAR(20)
)会导致副本因错误而停止。- ALL_LOSSY,ALL_NON_LOSSY
设置此模式后,允许所有支持的类型转换,无论它们是否是有损转换。
- ALL_SIGNED
将提升的整数类型视为有符号值(默认行为)。
- ALL_UNSIGNED
将提升的整数类型视为无符号值。
- ALL_SIGNED、ALL_UNSIGNED
如果可能,将提升的整数类型视为有符号,否则视为无符号。
- [空]
当
replica_type_conversions
或slave_type_conversions
不设置时,不允许属性提升或降级;这意味着源表和目标表中的所有列都必须属于相同类型。此模式是默认模式。
提升整数类型时,不会保留其符号。默认情况下,副本将所有此类值视为已签名。ALL_SIGNED
您可以使用、
ALL_UNSIGNED
或两者
来控制此行为
。ALL_SIGNED
告诉副本将所有提升的整数类型视为有符号;
ALL_UNSIGNED
指示它将这些视为未签名。如果可能,同时指定两者会导致副本将值视为有符号,否则将其视为无符号;它们列出的顺序并不重要。如果不使用or
中的至少一个,则none norALL_SIGNED
也没有
任何效果。
ALL_UNSIGNED
ALL_LOSSY
ALL_NONLOSSY
更改类型转换模式需要使用新的
replica_type_conversions
或
slave_type_conversions
设置重新启动副本。
支持的转换。 不同但相似的数据类型之间支持的转换如下表所示:
在任何整数类型
TINYINT
,SMALLINT
,MEDIUMINT
,INT
, 和 之间BIGINT
。这包括这些类型的有符号和无符号版本之间的转换。
通过将源值截断为目标列允许的最大值(或最小值)来进行有损转换。为确保从无符号类型转换为有符号类型时的无损转换,目标列必须足够大以容纳源列中的值范围。例如,您可以无损降级
TINYINT UNSIGNED
为SMALLINT
,但不能降级为TINYINT
。在任何十进制类型
DECIMAL
,FLOAT
,DOUBLE
, 和 之间NUMERIC
。FLOAT
toDOUBLE
是无损转换;DOUBLE
toFLOAT
只能有损处理。从 到 where和 ) 的转换是无损的;对于 、 或两者的任何情况 , 只能进行有损转换。DECIMAL(
M
,D
)DECIMAL(
M'
,D'
)D'
>=D
(
M'
-D'
) >= (M
-D
M'
<M
D'
<D
对于任何十进制类型,如果要存储的值不适合目标类型,则该值将根据文档中其他地方为服务器定义的舍入规则向下舍入。有关如何为十进制类型完成此操作的信息 ,请参阅 第 12.25.4 节,“舍入行为” 。
在任何字符串类型
CHAR
、VARCHAR
和TEXT
之间,包括不同宽度之间的转换。CHAR
将、VARCHAR
或 转换为相同大小或更大TEXT
的CHAR
、VARCHAR
或TEXT
列永远不会有损。N
通过仅在副本上插入字符串的第一个字符来处理有损转换,其中N
是目标列的宽度。重要的不支持使用不同字符集的列之间的复制。
在任何二进制数据类型
BINARY
、VARBINARY
和BLOB
之间,包括不同宽度之间的转换。BINARY
将、VARBINARY
或 转换为相同大小或更大BLOB
的BINARY
、VARBINARY
或BLOB
列永远不会有损。有损转换是通过仅在N
副本上插入字符串的第一个字节来处理的,其中N
是目标列的宽度。在任意
BIT
2 种尺寸的任意 2 列之间。将列 中 的值插入 列时,其中,列的最高有效位 被清除(设置为零),值的 位 被设置为 列的最低有效位。
BIT(
M
)BIT(
M'
)M'
>M
BIT(
M'
)M
BIT(
M
)BIT(
M'
)将源 列中的值插入目标 列时,其中,为该 列分配了最大可能值;换句话说,一个 “全集”值被分配给目标列。
BIT(
M
)BIT(
M'
)M'
<M
BIT(
M'
)
不在前面列表中的类型之间的转换是不允许的。