复制的源表和目标表不必完全相同。源上的表可以比副本中表的副本具有更多或更少的列。此外,源和副本上的对应表列可以使用不同的数据类型,但要受某些条件限制。
不支持对彼此分区不同的表进行复制。见第 19.5.1.25 节,“复制和分区”.
在源表和目标表定义不完全相同的所有情况下,数据库和表名在源和副本上都必须相同。在以下两个部分中,将讨论其他条件以及示例。
可以将表从源复制到副本,以便源和副本中表的副本具有不同数量的列,但要满足以下条件
两个版本表中共有的列必须在源和副本上按相同的顺序定义。(即使两个表具有相同数量的列,也是如此。)
两个版本表中共有的列必须在任何其他列之前定义。
这意味着在副本上执行
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
仍然分组在一起,在任何不同的列之前。但是,以下
ALTER TABLE
语句不能在副本上执行,否则会导致复制中断ALTER TABLE t ADD COLUMN cnew2 INT AFTER c2;
在副本上执行了刚刚显示的
ALTER TABLE
语句之后,复制失败,因为新列cnew2
出现在两个版本的t
中共有的列之间。在列数较多的版本表中,每个“额外的”列都必须具有默认值。
列的默认值由多种因素决定,包括其类型、是否使用
DEFAULT
选项定义、是否声明为NULL
以及创建时生效的服务器 SQL 模式;有关更多信息,请参阅 第 13.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
,源表上对该列的任何插入操作(如果成功)也应在副本上成功,因为不可能存在大到足以超过 BIGINT
列的合法 TINYINT
值。
基于行的复制:属性提升和降级: 基于行的复制支持较小数据类型和较大数据类型之间的属性提升和降级。还可以指定是否允许有损(截断)或无损转换降级列值,如本节稍后所述。
有损转换和无损转换: 如果目标类型无法表示要插入的值,则必须决定如何处理转换。如果允许转换,但截断(或以其他方式修改)源值以实现目标列中的 “拟合”,则进行所谓的 有损转换。不需要截断或类似修改以将源列值拟合到目标列中的转换称为 无损 转换。
类型转换模式: 系统变量 replica_type_conversions
的全局值控制副本上使用的类型转换模式。该变量接受以下列表中的一组值,该列表描述了每种模式对副本类型转换行为的影响
- ALL_LOSSY
在此模式下,允许会导致信息丢失的类型转换。
这并不意味着允许无损转换,仅仅意味着只允许需要有损转换或根本不需要转换的情况;例如,启用 仅 此模式允许将
INT
列转换为TINYINT
(有损转换),但不允许将TINYINT
列转换为INT
列(无损)。在这种情况下尝试后一种转换将导致复制在副本上停止并发生错误。- ALL_NON_LOSSY
此模式允许不需要截断或其他特殊处理源值的转换;也就是说,它允许目标类型范围比源类型更广的转换。
设置此模式不会影响是否允许有损转换;这由
ALL_LOSSY
模式控制。如果仅设置ALL_NON_LOSSY
,但未设置ALL_LOSSY
,则尝试导致数据丢失的转换(如INT
到TINYINT
,或CHAR(25)
到VARCHAR(20)
)会导致副本停止并发生错误。- ALL_LOSSY,ALL_NON_LOSSY
当设置此模式时,所有支持的类型转换都允许,无论它们是有损转换还是无损转换。
- ALL_SIGNED
将提升的整数类型视为有符号值(默认行为)。
- ALL_UNSIGNED
将提升的整数类型视为无符号值。
- ALL_SIGNED,ALL_UNSIGNED
如果可能,将提升的整数类型视为有符号值,否则视为无符号值。
- [empty]
当未设置
replica_type_conversions
时,不允许任何属性提升或降级;这意味着源表和目标表中的所有列必须具有相同的类型。此模式为默认模式。
当提升整数类型时,其符号不会保留。默认情况下,副本将所有此类值视为有符号值。您可以使用 ALL_SIGNED
、ALL_UNSIGNED
或两者来控制此行为。 ALL_SIGNED
指示副本将所有提升的整数类型视为有符号值;ALL_UNSIGNED
指示它将这些值视为无符号值。指定两者会导致副本在可能的情况下将该值视为有符号值,否则将其视为无符号值;它们列出的顺序无关紧要。如果至少没有设置 ALL_LOSSY
或 ALL_NONLOSSY
之一,则 ALL_SIGNED
或 ALL_UNSIGNED
不会有任何影响。
更改类型转换模式需要使用新的 replica_type_conversions
设置重新启动副本。
支持的转换: 支持的不同但类似数据类型之间的转换如下所示
在任何整数类型
TINYINT
、SMALLINT
、MEDIUMINT
、INT
和BIGINT
之间。这包括在这些类型的有符号版本和无符号版本之间进行转换。
有损转换通过将源值截断为目标列允许的最大值(或最小值)来完成。为了确保从无符号类型到有符号类型的无损转换,目标列必须足够大以容纳源列中的值范围。例如,您可以将
TINYINT UNSIGNED
无损降级为SMALLINT
,但不能降级为TINYINT
。在任何十进制类型
DECIMAL
、FLOAT
、DOUBLE
和NUMERIC
之间。FLOAT
到DOUBLE
是无损转换;DOUBLE
到FLOAT
只能有损处理。从DECIMAL(
到M
,D
)DECIMAL(
的转换,其中M'
,D'
)
且D'
>=D
(
) 是无损的;对于任何M'
-D'
) >= (M
-D
、M'
<M
或两者的情况,只能进行有损转换。D'
<D
对于任何十进制类型,如果要存储的值无法拟合到目标类型,则该值将根据文档中其他地方为服务器定义的舍入规则向下舍入。有关如何对十进制类型执行此操作的信息,请参阅 第 14.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
列之间,无论大小。当将值从
BIT(
列插入到M
)BIT(
列时,其中M'
)
,M'
>M
BIT(
列的最高有效位被清除(设置为零),而M'
)BIT(
值的M
)M
位被设置为BIT(
列的最低有效位。M'
)当将值从源
BIT(
列插入到目标M
)BIT(
列时,其中M'
)
,则为M'
<M
BIT(
列分配最大可能值;换句话说,为目标列分配“全置位”值。M'
)
不允许在不在上述列表中的类型之间进行转换。