复制功能
基于语句的复制AUTO_INCREMENT
,LAST_INSERT_ID()
和TIMESTAMP
值来执行对符合下列情况除外:
AUTO_INCREMENT
使用基于语句的复制无法正确复制调用触发器或函数导致更新列的语句。这些语句被标记为不安全。错误45677)一个
INSERT
成具有包括一个复合主键的表AUTO_INCREMENT
列,它是不该组合键的第一列不是基于语句的记录或复制的安全。这些语句被标记为不安全。(缺陷#11754117,错误#45670)此问题不会影响使用
InnoDB
存储引擎的InnoDB
表,因为具有AUTO_INCREMENT 列的表需要至少一个键,其中auto-increment列是唯一或最左边的列。在
AUTO_INCREMENT
表中添加列ALTER TABLE
可能与在从属服务器和主服务器上产生的行顺序不同。发生这种情况的原因是,行的编号顺序取决于用于表的特定存储引擎以及行的插入顺序。如果在主站和从站上具有相同的顺序很重要,则在分配AUTO_INCREMENT
编号之前必须对行进行排序。假设要向具有和的AUTO_INCREMENT
列的表t1
中添加一列,以下语句将生成一个新表,该表与相同,但带有col1
col2
t2
t1
AUTO_INCREMENT
柱:CREATE TABLE t2 LIKE t1;ALTER TABLE t2ADD id INTAUTO_INCREMENT PRIMARY KEY ;INSERT INTO t2SELECT *FROM t1ORDER BY col1, col2;重要
为了保证主服务器和从服务器上的顺序相同,该
ORDER BY
子句必须命名的所有列t1
。刚刚给出的说明受以下限制
CREATE TABLE ... LIKE
:外键定义DATA DIRECTORY
以及和INDEX DIRECTORY
表选项均被忽略。如果表定义包含这些特征中的任何t2
一个CREATE TABLE
,则使用与创建时使用的语句相同的语句进行创建t1
,但增加一AUTO_INCREMENT
列。无论使用哪种方法来创建和填充具有该
AUTO_INCREMENT
列的副本,最后一步都是删除原始表,然后重命名副本:DROP t1;ALTER TABLE t2RENAME t1;另请参见第B.4.6.1节“ ALTER TABLE的问题”。
复制和BLACKHOLE表
该BLACKHOLE
存储引擎接受数据,而是将其丢弃,不存储。执行二进制日志记录时,无论使用哪种日志记录格式,都始终记录对此类表的所有插入。根据使用的是基于语句的日志记录还是基于行的日志记录,对更新和删除的处理方式有所不同。使用基于语句的日志记录格式,BLACKHOLE
会记录所有影响表的语句,但是会忽略其影响。使用基于行的日志记录时,仅跳过对此类表的更新和删除-它们不会写入二进制日志。每当发生这种情况时,都会记录一条警告。
因此,建议您在使用BLACKHOLE
存储引擎将表binlog_format
变量中的服务器变量设置为STATEMENT
而不是ROW
或时复制到表中MIXED
。
复制和字符集
以下内容适用于使用不同字符集的MySQL服务器之间的复制:
- 如果主数据库具有字符集与全局
character_set_server
值不同的数据库,则应设计CREATE TABLE
语句,以使它们不隐式依赖数据库默认字符集。一个好的解决方法是在CREATE TABLE
语句中显式声明字符集和排序规则。
复制和CHECKSUM表
CHECKSUM TABLE
使用取决于表行存储格式的方法,返回逐行计算的校验和。不保证MySQL版本之间的存储格式相同,因此升级后校验和值可能会更改。
复制CREATE SERVER,ALTER SERVER和DROP SERVER
该声明CREATE SERVER
,ALTER SERVER
以及DROP SERVER
没有写入到二进制日志,无论二进制日志记录格式是在使用中。
复制CREATE ... IF NOT EXISTS语句
当CREATE ... IF NOT EXISTS
复制各种语句时,MySQL将应用以下规则:
CREATE DATABASE IF NOT EXISTS
无论数据库是否已经存在于主数据库上,都会复制每个语句。- 类似地,无论表是否存在于主数据库上,都会复制每个
CREATE TABLE IF NOT EXISTS
不带a的语句SELECT
。这包括CREATE TABLE IF NOT EXISTS ... LIKE
。复制CREATE TABLE IF NOT EXISTS ... SELECT
遵循一些不同的规则;有关更多信息,请参见“ CREATE TABLE ... SELECT语句的复制”。 CREATE EVENT IF NOT EXISTS
无论语句中命名的事件是否已经存在于主数据库上,始终都会被复制。
复制CREATE TABLE ... SELECT语句
当CREATE TABLE ... SELECT
复制语句时,MySQL将应用以下规则:
CREATE TABLE ... SELECT
总是执行隐式提交(“导致隐式提交的声明”)。如果目标表不存在,则按以下方式记录日志。是否
IF NOT EXISTS
存在都没有关系。STATEMENT
或MIXED
格式:该语句以书面形式记录。ROW
格式:该语句作为语句记录,CREATE TABLE
后面是一系列插入行事件。
- 如果该
CREATE TABLE ... SELECT
语句失败,则不记录任何内容。这包括目标表存在IF NOT EXISTS
且未给出的情况。 - 如果目标表存在并
IF NOT EXISTS
给出,则MySQL 8.0会完全忽略该语句;没有插入或记录任何内容。
当使用基于语句的复制时,MySQL 8.0不允许CREATE TABLE ... SELECT
语句对除该语句创建的表以外的其他表进行任何更改。使用基于行的复制时,这不是问题,因为该语句作为语句记录,并且CREATE TABLE
对表数据的任何更改均作为行插入事件而不是整个记录CREATE TABLE ... SELECT
。
复制CURRENT_USER()
以下语句支持使用该CURRENT_USER()
功能代替受影响的用户或定义者的名称,并可能代替该主机或主机的名称:
DROP USER
RENAME USER
GRANT
REVOKE
CREATE FUNCTION
CREATE PROCEDURE
CREATE TRIGGER
CREATE EVENT
CREATE VIEW
ALTER EVENT
ALTER VIEW
SET PASSWORD
当启用二进制日志记录CURRENT_USER()
或CURRENT_USER
在其中任何一条语句中将其用作定义程序时,MySQL Server会确保在复制该语句时,将该语句应用于主服务器和从属服务器上的同一用户。在某些情况下,例如更改密码的语句,在将函数引用写入二进制日志之前会对其进行扩展,以便该语句包含用户名。对于所有其他情况,主服务器上当前用户的名称将作为元数据复制到从服务器,而从服务器会将语句应用于元数据中命名的当前用户,而不是从服务器上的当前用户。
在主服务器和从服务器上使用不同的表定义进行复制
复制的源表和目标表不必相同。主服务器上的表可以具有比从服务器的表更多或更少的列。此外,在某些条件下,主服务器和从服务器上的相应表列可以使用不同的数据类型。
注意不支持在分区彼此不同的表之间进行复制。
在源表和目标表没有相同定义的所有情况下,主数据库和从数据库上的数据库和表名称必须相同。在以下两个部分中,将通过示例讨论其他条件。
在主服务器或从属服务器上具有更多列的复制
您可以将表从主服务器复制到从服务器,以使该表的主副本和从属副本具有不同的列数,但要满足以下条件:
在主表和从表上,必须以相同的顺序定义表的两个版本共有的列。
(即使两个表具有相同的列数,也是如此。)
在任何其他列之前,必须定义该表的两个版本共有的列。
这意味着
ALTER TABLE
在两个表共有的列范围内在表上插入新列的从属服务器上执行语句将导致复制失败,如以下示例所示:假设
t
通过以下CREATE TABLE
语句定义主控和从属内存在的表:CREATE TABLE t ( c1 INT, c2 INT, c3 INT );假设
ALTER TABLE
此处显示的语句在从属服务器上执行:ALTER TABLE tADD COLUMN cnew1 INTAFTER c3;ALTER TABLE
在从属服务器上允许使用前一个,因为在这两个版本的表中相同的列c1
,c2
和,在任何不同的列之前,都在表的两个版本中分组在一起。c3
t
但是,以下
ALTER TABLE
语句不能在从属服务器上执行而不会导致复制中断:ALTER TABLE tADD COLUMN cnew2 INTAFTER c2;在
ALTER TABLE
刚刚显示的语句的从属服务器上执行之后,复制失败,因为新列cnew2
位于两个版本的通用列之间t
。该表版本中的每个具有更多列的“额外”列都必须具有默认值。
列的默认值由许多因素决定,包括其类型,是否用
DEFAULT
选项定义,是否声明为NULL
以及创建时有效的服务器SQL模式。有关更多信息,请参见“数据类型默认值”)。
另外,当表的从属副本比主副本具有更多列时,表公用的每一列在两个表中必须使用相同的数据类型。
例子。以下示例说明了一些有效和无效的表定义:
关于母版的更多专栏。下表定义有效并且可以正确复制:
master >CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);slave >CREATE TABLE t1 (c1 INT, c2 INT);
下表定义将引发错误,因为该表的两个版本共同的列定义在从属服务器上的顺序与主服务器上的顺序不同:
master >CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);slave >CREATE TABLE t1 (c2 INT, c1 INT);
下列表定义也会引发错误,因为主表上多余的列的定义出现在该表的两个版本共同的列的定义之前:
master >CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);slave >CREATE TABLE t1 (c1 INT, c2 INT);
从属上有更多列。下表定义有效并且可以正确复制:
master >CREATE TABLE t1 (c1 INT, c2 INT);slave >CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
以下定义引起错误,因为在主表和从表上,表的两个版本的公共列未按相同的顺序定义:
master >CREATE TABLE t1 (c1 INT, c2 INT);slave >CREATE TABLE t1 (c2 INT, c1 INT, c3 INT);
下列表定义也会引发错误,因为从表的版本中的额外列的定义出现在表的两个版本所共有的列的定义之前:
master >CREATE TABLE t1 (c1 INT, c2 INT);slave >CREATE TABLE t1 (c3 INT, c1 INT, c2 INT);
以下表定义失败,因为表的从属版本比主版本具有更多列,并且表的两个版本对公共列使用不同的数据类型c2
:
master >CREATE TABLE t1 (c1 INT, c2 BIGINT);slave >CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);
复制具有不同数据类型的列
理想情况下,同一表的主副本和从属副本上的对应列应具有相同的数据类型。但是,只要满足某些条件,这并不总是严格执行。
通常可以从给定数据类型的列复制到相同类型和相同大小或宽度(如果适用)或更大的另一列。例如,您可以从一CHAR(10)
列复制到另一列CHAR(10)
,或从一CHAR(10)
列复制到一CHAR(25)
列而没有任何问题。在某些情况下,还可以从一种数据类型的列(在主数据库上)复制到另一种数据类型的列(在从属数据库上)。当列的主版本的数据类型在从属上提升为相同大小或更大的类型时,这称为属性提升。
属性升级可与基于语句的复制和基于行的复制一起使用,并且不依赖于主服务器或从服务器使用的存储引擎。但是,日志记录格式的选择确实会影响允许的类型转换。详细信息将在本节后面讨论。
重要无论您使用基于语句的复制还是基于行的复制,如果希望使用属性提升,表的从属副本都不能包含比主副本多的列。
基于语句的复制。使用基于语句的复制时,遵循的一条简单经验法则是:“如果在主服务器上运行的语句也将在从服务器上成功执行,那么它也应成功复制”。换句话说,如果该语句使用的值与从属服务器上给定列的类型兼容,则可以复制该语句。例如,您也可以将适合TINYINT
列的任何值插入到BIGINT
列中。因此,即使您TINYINT
将从属表的副本中的列类型更改为BIGINT
,成功插入主节点上该列的任何内容也应在从属节点上成功,因为不可能有TINYINT
足够大的合法值来超过一BIGINT
列。
基于行的复制:属性提升和降级。基于行的复制支持较小数据类型和较大数据类型之间的属性提升和降级。也可以指定是否允许降级的列值进行有损(截断)或无损转换,如本节后面所述。
有损和无损转换。如果目标类型不能代表要插入的值,则必须决定如何处理转换。如果我们允许转换但截断(或以其他方式修改)源值以在目标列中实现“拟合”,那么我们进行所谓的有损转换。不需要截断或类似修改即可将源列值适合目标列的转换是无损转换。
类型转换模式(slave_type_conversions变量)。slave_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
如果可能,将提升的整数类型视为带符号,否则视为无符号。
- [空]
如果
slave_type_conversions
未设置,则不允许属性提升或降级。这意味着源表和目标表中的所有列都必须具有相同的类型。此模式是默认模式。
提升整数类型时,不会保留其有符号性。默认情况下,从站将所有此类值视为带符号。您可以使用控制这种行为ALL_SIGNED
,ALL_UNSIGNED
或者两者兼而有之。ALL_SIGNED
告诉从站将所有提升的整数类型都视为带符号;ALL_UNSIGNED
指示其将这些视为未签名。同时指定两者会使从属设备将值视为有符号,否则将其视为无符号。列出的顺序并不重要。如果其中至少之一或未使用,也ALL_SIGNED
不会ALL_UNSIGNED
产生任何影响。ALL_LOSSY
ALL_NONLOSSY
更改类型转换模式需要使用新slave_type_conversions
设置重新启动从站。
支持的转换。下表显示了不同但相似的数据类型之间受支持的转换:
之间的任意整数类型的
TINYINT
,SMALLINT
,MEDIUMINT
,INT
,和BIGINT
。这包括这些类型的带符号和无符号版本之间的转换。
通过将源值截断为目标列允许的最大值(或最小值)来进行有损转换。为了确保从无符号类型转换为带符号类型时的无损转换,目标列必须足够大以容纳源列中的值范围。例如,您可以
TINYINT UNSIGNED
无损地降级为SMALLINT
,但不能降级为TINYINT
。之间的任何小数类型
DECIMAL
,FLOAT
,DOUBLE
,和NUMERIC
。FLOAT
toDOUBLE
是无损转换;DOUBLE
到FLOAT
只能有损地处理。从到 where 和的转换是无损的;对于其中任何情况下,或两者,仅有损转换可。DECIMAL(M,D)
DECIMAL(M',D')
D'>=D
(M'-D')>=(M-D
M'<M
D'<D
对于任何十进制类型,如果要存储的值不能适合目标类型,则该值将根据文档中其他位置为服务器定义的舍入规则进行舍入。有关如何对十进制类型执行此操作的信息,请参见“舍入行为”。
之间的任何字符串类型
CHAR
,VARCHAR
以及TEXT
,包括不同的宽度之间的转换。的转换
CHAR
,VARCHAR
或TEXT
以一个CHAR
,VARCHAR
或TEXT
列中的相同大小或更大是从未有损耗的。有损转换通过仅在N
从属服务器上插入字符串的第一个字符N
(即目标列的宽度)来处理。重要
不支持使用不同字符集的列之间复制。
之间的任何二进制数据类型
BINARY
,VARBINARY
以及BLOB
,包括不同的宽度之间的转换。的转换
BINARY
,VARBINARY
或BLOB
以一个BINARY
,VARBINARY
或BLOB
列中的相同大小或更大是从未有损耗的。有损转换是通过仅在N
从属服务器上插入字符串的第一个字节来处理的,其中N
目标列的宽度为。在任意2
BIT
个尺寸的任意2 列之间。将列中的值插入列中时,,列的最高有效位被清除(设置为零),并且该值的位被设置为该列的最低有效位。
BIT(M)
BIT(M')
M'>M
BIT(M')
M
BIT(M)
BIT(M')
当将值从源列插入目标列时,其中分配了该列的最大可能值;换句话说,“ all-set ”值分配给目标列。
BIT(M)
BIT(M')
M'<M
BIT(M')
不允许在上一个列表以外的类型之间进行转换。
复制和目录表选项
如果在主服务器上的语句中使用DATA DIRECTORY
或INDEX DIRECTORY
表选项,则CREATE TABLE
在从服务器上也使用table选项。如果从属主机文件系统中不存在相应目录,或者该目录存在但不能被从属服务器访问,则可能导致问题。可以通过NO_DIR_IN_CREATE
在从属服务器上使用服务器SQL模式来覆盖此方法,这会导致从属服务器在复制语句时忽略DATA DIRECTORY
和INDEX DIRECTORY
表选项CREATE TABLE
。结果是MyISAM
在表的数据库目录中创建了数据和索引文件。
有关更多信息,请参见“服务器SQL模式”。
DROP ... IF EXISTS语句的复制
的DROP DATABASE IF EXISTS
,DROP TABLE IF EXISTS
和DROP VIEW IF EXISTS
语句总是被复制,即使数据库,表或视图被丢弃在主不存在。这是为了确保一旦从站追随主站,要丢弃的对象就不再存在于主站或从站上。
DROP ... IF EXISTS
即使主控制器上不存在要删除的存储程序,也将复制存储程序的语句(存储过程和函数,触发器和事件)。
复制和浮点值
使用基于语句的复制,值从十进制转换为二进制。由于十进制和二进制表示形式之间的转换可能是近似的,因此涉及浮点值的比较是不精确的。对于显式使用浮点值或隐式转换为浮点的值的操作而言,这是正确的。由于计算机体系结构,用于构建MySQL的编译器等方面的差异,浮点值的比较可能在主服务器和从属服务器上产生不同的结果。请参见“表达式求值中的类型转换”和第B.4.4.8节“浮点值的问题”。
复制和FLUSH
在一些形式的FLUSH
声明还没有登录,因为如果复制到从它们可能导致问题:FLUSH LOGS
和FLUSH TABLES WITH READ LOCK
。有关语法示例,请参见“ FLUSH语句”。在FLUSH TABLES
,ANALYZE TABLE
,OPTIMIZE TABLE
,和REPAIR TABLE
语句被写入二进制日志,因此复制到奴隶。通常这不是问题,因为这些语句不会修改表数据。
但是,这种行为在某些情况下会引起困难。如果您在mysql
数据库中复制特权表并不使用而直接更新这些表GRANT
,则必须FLUSH PRIVILEGES
在从属服务器上发出a ,以使新特权生效。另外,如果FLUSH TABLES
在重命名作为MyISAM
表一部分的MERGE
表时使用,则必须FLUSH TABLES
在从属服务器上手动发布。这些语句将写入二进制日志,除非您指定NO_WRITE_TO_BINLOG
其别名或别名LOCAL
。
复制和系统功能
某些功能在某些情况下不能很好地复制:
的
USER()
,CURRENT_USER()
(或CURRENT_USER
)UUID()
,VERSION()
和LOAD_FILE()
功能被复制没有改变,因此并除非基于行的复制功能无法可靠地在从服务器上运行。(请参见“复制格式”。)USER()
并CURRENT_USER()
在使用MIXED
模式时使用基于行的复制自动复制,并在STATEMENT
模式下生成警告。(另请参见“ CURRENT_USER()的复制”。)对于VERSION()
和也是如此RAND()
。对于
NOW()
,二进制日志包括时间戳。这意味着在主服务器上对该函数的调用返回的值将复制到从服务器。为了避免在不同时区的MySQL服务器之间进行复制时出现意外结果,请在主服务器和从服务器上都设置时区。有关更多信息,请参见“使用复制进行备份”。为了解释在不同时区的服务器之间进行复制时可能出现的问题,假设主服务器位于纽约,从服务器位于斯德哥尔摩,并且两个服务器都使用本地时间。进一步假设,在主数据库上,创建一个表
mytable
,对该表执行一条INSERT
语句,然后从表中进行选择,如下所示:mysql>
CREATE TABLE mytable (mycol TEXT); Query OK, 0 rows affected (0.06 sec) mysql>INSERT INTO mytableVALUES ( NOW() ); Query OK, 1 row affected (0.00 sec) mysql>SELECT *FROM mytable; +--------------------- + | mycol | +--------------------- + | 2009 -09 -01 12:00:00 | +--------------------- + 1 row in set (0.00 sec)斯德哥尔摩的当地时间比纽约的当地时间晚6个小时;因此,如果您
SELECT NOW()
在完全相同的时刻在从站上发布,则将2009-09-01 18:00:00
返回该值。因此,如果在复制了刚刚显示mytable
的CREATE TABLE
和INSERT
语句之后从从服务器的副本中进行选择,则可能希望mycol
包含value2009-09-01 18:00:00
。然而,这种情况并非如此;当您从的从副本中进行选择时mytable
,您将获得与主副本完全相同的结果:mysql>
SELECT *FROM mytable; +--------------------- + | mycol | +--------------------- + | 2009 -09 -01 12:00:00 | +--------------------- + 1 row in set (0.00 sec)不同于
NOW()
,该SYSDATE()
函数不是复制安全的,因为它不受SET TIMESTAMP
二进制日志中语句的影响,并且如果使用基于语句的日志记录则不确定。如果使用基于行的日志记录,这不是问题。一种替代方法是使用该
--sysdate-is-now
选项使SYSDATE()
成为的别名NOW()
。必须在主机和从机上完成此操作才能正常工作。在这种情况下,此功能仍会发出警告,但只要--sysdate-is-now
在主站和从站上都使用该警告,就可以安全地将其忽略。SYSDATE()
使用MIXED
模式时,将使用基于行的复制自动复制,并在STATEMENT
模式下生成警告。另请参见“使用复制进行备份”。
以下限制仅适用于基于语句的复制,不适用于基于行的复制。的
GET_LOCK()
,RELEASE_LOCK()
,IS_FREE_LOCK()
,和IS_USED_LOCK()
函数手柄用户级锁,而不从知道在主并发上下文复制。因此,不应将这些函数用于插入主表,因为从站上的内容会有所不同。例如,不要发出诸如这样的语句INSERT INTO mytable VALUES(GET_LOCK(...))
。在使用
MIXED
模式时,将使用基于行的复制功能自动复制这些功能,并在STATEMENT
模式下生成警告。
当基于语句的复制生效时,作为上述限制的解决方法,您可以使用以下策略:将有问题的函数结果保存在用户变量中,并在以后的语句中引用该变量。例如,INSERT
由于对该UUID()
函数的引用,以下单行是有问题的:
INSERT INTO tVALUES (UUID());
要变通解决此问题,请执行以下操作:
SET @my_uuid = UUID();INSERT INTO tVALUES (@my_uuid);
之所以重复执行该语句序列,@my_uuid
是因为的值作为该INSERT
语句之前的用户变量事件存储在二进制日志中,并且可以在中使用INSERT
。
相同的想法适用于多行插入,但使用起来比较麻烦。对于两行插入,可以执行以下操作:
SET @my_uuid1 = UUID(); @my_uuid2 = UUID();INSERT INTO tVALUES (@my_uuid1),(@my_uuid2);
但是,如果行数很大或未知,则解决方法将很难或不可行。例如,您不能将以下语句转换为给定的单个用户变量与每一行相关联的语句:
INSERT INTO t2SELECT UUID(), *FROM t1;
在存储的函数中,RAND()
只要在函数执行期间仅被调用一次,就可以正确复制。(您可以将函数执行时间戳和随机数种子视为隐式输入,它们在主服务器和从属服务器上是相同的。)
该FOUND_ROWS()
和ROW_COUNT()
功能使用基于语句的复制不可复制的可靠。一种解决方法是将函数调用的结果存储在用户变量中,然后在INSERT
语句中使用它。例如,如果您希望将结果存储在名为的表中mytable
,通常可以这样进行:
SELECT SQL_CALC_FOUND_ROWS FROM mytableLIMIT 1;INSERT INTO mytableVALUES ( FOUND_ROWS() );
但是,如果要复制mytable
,则应使用SELECT ... INTO
,然后将变量存储在表中,如下所示:
SELECT SQL_CALC_FOUND_ROWS INTO @found_rowsFROM mytableLIMIT 1;INSERT INTO mytableVALUES (@found_rows);
这样,用户变量将作为上下文的一部分进行复制,并正确应用于从站。
在使用MIXED
模式时,将使用基于行的复制功能自动复制这些功能,并在STATEMENT
模式下生成警告。(缺陷#12092,缺陷#30244)
复制和小数秒支持
MySQL 8.0允许对TIME
,DATETIME
和TIMESTAMP
值使用小数秒,精度最高为微秒(6位数)。请参见“时间值的分数秒”。
调用功能的复制
复制诸如用户定义函数(UDF)和存储程序(存储过程和函数,触发器和事件)之类的已调用功能具有以下特征:
- 该功能的效果始终被复制。
使用基于语句的复制来复制以下语句:
CREATE EVENT
ALTER EVENT
DROP EVENT
CREATE PROCEDURE
DROP PROCEDURE
CREATE FUNCTION
DROP FUNCTION
CREATE TRIGGER
DROP TRIGGER
但是,使用基于行的复制来复制使用这些语句创建,修改或删除的功能的效果。
注意
尝试使用基于语句的复制来复制调用的功能会产生警告,“以语句格式登录是不安全的”。例如,尝试使用基于语句的复制来复制UDF会产生此警告,因为MySQL服务器目前无法确定UDF是否是确定性的。如果您完全确定所调用功能的影响是确定性的,则可以放心地忽略此类警告。
在的情况下
CREATE EVENT
和ALTER EVENT
:SLAVESIDE_DISABLED
无论指定的状态如何,事件的状态都设置为从属设备上的状态(不适用于DROP EVENT
)。- 通过从属服务器的服务器ID在从属服务器上标识创建事件的主服务器。中的
ORIGINATOR
列INFORMATION_SCHEMA.EVENTS
存储此信息。有关更多信息,请参见“ INFORMATION_SCHEMA事件表”和“ SHOW事件声明”。
- 功能实现以可更新的状态驻留在从属设备上,因此,如果主设备出现故障,则可以将该从设备用作主设备,而不会丢失事件处理。
要确定在MySQL服务器上是否存在在其他服务器(充当复制主服务器)上创建的任何计划事件,请INFORMATION_SCHEMA.EVENTS
按照与此处所示类似的方式查询表:
SELECT EVENT_SCHEMA, EVENT_NAMEFROM INFORMATION_SCHEMA.EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
另外,您可以使用如下SHOW EVENTS
语句:
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
将具有此类事件的复制从设备提升为复制主设备时,必须使用启用每个事件,其中事件的名称为。ALTER EVENT event_name ENABLE
event_name
如果在此从属服务器上创建事件涉及多个主服务器,并且您希望标识仅在具有服务器ID的给定主服务器上创建的事件master_id
,请修改EVENTS
表上的上一个查询以包括该ORIGINATOR
列,如下所示:
SELECT EVENT_SCHEMA, EVENT_NAME, ORIGINATORFROM INFORMATION_SCHEMA.EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED' AND ORIGINATOR = 'master_id'
您可以采用类似的方式使用ORIGINATOR
该SHOW EVENTS
语句:
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED' AND ORIGINATOR = 'master_id'
在启用从主服务器复制的事件之前,您应该在从服务器上禁用MySQL Event Scheduler(使用诸如的语句SET GLOBAL event_scheduler = OFF;
),运行任何必要的ALTER EVENT
语句,重启服务器,然后再在从服务器上重新启用Event Scheduler(使用诸如SET GLOBAL event_scheduler = ON;
)之类的陈述
如果以后将新的主服务器降级为复制从机,则必须手动禁用该ALTER EVENT
语句启用的所有事件。您可以通过在单独的表中存储SELECT
之前显示的语句中的事件名称来完成此操作,或者使用ALTER EVENT
语句使用共同的前缀重命名事件replicated_
以标识事件。
如果重命名事件,则在将该服务器降级为复制从属时,可以通过查询EVENTS
表来标识事件,如下所示:
SELECT CONCAT(EVENT_SCHEMA, '.', EVENT_NAME)AS 'Db.Event'FROM INFORMATION_SCHEMA.EVENTS WHERE INSTR(EVENT_NAME, 'replicated_') = 1;
JSON文档的复制
在MySQL 8.0之前,对JSON列的更新始终作为完整文档写入二进制日志。在MySQL 8.0中,可以记录对JSON文档的部分更新(请参阅JSON Values的部分更新),这样效率更高。日志记录行为取决于所使用的格式,如下所述:
基于语句的复制。 JSON部分更新始终记录为部分更新。使用基于语句的日志记录时,不能禁用此功能。
基于行的复制。默认情况下,不记录JSON部分更新,而是记录为完整文档。要启用记录部分更新,请设置binlog_row_value_options=PARTIAL_JSON
。如果复制主服务器设置了此变量,则复制从服务器将处理并应用从该主服务器接收的部分更新,而不管从服务器自己为变量设置如何。
运行MySQL 8.0.2或更早版本的服务器无法识别用于JSON部分更新的日志事件。因此,从运行MySQL 8.0.3或更高版本的服务器复制到这样的服务器时,binlog_row_value_options
必须在主服务器上禁用此变量,方法是将此变量设置为''
(空字符串)。有关更多信息,请参见此变量的描述。
复制和限制
基于语句复制LIMIT
的条款DELETE
,UPDATE
以及INSERT ... SELECT
因为受影响的行的顺序没有界定的前瞻性声明是不安全的。(只有ORDER BY
当这些语句也包含一个子句时,才能使用基于语句的复制正确地复制这些语句。)遇到此类语句时:
使用
STATEMENT
模式时,现在发出警告,指出该语句对于基于语句的复制不安全。使用
STATEMENT
模式时,LIMIT
即使DML语句也有一个ORDER BY
子句,也会对其发出警告(因此将其确定为警告)。这是一个已知的问题。(错误#42851)- 使用
MIXED
模式时,现在将使用基于行的模式自动复制该语句。
复制和加载数据
LOAD DATA
对于基于语句的日志记录,认为不安全(请参见“在二进制日志记录中确定安全和不安全语句”)。当binlog_format=MIXED
设置,该语句将记录在基于行的格式。binlog_format=STATEMENT
设置为when时,请注意LOAD DATA
不会产生警告,这与其他不安全的声明不同。
如果您确实使用了LOAD DATA
when binlog_format=STATEMENT
设置,则会在应用更改的复制从属服务器上创建一个包含数据的临时文件。然后,从站使用一条LOAD DATA INFILE
语句来应用更改。如果服务器上的二进制日志加密处于活动状态,请注意该临时文件未加密。当需要加密时,请确保改用基于行或混合的二进制日志记录格式,这些格式不会创建临时文件。
如果使用了一个PRIVILEGE_CHECKS_USER
帐户来保护复制通道的安全(请参见“复制权限检查”),则强烈建议您LOAD DATA
使用基于行的二进制日志记录操作(binlog_format=ROW
)。如果REQUIRE_ROW_FORMAT
为通道设置了,则需要基于行的二进制日志记录。使用这种日志记录格式,FILE
不需要特权来执行事件,因此请不要给该PRIVILEGE_CHECKS_USER
帐户特权。如果您需要从涉及LOAD DATA INFILE
以语句格式登录的操作的复制错误中恢复,并且复制的事件是受信任的,则可以将FILE
特权授予PRIVILEGE_CHECKS_USER
临时帐户,在应用复制事件后将其删除。
当mysqlbinlog读取LOAD DATA
以基于语句的格式记录的语句的日志事件时,会在一个临时目录中创建一个生成的本地文件。这些临时文件不会被mysqlbinlog或任何其他MySQL程序自动删除。如果确实LOAD DATA
在基于语句的二进制日志中使用了语句,则在不再需要语句日志之后,应该自己删除临时文件。有关更多信息,请参见“mysqlbinlog-用于处理二进制日志文件的实用程序”。
复制和max_allowed_packet
max_allowed_packet
设置MySQL服务器和客户端(包括复制从站)之间的任何单个消息的大小的上限。如果要复制较大的列值(例如,可能在TEXT
或BLOB
列中找到),而max_allowed_packet
在主服务器上的数据太小,则主服务器会因错误而失败,而从服务器将关闭I / O线程。如果max_allowed_packet
从站上的地址太小,这也会导致从站停止I / O线程。
当前,基于行的复制将所有行的列和列值从主服务器发送到从属服务器,包括未由更新实际更改的列的值。这意味着,当您使用基于行的复制来复制较大的列值时max_allowed_packet
,即使仅复制更新或仅插入更新,也必须注意设置足够大以容纳要复制的任何表中最大的行。相对较小的值。
在多线程从站(带有slave_parallel_workers > 0
)上,确保将slave_pending_jobs_size_max
系统变量设置为等于或大于max_allowed_packet
主站上系统变量设置的值。的默认设置slave_pending_jobs_size_max
,128M,是默认设置的两倍max_allowed_packet
,这是64M。max_allowed_packet
限制了主机将发送的数据包大小,但是添加事件标头可能会产生超出此大小的二进制日志事件。同样,在基于行的复制中,单个事件的max_allowed_packet
大小可能会大大大于该事件的大小,因为的值max_allowed_packet
仅会限制表的每一列。
复制从站实际上接受的数据包达到其slave_max_allowed_packet
设置所设置的限制(默认设置为最大设置1GB),以防止由于大数据包而导致复制失败。但是,slave_pending_jobs_size_max
controls 的值控制从站上可用的内存以容纳传入的数据包。指定的内存在所有从属工作队列之间共享。
的值slave_pending_jobs_size_max
是一个软限制,并且如果一个异常大的事件(由一个或多个数据包组成)超过了此大小,则将保留事务,直到所有从属工作者都拥有空队列,然后进行处理。保留所有后续事务,直到完成大事务为止。因此,尽管异常事件的数量大于slave_pending_jobs_size_max
可以处理的异常事件,但清除所有从属工作程序的队列的延迟以及等待排队后续事务的延迟可能会导致复制从属程序出现滞后并降低从属工作程序的并发性。slave_pending_jobs_size_max
因此,应将其设置为足够高以容纳大多数预期的事件大小。
复制和内存表
当主服务器关闭并重新启动时,其MEMORY
表将变为空。为了将此效果复制到从属服务器,主服务器MEMORY
在启动后第一次使用给定表时,它会记录一个事件,该事件通过将该表的DELETE
语句写入二进制日志来通知从属服务器必须清空该表。可通过二进制日志中的注释来识别此生成的事件,如果服务器上正在使用GTID,则会为其分配GTID。
当从属服务器关闭并重新启动时,其MEMORY
表将变为空。这会导致从站与主站不同步,并可能导致其他故障或导致从站停止:
- 从母版收到的行格式更新和删除可能会失败。
Can't find record in 'memory_table'
- 诸如之类的语句可能会在主服务器和从服务器上插入不同的行集。
INSERT INTO ... SELECT FROM memory_table
重新启动正在复制MEMORY
表的从属服务器的安全方法是,首先从MEMORY
主服务器上的表中删除或删除所有行,然后等待这些更改复制到从属服务器上。这样就可以安全地重新启动从站。
在某些情况下,可以使用其他重新启动方法。如果binlog_format=ROW
,可以防止从从停止,如果你设置slave_exec_mode=IDEMPOTENT
你再次启动从服务器之前。这允许从服务器继续复制,但是其MEMORY
表仍将与主服务器上的表不同。如果应用程序逻辑MEMORY
可以安全地丢失表的内容(例如,如果MEMORY
表用于缓存),则可以这样做。slave_exec_mode=IDEMPOTENT
全局应用于所有表,因此它可能会在非MEMORY
表中隐藏其他复制错误。
(刚刚描述的方法不适用于slave_exec_mode
始终为IDEMPOTENT
且不能更改的NDB群集。)
MEMORY
表的大小受max_heap_table_size
系统变量的值限制,该值不被复制(请)。在改变max_heap_table_size
发生的影响MEMORY
创建或更新使用表ALTER TABLE ... ENGINE = MEMORY
或TRUNCATE TABLE
以下的变化,或所有MEMORY
表以下重新启动服务器。如果您在主服务器上增加此变量的值而不在从属服务器上增加此变量的值,则主服务器上的表可能变得大于从属服务器上的对应表,从而导致插入在主服务器上成功但在从属服务器上失败与表已满错误。这是一个已知的问题(错误#48666)。在这种情况下,必须max_heap_table_size
在从服务器和主服务器上设置全局值,然后重新启动复制。还建议您重新启动主MySQL服务器和从MySQL服务器,以确保新值对它们中的每一个都具有完全的(全局)作用。
有关表的更多信息,请参见“ MEMORY存储引擎”MEMORY
。
复制mysql系统架构
mysql
根据的值复制模式中对表所做的数据修改语句binlog_format
;如果该值为MIXED
,则使用基于行的格式复制这些语句。但是,通常会间接更新此信息的语句(例如GRANT
,REVOKE
以及操纵触发器,存储的例程和视图的语句)将使用基于语句的复制复制到从属服务器。
复制和查询优化器
如果以非确定性的数据修改方式编写语句,则主服务器和从服务器上的数据可能会变得不同。也就是说,保留查询优化器。(在一般情况下,这不是一个好的实践中,即使外部的复制。)非确定性语句的示例包括DELETE
或UPDATE
语句使用LIMIT
没有ORDER BY
条款;有关这些内容的详细讨论,
复制和分区
只要分区表使用相同的分区方案并且在其他结构上具有相同的结构(除非特别允许例外),就支持在分区表之间进行复制(请参见“在主表和从表上使用不同的表定义进行复制”)。
通常不支持在具有不同分区的表之间进行复制。这是因为ALTER TABLE ... DROP PARTITION
在这种情况下直接作用于分区的语句(例如)可能在主服务器和从服务器上产生不同的结果。如果表在主服务器上而不在从属服务器上进行分区,则在从属服务器的主副本上对分区进行操作的任何语句都将失败。当对表的从属副本进行分区而对主副本不进行分区时,作用在分区上的语句不能在主副本上运行而不会引起错误。
由于存在导致复制完全失败(由于失败的语句)和不一致(当分区级SQL语句的结果在主数据库和从数据库上产生不同结果)的危险,我们建议确保对任何表进行分区这些表的从属版本与要从主控复制的副本匹配。
复制和修复表
当在已损坏或以其他方式损坏的表上使用时,该REPAIR TABLE
语句可能会删除无法恢复的行。但是,此语句对表数据所做的任何此类修改都不会被复制,这可能导致主服务器和从服务器失去同步。因此,如果主服务器上的表已损坏并REPAIR TABLE
用于修复该表,则应在使用之前先停止复制(如果该表仍在运行)REPAIR TABLE
,然后再比较该表的主副本和从属副本,在重新启动复制之前,准备手动纠正任何差异。
复制和保留字
尝试从较旧的主服务器复制到较新的从服务器时,您会遇到问题,并且在主服务器上使用标识符,这些标识符是在从服务器上运行的较新MySQL版本中的保留字。例如,rank
在MySQL 5.7主数据库上命名的表列正在复制到MySQL 8.0从数据库,这可能会引起问题,因为这RANK
是从MySQL 8.0开始的保留字。
复制可以在这种情况下,错误1064失败,你在你的SQL语法错误......,即使使用保留字或有使用保留字命名列的表名为数据库或表是从复制排除。这是由于每个SQL事件都必须在执行之前由从服务器解析,以便从服务器知道哪个数据库对象将受到影响。该事件被解析之后才可以从应用由定义的任何过滤规则--replicate-do-db
,--replicate-do-table
,--replicate-ignore-db
,和--replicate-ignore-table
。
要解决主服务器上的数据库,表或列名被从服务器视为保留字的问题,请执行以下操作之一:
ALTER TABLE
在主服务器上使用一个或多个语句来更改任何数据库对象的名称,这些名称将被视为从服务器上的保留字,并更改所有使用旧名称的SQL语句来使用新名称。- 在使用这些数据库对象名称的任何SQL语句中,使用反引号(
`
)将名称写为带引号的标识符。
有关按MySQL版本保留字的列表,请参见《MySQL服务器版本参考》中的“保留字”。有关标识符引用规则,请参见“模式对象名称”。
复制和主/从关闭
关闭主服务器并稍后重新启动是安全的。当从站失去与主站的连接时,从站会立即尝试重新连接,并在失败的情况下定期重试。默认值为每60秒重试一次。这可以通过CHANGE MASTER TO
语句更改。从站也能够处理网络连接中断。但是,从设备仅在slave_net_timeout
几秒钟内没有收到来自主机的数据后,才注意到网络中断。如果您的中断时间很短,则可能需要减少slave_net_timeout
。请参见“处理复制从属的意外中断”。
由于未刷新主二进制日志文件,因此主节点侧的异常关闭(例如,崩溃)可能导致主二进制日志的最终位置小于从节点读取的最新位置。这可能导致从服务器在主服务器备份时无法复制。sync_binlog=1
在主my.cnf
文件中进行设置有助于最大程度地减少此问题,因为这会使主文件更频繁地刷新其二进制日志。为了在InnoDB
与事务一起使用的复制设置中获得最大的持久性和一致性,还应该设置innodb_flush_log_at_trx_commit=1
。使用此设置,InnoDB
重做日志缓冲区在每次事务提交时被写出到日志文件,并且日志文件被刷新到磁盘。注意,使用该设置仍然不能保证事务的持久性,因为操作系统或磁盘硬件可能会告诉mysqld磁盘刷新操作已经进行,即使尚未进行。
干净地关闭奴隶是安全的,因为它可以跟踪其离开的地方。但是,请注意,从站没有打开临时表。不干净的关机可能会产生问题,尤其是如果在发生问题之前未将磁盘缓存刷新到磁盘时:
- 对于事务,从节点提交然后更新
relay-log.info
。如果这两个操作之间发生崩溃,则中继日志处理将比信息文件所指示的进行得更远,并且从属服务器将在重新启动后从中继日志中的最后一个事务重新执行事件。 - 如果从属
relay-log.info
服务器更新,但服务器主机在写入被刷新到磁盘之前崩溃,则会发生类似的问题。为了最大程度地减少这种情况的发生,请sync_relay_log_info=1
在从属my.cnf
文件中进行设置。设置sync_relay_log_info
为0不会强制写入磁盘,并且服务器依赖操作系统不时刷新文件。
如果您拥有良好的不间断电源,则针对这些类型问题的系统的容错能力将大大提高。
复制期间的从站错误
如果一条语句在主服务器和从服务器上均产生相同的错误(相同的错误代码),则会记录该错误,但复制将继续。
如果一条语句在主服务器和从服务器上产生了不同的错误,则从服务器SQL线程终止,从服务器将一条消息写入其错误日志,并等待数据库管理员决定如何处理该错误。这包括以下情况:一条语句在主机或从机上产生错误,但不会同时在两者上产生错误。要解决该问题,请手动连接到从站并确定问题的原因。SHOW SLAVE STATUS
对此很有用。然后解决问题并运行START SLAVE
。例如,您可能需要创建一个不存在的表,然后才能再次启动从属服务器。
注意如果在从站的错误日志中记录了一个临时错误,则不必采取引用的错误消息中建议的任何操作。临时错误应由客户端重试事务处理。例如,如果从属SQL线程记录了一个与死锁有关的临时错误,则无需在从属服务器上手动重新启动事务,除非从属SQL线程随后以非临时错误消息终止。
如果不希望使用此错误代码验证行为,则可以使用该--slave-skip-errors
选项掩盖(忽略)某些或所有错误。
对于诸如这样的非事务性存储引擎MyISAM
,可能有一条语句仅部分更新表并返回错误代码。例如,在具有违反键约束的一行的多行插入中,或者在更新某些行后杀死长的update语句时,可能会发生这种情况。如果这在主服务器上发生,则从服务器希望执行该语句会导致相同的错误代码。如果不是,则从SQL线程如前所述停止。
如果要在主服务器和从服务器上使用不同存储引擎的表之间进行复制,请记住,针对一个表的版本运行同一条语句可能会产生不同的错误,但针对另一表的版本运行该语句可能会产生不同的错误,否则可能会导致一个错误该表的版本,但其他版本除外。例如,由于MyISAM
忽略外键约束,所以访问主服务器上的表的INSERT
or UPDATE
语句InnoDB
可能会导致外键冲突,但是MyISAM
在从属服务器上的同一表的版本上执行的同一语句不会产生此类错误,从而导致复制停止。
复制和服务器SQL模式
在主服务器和从服务器上使用不同的服务器SQL模式设置可能会导致在主服务器和从服务器上对相同的INSERT
语句进行不同的处理,从而导致主服务器和从服务器产生分歧。为了获得最佳结果,您应该始终在主服务器和从服务器上使用相同的服务器SQL模式。无论您使用的是基于语句的复制还是基于行的复制,此建议均适用。
如果要复制分区表,则在主服务器和从服务器上使用不同的SQL模式可能会引起问题。至少,这很可能导致分区之间的数据分配在给定表的主副本和从属副本中有所不同。这也可能导致在成功在主服务器上成功执行的分区表中的插入在从服务器上失败。
有关更多信息,请参见“服务器SQL模式”。
复制和临时表
在MySQL 8.0中,当binlog_format
设置为ROW
或时MIXED
,专用于临时表的语句不会记录在主数据库上,因此不会复制临时表。涉及临时表和非临时表混合的语句仅针对非临时表上的操作记录在主数据库上,而不会记录临时表上的操作。这意味着,如果从站意外关闭,则从站上不会丢失任何临时表。有关基于行的复制和临时表的更多信息,请参见临时表的基于行的日志记录。
当binlog_format
设置为时STATEMENT
,对临时表的操作将记录在主服务器上,并在从属服务器上复制,前提是可以使用基于语句的格式安全地记录涉及临时表的语句。在这种情况下,从属服务器上已复制临时表的丢失可能是一个问题。在基于语句的复制模式,CREATE TEMPORARY TABLE
并DROP TEMPORARY TABLE
语句不能交易,程序,函数,或触发器内使用时GTIDs是在服务器上使用(即,当enforce_gtid_consistency
系统变量被设置为ON
)。如果autocommit=1
已设置GTID,则可以在这些上下文之外使用它们。
由于关于临时表的基于行或混合复制模式与基于语句的复制模式之间的行为差异,因此,如果更改适用于包含任何打开的临时文件的上下文(全局或会话),则无法在运行时切换复制格式表。有关更多详细信息,请参见该binlog_format
选项的描述。
使用临时表时,安全关闭从站。在基于语句的复制模式下,临时表将被复制,除非您停止从属服务器(不仅是从属线程)并且已复制了临时表,这些临时表已打开供在尚未在从属服务器上执行的更新中使用。。如果停止从服务器,则重新启动从服务器时,这些更新所需的临时表将不再可用。为避免此问题,请勿在从站打开临时表时关闭它。而是,使用以下过程:
- 发表
STOP SLAVE SQL_THREAD
声明。 - 使用
SHOW STATUS
检查的数值Slave_open_temp_tables
变量。 - 如果该值不为0,请使用重新启动从SQL线程,
START SLAVE SQL_THREAD
并在以后重复此过程。 - 当值为0时,发出mysqladmin shutdown命令停止从服务器。
临时表和复制选项。默认情况下,使用基于语句的复制,将复制所有临时表;这种情况是否不存在任何匹配--replicate-do-db
,--replicate-do-table
或--replicate-wild-do-table
在效果选项。但是,--replicate-ignore-table
和--replicate-wild-ignore-table
选项适用于临时表。唯一的例外是,为了在会话结束时正确删除临时表,复制从属服务器始终复制一条DROP TEMPORARY TABLE IF EXISTS
语句,而不管通常适用于指定表的任何排除规则如何。
使用基于语句的复制时,建议的做法是指定一个前缀,以专用于命名您不想复制的临时表,然后使用一个--replicate-wild-ignore-table
选项来匹配该前缀。例如,你可能会给开头的每一这样的表的名称norep
(如norepmytable
,norepyourtable
等),然后使用--replicate-wild-ignore-table=norep%
,以防止它们被复制。
复制重试和超时
全局系统变量slave_transaction_retries
设置单线程或多线程复制从属服务器上的应用程序线程在停止之前自动重试失败的事务的最大次数。当SQL线程由于InnoDB
死锁而无法执行事务时,或者事务的执行时间超过该InnoDB
innodb_lock_wait_timeout
值时,将自动重试事务。如果事务有一个非临时性错误会阻止它成功执行,则不会重试该事务。
的默认设置为slave_transaction_retries
10,这意味着在应用程序线程停止之前,将重试失败的事务(带有明显的临时错误)10次。将该变量设置为0将禁用事务的自动重试。在多线程从站上,可以在所有通道的所有应用程序线程上执行指定次数的事务重试。性能模式表replication_applier_status
在该COUNT_TRANSACTIONS_RETRIES
列中显示了每个复制通道上发生的事务重试的总数。
重试事务的过程可能会导致复制从属服务器或组复制组成员(它们可以配置为单线程或多线程从属服务器)上出现延迟。性能架构表replication_applier_status_by_worker
显示有关单线程或多线程从属服务器上的应用程序线程重试事务的详细信息。此数据包括时间戳,该时间戳显示应用程序线程从头到尾应用最后一个事务花费了多长时间(以及当前正在进行的事务何时开始),以及在提交原始主服务器和直接主服务器之后经过多长时间。该数据还显示最后一个事务和当前正在进行的事务的重试次数,并使您能够识别导致重试事务的瞬时错误。您可以使用此信息来参见事务重试是否是复制延迟的原因,
复制和时区
默认情况下,主服务器和从服务器假定它们位于同一时区。如果要在不同时区的服务器之间复制,则必须同时在主服务器和从服务器上设置时区。否则,不能正确复制依赖于主服务器上本地时间的语句,例如使用NOW()
或FROM_UNIXTIME()
函数的语句。
验证主服务器和从system_time_zone
服务器上的系统时区(),服务器当前时区(的全局值time_zone
)和每个会话时区(的会话值time_zone
)的设置组合是否产生了正确的结果。特别是,如果将time_zone
系统变量设置为value SYSTEM
,指示服务器时区与系统时区相同,则这可能导致主服务器和从服务器应用不同的时区。例如,主服务器可以在二进制日志中编写以下语句:
SET @@session.time_zone='SYSTEM';
如果此主服务器及其从属服务器的系统时区设置不同,则即使从属服务器的全局time_zone
值已设置为与主服务器的全局值匹配,此语句也会在从属服务器上产生意外结果。有关MySQL Server时区设置以及如何更改它们的说明,请参见“ MySQL Server时区支持”。
另请参见“MySQL复制数据”。
复制和事务不一致
根据中继配置,可能会发生从中继日志执行的事务顺序不一致的情况。本节说明如何避免不一致并解决它们引起的任何问题。
可能存在以下类型的不一致:
- 半申请交易。更新非事务处理表的事务已应用了部分但不是全部更改。
- 缝隙。在给定一个有序的事务序列的情况下,在序列中后面的某个事务在该序列中之前的某个其他事务被应用之前,在外部化事务集中出现空白。间隙仅在使用多线程从站时出现。为避免出现间隙,请设置
slave_preserve_commit_order=1
。直到并包括MySQL 8.0.18,此设置要求还启用二进制日志记录(log_bin
)和从属更新日志记录(log_slave_updates
),这是MySQL 8.0的默认设置。从MySQL 8.0.19开始,不需要在从服务器上设置二进制日志和从服务器更新日志slave_preserve_commit_order=1
,并且可以根据需要将其禁用。在所有版本中,设置slave_preserve_commit_order=1
要求将slave_parallel_type
其设置为LOGICAL_CLOCK
,这不是默认设置。请注意,在某些特定情况下(如的说明中所述)slave_preserve_commit_order
,设置slave_preserve_commit_order=1
不能保留从属服务器上的提交顺序,因此在这些情况下,从从属服务器的中继日志执行的事务序列中仍可能出现间隙。 - 主日志位置滞后。即使没有间隙,也可能会
Exec_master_log_pos
应用之后的交易。也就是说,所有最新交易N
都已应用,之后没有交易N
,但Exec_master_log_pos
值小于N
。在这种情况下,Exec_master_log_pos
是所应用交易的“低水位线”,并且落后于最近应用的交易的头寸。这只能在多线程从站上发生。启用slave_preserve_commit_order
并不能防止主日志位置滞后。
以下方案与存在半申请的事务,差距和主日志位置滞后有关:
- 从线程正在运行时,可能存在间隙和半应用的事务。
- mysqld关闭。干净和不干净的关闭都会中止正在进行的事务,并且可能会留下空白和半应用的事务。
KILL
复制线程(使用单线程从属服务器时为SQL线程,使用多线程从属服务器时为协调器线程)。这会中止正在进行的交易,并可能留下缺口和半申请的交易。- 应用线程中的错误。这可能会留下空白。如果错误出在混合事务中,那么该事务将被应用一半。使用多线程从属服务器时,未收到错误的工作人员将完成队列,因此停止所有线程可能需要一些时间。
STOP SLAVE
使用多线程从站时。发行后STOP SLAVE
,从站等待所有间隙被填充,然后进行更新Exec_master_log_pos
。这样可以确保它永远不会留下间隙或主日志位置滞后,除非上述任何情况都适用,换句话说,在STOP SLAVE
完成之前,要么发生错误,要么发生另一个线程问题KILL
,要么服务器重新启动。在这些情况下,STOP SLAVE
成功返回。- 如果中继日志中的最后一个事务仅被接收一半,并且多线程从属协调器已开始将事务调度给工作人员,则
STOP SLAVE
最多等待60秒才能接收到该事务。超时后,协调器放弃并中止事务。如果交易混合,则可能会完成一半。 STOP SLAVE
使用单线程从属服务器时。如果正在进行的事务仅更新事务表,则会回滚并STOP SLAVE
立即停止。如果正在进行的事务混合在一起,则STOP SLAVE
最多等待60秒以使事务完成。在此超时之后,它将中止事务,因此它可能会保留一半。
全局变量rpl_stop_slave_timeout
与停止复制线程的过程无关。它只会使发出问题的客户端STOP SLAVE
返回到客户端,但是复制线程会继续尝试停止。
如果复制通道存在间隙,则会导致以下后果:
- 从属数据库处于可能永远不会存在于主数据库上的状态。
- 该领域
Exec_master_log_pos
中SHOW SLAVE STATUS
只是一个“低水位”。换句话说,保证在该头寸之前出现的交易已被保证已落实,但在该头寸之后的交易可能已承诺或未承诺。 CHANGE MASTER TO
除非应用程序线程正在运行并且该CHANGE MASTER TO
语句仅设置接收器选项,否则该通道的语句将失败并显示错误。- 如果mysqld以开头
--relay-log-recovery
,则该通道不会进行任何恢复,并且会显示警告。 如果mysqldump与一起使用
--dump-slave
,则不会记录间隙的存在;因而它打印CHANGE MASTER TO
与RELAY_LOG_POS
设置到“低水位标记”在适当的位置Exec_master_log_pos
。在将转储应用于另一台服务器上并启动复制线程之后,将再次复制该位置之后出现的事务。请注意,如果启用了GTID,这是无害的(但是,在这种情况下,不建议使用
--dump-slave
)。
如果复制通道具有主日志位置滞后但没有间隙,则上述情况2至5适用,但情况1不适用。
主日志位置信息以二进制格式保存在内部表中mysql.slave_worker_info
。START SLAVE[SQL_THREAD]
始终咨询此信息,以便仅应用正确的交易。即使在slave_parallel_workers
之前已更改为0 START SLAVE
,甚至START SLAVE
与UNTIL
子句一起使用,也是如此。START SLAVE UNTIL SQL_AFTER_MTS_GAPS
仅应用所需数量的交易以填补空白。如果START SLAVE
与UNTIL
用于告诉它在耗尽所有间隙之前停止的子句一起使用,则它将留下剩余的间隙。
RESET SLAVE
删除中继日志并重置复制位置。因此,RESET SLAVE
在具有间隙的从站上发布意味着从站会丢失有关间隙的任何信息,而无需校正间隙。
复制和事务
在同一笔交易中混合使用交易和非交易语句。通常,应避免在复制环境中同时更新事务表和非事务表的事务。您还应该避免使用任何访问事务性(或临时)和非事务性表并对其进行写操作的语句。
服务器使用以下规则进行二进制日志记录:
- 如果事务中的初始语句是非事务性的,则它们将立即写入二进制日志。事务中的其余语句将被缓存,直到提交事务后才写入二进制日志。(如果回滚事务,则仅当高速缓存的语句进行了无法回滚的非事务更改时,才会将其写入二进制日志。否则,将丢弃它们。)
- 对于基于语句的日志记录,非事务性语句的日志记录受
binlog_direct_non_transactional_updates
系统变量影响。当此变量为OFF
默认值时,日志记录如前所述。当此变量ON
为时,对于事务中任何地方发生的非事务性语句(不仅是初始的非事务性语句),都会立即进行日志记录。其他语句保留在事务缓存中,并在事务提交时记录。binlog_direct_non_transactional_updates
对行格式或混合格式的二进制日志记录无效。
事务性,非事务性和混合语句。要应用这些规则,如果服务器仅更改非事务表,则服务器将其视为非事务语句;如果仅更改事务表,则将其视为事务性语句。引用非事务和事务表并更新任何涉及的表的语句被视为“混合”语句。事务提交时,将混合语句(例如事务语句)缓存并记录下来。
如果该语句还执行以下任一操作,则更新该事务表的混合语句将被视为不安全:
- 更新或读取临时表
- 读取一个非事务表,并且事务隔离级别小于REPEATABLE_READ
如果在事务中更新事务表之后的混合语句执行以下任一操作,则被认为是不安全的:
- 更新任何表并从任何临时表中读取
- 更新非事务处理表并
binlog_direct_non_transactional_updates
关闭
有关更多信息,请参见“复制格式”。
注意混合语句与混合二进制日志记录格式无关。
在事务混合了对事务表和非事务表的更新的情况下,二进制日志中语句的顺序是正确的,并且即使在的情况下,所有需要的语句也会写入二进制日志ROLLBACK
。但是,当第二个连接在第一个连接事务完成之前更新了非事务表时,由于第二个连接更新是在执行之后立即写入的,所以无论第一个连接正在执行的事务状态如何,语句都可能被乱序记录。连接。
在主服务器和从服务器上使用不同的存储引擎。可以使用从属服务器上的非事务表复制主服务器上的事务表。例如,您可以将InnoDB
主表复制为MyISAM
从表。但是,如果这样做,则由于从站在BEGIN
...COMMIT
块的中间停止而导致问题,因为从站在BEGIN
块的开头重新启动。
将事务从MyISAM
主服务器上的表复制InnoDB
到从属服务器上的事务表(例如使用存储引擎的表)也是安全的。在这种情况下,将AUTOCOMMIT=1
复制在主服务器上发出的语句,从而AUTOCOMMIT
在从服务器上强制执行模式。
当从属服务器的存储引擎类型为非事务处理时,应避免在主服务器上混合事务和非事务处理表的更新的事务,因为它们会导致主事务表和从属非事务处理表之间的数据不一致。也就是说,此类事务可能导致特定于主存储引擎的行为,并可能导致复制不同步的影响。MySQL不会对此发出警告,因此在将事务表从主表复制到从属表上的非事务表时,应格外小心。
更改事务中的二进制日志记录格式。只要交易正在进行,binlog_format
和binlog_checksum
变量都是只读的。
每个事务(包括autocommit
事务)都被记录在二进制日志中,就像它以一条BEGIN
语句开始,以 a COMMIT
或一条ROLLBACK
语句结束一样。对于影响使用非事务性存储引擎(例如MyISAM
)的表的语句,甚至是这样。
有关专门适用于XA交易的限制,请参见“ XA交易限制”。
复制和触发器
使用基于语句的复制时,在主服务器上执行的触发器也将在从服务器上执行。使用基于行的复制时,在主服务器上执行的触发器不会在从服务器上执行。取而代之的是,由触发器执行导致的主节点上的行更改被复制并应用于从节点。
此行为是设计使然。如果在基于行的复制中从属应用触发器以及由触发器引起的行更改,则更改实际上将在从属上应用两次,从而导致主控和从属上的数据不同。
如果希望触发器在主服务器和从服务器上都执行(也许是因为在主服务器和从服务器上有不同的触发器),则必须使用基于语句的复制。但是,要启用从属端触发器,不必专门使用基于语句的复制。仅对需要这种效果的那些语句切换到基于语句的复制就足够了,并在其余时间使用基于行的复制就足够了。
AUTO_INCREMENT
使用基于语句的复制无法正确复制导致导致列更新的调用触发器(或函数)的语句。MySQL 8.0将此类语句标记为不安全。错误45677)
触发器可以具有用于触发事件的不同组合(触发器INSERT
,UPDATE
,DELETE
)和动作时间(BEFORE
,AFTER
),和多个触发器是允许的。
为简便起见,“多个触发器”是“具有相同触发事件和动作时间的多个触发器”的简写。”
升级。MySQL 5.7之前的版本不支持多个触发器。如果要在复制拓扑中升级使用MySQL 5.7之前版本的服务器,请先升级复制从服务器,然后再升级主服务器。如果升级的复制主服务器仍具有使用不支持多个触发器的MySQL版本的旧从服务器,则如果在主服务器上为已具有具有相同触发事件和动作时间的触发器的表创建了触发器,则这些从服务器上将发生错误。
降级。如果将支持多个触发器的服务器降级为不支持的触发器,则降级会产生以下影响:
- 对于每个具有触发器的表,所有触发器定义都在
.TRG
该表的文件中。但是,如果有多个具有相同触发事件和动作时间的触发,则当触发事件发生时,服务器仅执行其中一个。有关.TRG
文件的信息,请参见MySQL Server Doxygen文档的Table Trigger Storage部分,该文档可从https://dev.mysql.com/doc/index-other.html获得。 - 如果在降级之后添加或删除了表的触发器,则服务器将重写表的
.TRG
文件。每次触发事件和动作时间的组合,重写的文件仅保留一个触发;其他人迷路了。
为避免这些问题,请在降级之前修改触发器。对于每个包含触发事件和动作时间组合的多个触发器的表,将每个这样的一组触发器转换为单个触发器,如下所示:
- 对于每个触发器,创建一个存储的例程,其中包含触发器中的所有代码。使用
NEW
和访问的值OLD
可以使用参数传递给例程。如果触发器需要代码中的单个结果值,则可以将代码放入存储的函数中,并使该函数返回该值。如果触发器需要代码中的多个结果值,则可以将代码放入存储过程中,并使用OUT
参数返回值。 - 删除表的所有触发器。
- 为表创建一个新的触发器,该触发器调用刚刚创建的存储例程。因此,此触发器的效果与其替换的多个触发器相同。
复制和截断表
TRUNCATE TABLE
通常被视为DML语句,因此当二进制日志记录模式为ROW
或时,可以期望使用基于行的格式进行记录和复制MIXED
。然而,这造成的问题时,记录或复制,在STATEMENT
或MIXED
模式下,使用的事务性存储引擎,如表InnoDB
当事务隔离级别是READ COMMITTED
或READ UNCOMMITTED
,这就排除了基于语句的记录。
TRUNCATE TABLE
出于日志记录和复制的目的,该命令被视为DDL而不是DML,以便可以将其记录为语句并复制。但是,该语句InnoDB
和其他事务表对复制从属服务器的影响仍然遵循“ TRUNCATE TABLE语句”中描述的规则,以控制这些表。错误36763)
复制和用户名长度
MySQL用户名的最大长度为32个字符。将长度超过16个字符的用户名复制到仅支持较短用户名的MySQL 5.7之前的从属服务器将失败。但是,仅当从较新的主服务器复制到较旧的从属服务器时才应该发生这种情况,这不是建议的配置。
复制和变量
使用STATEMENT
模式时,系统变量不能正确复制,但以下变量与会话范围一起使用时除外:
auto_increment_increment
auto_increment_offset
character_set_client
character_set_connection
character_set_database
character_set_server
collation_connection
collation_database
collation_server
foreign_key_checks
identity
last_insert_id
lc_time_names
pseudo_thread_id
sql_auto_is_null
time_zone
timestamp
unique_checks
使用MIXED
mode时,上一列表中的变量与会话作用域一起使用时,会导致从基于语句的日志记录切换到基于行的日志记录。请参见“MySQL服务器二进制日志”。
sql_mode
除了NO_DIR_IN_CREATE
模式外,也被复制;从属设备始终为保留自己的值NO_DIR_IN_CREATE
,而不管主控设备上的更改如何。所有复制格式都是如此。
但是,当mysqlbinlog解析一条语句时,包括在内的完整值将传递到接收服务器。因此,在使用模式时,复制这样的语句可能并不安全。SET @@sql_mode =mode
mode
NO_DIR_IN_CREATE
STATEMENT
所述default_storage_engine
系统变量是不可复制的,无论记录模式的;这旨在促进不同存储引擎之间的复制。
该read_only
系统变量是不可复制的。另外,启用此变量对临时表,表锁定和SET PASSWORD
不同MySQL版本中的语句有不同的影响。
该max_heap_table_size
系统变量是不可复制的。尝试在主服务器上执行表上的语句时,如果在主服务器上执行此语句的值大于在从属服务器上的对应表的语句,则在从属服务器上增加此变量的值而不在从属服务器上执行此操作的值最终会导致表错误。。有关更多信息,INSERT
MEMORY
在基于语句的复制中,会话变量在更新表的语句中使用时无法正确复制。例如,以下语句序列将不会在主服务器和从服务器上插入相同的数据:
SET max_join_size=1000;INSERT INTO mytableVALUES (@@max_join_size);
这不适用于通用序列:
SET time_zone=...;INSERT INTO mytableVALUES (CONVERT_TZ(..., ..., @@time_zone));
使用基于行的复制时,会话变量的复制不是问题,在这种情况下,会话变量始终被安全地复制。请参见“复制格式”。
以下会话变量将被写入二进制日志,并在解析二进制日志时由复制从属服务器遵循,而与日志记录格式无关:
sql_mode
foreign_key_checks
unique_checks
character_set_client
collation_connection
collation_database
collation_server
sql_auto_is_null
重要即使与字符集和归类有关的会话变量已写入二进制日志,也不支持不同字符集之间的复制。
为了帮助减少可能的混乱,我们建议始终lower_case_table_names
在主服务器和从服务器上对系统变量使用相同的设置,尤其是在具有区分大小写的文件系统的平台上运行MySQL时。lower_case_table_names
只能在初始化服务器时配置该设置。
复制和视图
视图始终复制到从属服务器。视图是按自己的名称过滤的,而不是按引用的表过滤的。这意味着即使视图包含通常会被replication-ignore-table
规则过滤掉的表,也可以将视图复制到从属服务器。因此,应注意确保视图不会复制出于安全原因通常会被过滤的表数据。
使用基于语句的日志记录支持从表复制到同名视图,但使用基于行的日志记录则不支持。当基于行的日志记录有效时,尝试这样做会导致错误。