在 MySQL 复制中,语句的“安全性”是指该语句和其效果是否可以使用基于语句的格式正确地复制。如果语句满足这个条件,我们将其称为 安全;否则,我们将其称为 不安全。
一般来说,一个语句是安全的,如果它是确定性的;否则,它是不安全的。然而,一些非确定性函数不被认为是不安全的(见 不被认为是不安全的非确定性函数,本节后面)。此外,使用浮点数数学函数的结果的语句——这些函数是硬件依赖的——总是被认为是不安全的(见 第 19.5.1.12 节,“复制和浮点数值”)。
安全和不安全语句的处理。 语句的处理方式取决于该语句是否被认为是安全的,以及当前的二进制日志记录格式(即 binlog_format
的当前值)。
-
使用基于行的日志记录时,不区分安全和不安全语句。
-
使用混合格式日志记录时,被标记为不安全的语句将使用基于行的格式记录,而被认为是安全的语句将使用基于语句的格式记录。
-
使用基于语句的日志记录时,被标记为不安全的语句将生成警告。安全语句将正常记录。
每个被标记为不安全的语句都会生成警告。如果源上执行了大量这样的语句,这可能会导致错误日志文件变得非常大。为了防止这种情况,MySQL 有一个警告抑制机制。只要在 50 秒内生成了 50 个以上的 ER_BINLOG_UNSAFE_STATEMENT
警告,警告抑制机制就会被激活。当激活时,这将导致这些警告不被写入错误日志;相反,每 50 个警告将写入一条 The last warning was repeated
信息到错误日志中。这将继续下去,直到最近的 50 个警告是在 50 秒内生成的;一旦速率降低到这个阈值以下,警告将再次正常记录。警告抑制机制对语句的安全性判定和警告发送到客户端没有影响。MySQL 客户端仍将收到每个这样的语句的一个警告。N
times in last S
seconds
有关更多信息,请参阅 第 19.2.1 节,“复制格式”。
被认为是不安全的语句。 具有以下特征的语句被认为是不安全的:
-
包含可能在副本上返回不同值的系统函数的语句。 这些函数包括
FOUND_ROWS()
,GET_LOCK()
,IS_FREE_LOCK()
,IS_USED_LOCK()
,LOAD_FILE()
,RAND()
,RELEASE_LOCK()
,ROW_COUNT()
,SESSION_USER()
,SLEEP()
,SOURCE_POS_WAIT()
,SYSDATE()
,SYSTEM_USER()
,USER()
,UUID()
, 和UUID_SHORT()
。非确定性函数不被认为是不安全的。 尽管这些函数不是确定性的,但它们被视为日志记录和复制的安全函数:
CONNECTION_ID()
,CURDATE()
,CURRENT_DATE()
,CURRENT_TIME()
,CURRENT_TIMESTAMP()
,CURTIME()
,LAST_INSERT_ID()
,LOCALTIME()
,LOCALTIMESTAMP()
,NOW()
,UNIX_TIMESTAMP()
,UTC_DATE()
,UTC_TIME()
, 和UTC_TIMESTAMP()
。有关更多信息,请参阅 第 19.5.1.14 节,“复制和系统函数”。
-
系统变量的引用。 大多数系统变量不能使用基于语句的格式正确地复制。请参阅 第 19.5.1.39 节,“复制和变量”。对于例外情况,请参阅 第 7.4.4.3 节,“混合二进制日志格式”。
-
可加载函数。 由于我们无法控制可加载函数的行为,因此我们必须假设它执行了不安全的语句。
-
全文插件。 由于这个插件可能在不同的 MySQL 服务器上表现不同,因此依赖于它的语句可能会产生不同的结果。因此,在 MySQL 中,所有依赖于全文插件的语句都被视为不安全的。
-
触发器或存储程序更新了具有 AUTO_INCREMENT 列的表。 这是不安全的,因为源和副本上的更新顺序可能不同。
此外,
INSERT
到具有复合主键的表中,其中 AUTO_INCREMENT 列不是第一个列,也是不安全的。有关更多信息,请参阅 第 19.5.1.1 节,“复制和 AUTO_INCREMENT”。
-
在具有多个主键或唯一键的表上执行INSERT ... ON DUPLICATE KEY UPDATE语句。 当执行该语句时,对于包含多个主键或唯一键的表,语句被认为是不安全的,因为存储引擎检查键的顺序是不确定的,并且MySQL服务器的选择取决于该顺序。
在具有多个唯一键或主键的表上执行
INSERT ... ON DUPLICATE KEY UPDATE
语句被标记为基于语句的复制不安全。(Bug #11765650, Bug #58637) -
使用LIMIT的更新。 行的检索顺序未指定,因此被认为是不安全的。见第19.5.1.18节,“复制和LIMIT”。
-
访问或引用日志表。 系统日志表的内容可能在源和副本之间不同。
-
事务后执行非事务操作。 在事务中,允许任何非事务读取或写入在任何事务读取或写入后执行,被认为是不安全的。
更多信息,请参见第19.5.1.35节,“复制和事务”。
-
访问或引用自记录表。 所有对自记录表的读取和写入被认为是不安全的。在事务中,任何跟随读取或写入自记录表的语句也被认为是不安全的。
-
LOAD DATA语句。
LOAD DATA
被视为不安全的,并且当binlog_format=MIXED
时,该语句以基于行的格式记录日志。当binlog_format=STATEMENT
时,LOAD DATA
语句不生成警告,与其他不安全语句不同。 -
XA事务。 如果两个XA事务在源上并行提交,在副本上以逆序准备时,可能会出现锁定依赖关系,无法安全地解决,并且可能会在副本上出现死锁。当
binlog_format=STATEMENT
时,XA事务中的DML语句被标记为不安全,并生成警告。当binlog_format=MIXED
或binlog_format=ROW
时,XA事务中的DML语句使用基于行的复制记录,不存在潜在的问题。 -
DEFAULT
子句引用了不确定函数。 如果表达式默认值引用了不确定函数,那么任何导致表达式被评估的语句都是基于语句的复制不安全的。这包括语句,如INSERT
、UPDATE
和ALTER TABLE
。与大多数其他不安全语句不同,这类语句不能在基于行的格式下安全地复制。当binlog_format
设置为STATEMENT
时,该语句被记录和执行,但警告消息被写入错误日志。当binlog_format
设置为MIXED
或ROW
时,该语句不被执行,并且错误消息被写入错误日志。更多信息,请参见显式默认处理。
更多信息,请参见第19.5.1节,“复制功能和问题”。