19.2.1.3 确定二进制日志记录中的安全和不安全语句
MySQL 复制中语句的““安全性””指的是语句及其效果是否可以使用语句格式正确地复制。如果语句满足这个条件,我们称其为安全语句;否则,我们称其为不安全语句。
通常情况下,语句是安全的,如果它是确定性的;否则,它是unsafe的。然而,有些非确定性函数不被认为是unsafe(见非确定性函数不被认为是unsafe,在本节后面)。此外,使用浮点数数学函数的语句总是被认为是unsafe(见Section 19.5.1.12, “Replication and Floating-Point Values”)。
处理安全和不安全语句的方法。 语句的处理方式取决于语句是否被认为是安全的,以及与二进制日志格式(即当前的binlog_format
值)有关。
-
在使用行格式日志时,不会根据语句是否安全进行区分。
-
在使用混合格式日志时,标记为不安全的语句将使用行格式日志,标记为安全的语句将使用语句格式日志。
-
在使用语句格式日志时,标记为不安全的语句将生成警告。安全语句将被正常记录。
每个标记为不安全的语句都将生成警告。如果在源服务器上执行了大量这样的语句,这可能会导致错误日志文件过大。为了防止这种情况,MySQL 提供了警告抑制机制。每当在 50 秒内生成的 50 个以上的ER_BINLOG_UNSAFE_STATEMENT
警告时,这种警告抑制将被激活。激活后,这些警告将不再写入错误日志,而是将写入一个警告,指出在最近 50 秒内生成的警告次数和频率。这种警告抑制机制对语句的安全性判断和客户端收到的警告没有影响。客户端仍然会收到每个语句的警告。
更多信息,请见Section 19.2.1, “Replication Formats”。
被认为是unsafe的语句。具有以下特征的语句被认为是unsafe:
-
包含系统函数的语句可能在复制服务器上返回不同的值。 这些函数包括
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 Server的选择。
在具有多个唯一或主键的表上执行
INSERT ... ON DUPLICATE KEY UPDATE
语句将被标记为不安全(Bug #11765650,Bug #58637)。 -
使用LIMIT的更新. 读取的顺序不确定,因此被认为是安全的。请见第19.5.1.18节,“复制和LIMIT”。
-
访问或引用日志表. 日志表的内容可能在源和副本之间不同。
-
非事务操作在事务操作后. 在事务中,允许任何非事务读取或写入在任何事务读取或写入后执行是安全的。
更多信息,请见第19.5.1.35节,“复制和事务”。
-
访问或引用自_logging表. 所有读取和写入自_logging表都被认为是安全的。在事务中,任何语句在读取或写入自_logging表后执行的语句也被认为是安全的。
-
LOAD 数据语句。
LOAD 数据
被视为不安全的语句,当binlog_format=MIXED
时,该语句将以行基于的格式记录在二进制日志中。 当binlog_format=STATEMENT
时,LOAD 数据
不会生成警告,除非其他不安全语句。 -
XA事务。 如果两个XA事务在源端并行提交,然后在副本端以相反的顺序准备,这可能会导致锁定依赖关系无法安全地解决,从而导致副本端的死锁。 当
binlog_format=STATEMENT
时,DML语句内的XA事务将被标记为不安全,并生成警告。 当binlog_format=MIXED
或binlog_format=ROW
时,DML语句内的XA事务将使用行基于的复制,潜在问题将不存在。 -
DEFAULT
子句引用非确定性函数。 如果表达式默认值引用非确定性函数,则任何导致表达式被评估的语句都将被视为不安全的语句,这包括语句如INSERT
、UPDATE
和ALTER TABLE
。与大多数其他不安全语句不同,这类语句不能安全地在行基于的格式下复制。 当binlog_format
设置为STATEMENT
时,语句将被记录和执行,但将在错误日志中写入警告信息。 当binlog_format
设置为MIXED
或ROW
时,语句将不执行,并将在错误日志中写入错误信息。 有关显式默认值的处理,请参阅Explicit Default Handling。