在 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节,“复制功能和问题”。