MySQL 复制中语句的“安全性”是指语句及其效果是否可以使用基于语句的格式正确复制。如果该语句为真,我们称该语句为 安全的;否则,我们将其称为unsafe。
一般来说,如果一个语句是确定性的,它就是安全的,如果不是,它就是不安全的。但是,某些非确定性函数 不被认为是不安全的(请参阅 本节后面的非确定性函数不被认为是不安全的)。此外,使用浮点数学函数结果的语句(依赖于硬件)总是被认为是不安全的(参见 第 17.5.1.12 节,“复制和浮点值”)。
处理安全和不安全的语句。 根据语句是否被认为是安全的,以及相对于二进制日志记录格式(即 的当前值 binlog_format),对语句的处理方式会有所不同。
- 使用基于行的日志记录时,对安全和不安全语句的处理没有区别。
- 使用混合格式日志记录时,标记为不安全的语句使用基于行的格式进行记录;被视为安全的语句使用基于语句的格式记录。
- 使用基于语句的日志记录时,标记为不安全的语句会为此生成警告。安全语句正常记录。
每个标记为不安全的语句都会生成一个警告。如果在源上执行了大量此类语句,则可能导致错误日志文件过大。为了防止这种情况,MySQL 有一个警告抑制机制。只要 50 个最近的 ER_BINLOG_UNSAFE_STATEMENT 警告在任何 50 秒内生成超过 50 次,就会启用警告抑制。激活后,这会导致此类警告不写入错误日志;相反,对于这种类型的每 50 个警告,一个注释The last warning was repeated *N* times in last *S* seconds写入错误日志。只要最近 50 个此类警告在 50 秒或更短时间内发出,这种情况就会持续;一旦速率降至此阈值以下,警告将再次正常记录。警告抑制对如何确定基于语句的日志记录的语句安全性没有影响,也对如何向客户端发送警告没有影响。MySQL 客户端仍然会为每个这样的语句收到一个警告。
有关更多信息,请参阅第 17.2.1 节,“复制格式”。
被认为不安全的语句。 具有以下特征的语句被认为是不安全的:
-
包含可能在副本上返回不同值的系统函数的语句。 这些函数包括
FOUND_ROWS(),GET_LOCK(),IS_FREE_LOCK(),IS_USED_LOCK(),LOAD_FILE(),MASTER_POS_WAIT(),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().有关更多信息,请参阅 第 17.5.1.14 节,“复制和系统函数”。
-
对系统变量的引用。 大多数系统变量没有使用基于语句的格式正确复制。请参阅 第 17.5.1.39 节,“复制和变量”。有关例外情况,请参阅第 5.4.4.3 节,“混合二进制日志记录格式”。
-
可加载函数。 由于我们无法控制可加载函数的作用,我们必须假设它正在执行不安全的语句。
-
全文插件。 这个插件在不同的 MySQL 服务器上可能表现不同;因此,依赖于它的语句可能会产生不同的结果。因此,所有依赖全文插件的语句在 MySQL 中都被视为不安全的。
-
触发器或存储程序更新具有 AUTO_INCREMENT 列的表。 这是不安全的,因为更新行的顺序在源和副本上可能不同。
此外,
INSERT进入具有复合主键的表包含AUTO_INCREMENT不是该复合键第一列的列是不安全的。有关更多信息,请参阅 第 17.5.1.1 节,“复制和 AUTO_INCREMENT”。
-
INSERT … ON DUPLICATE KEY UPDATE 对具有多个主键或唯一键的表的语句。 当对包含多个主键或唯一键的表执行时,此语句被认为是不安全的,它对存储引擎检查键的顺序敏感,这不是确定性的,并且选择的行更新由MySQL 服务器取决于。
INSERT ... ON DUPLICATE KEY UPDATE针对具有多个唯一键或主键的表 的 语句被标记为对于基于语句的复制不安全。(错误 #11765650、错误 #58637) -
使用 LIMIT 更新。 未指定检索行的顺序,因此被认为是不安全的。请参阅 第 17.5.1.18 节,“复制和限制”。
-
访问或引用日志表。 系统日志表的内容可能在源和副本之间有所不同。
-
事务操作之后的非事务操作。 在事务中,允许在任何事务性读取或写入之后执行任何非事务性读取或写入被认为是不安全的。
有关更多信息,请参阅 第 17.5.1.35 节,“复制和事务”。
-
访问或引用自记录表。 所有对自记录表的读取和写入都被认为是不安全的。在事务中,对自记录表进行读取或写入之后的任何语句也被认为是不安全的。
-
加载数据语句。 当语句以基于行的格式记录时,
LOAD DATA被视为不安全 。binlog_format=MIXED与其他不安全语句不同, When 不会生成警告。binlog_format=STATEMENTLOAD DATA -
XA 交易。 如果在源上并行提交的两个 XA 事务正在以相反的顺序在副本上准备,则基于语句的复制可能会发生锁定依赖关系,无法安全解决,并且复制可能会因副本上的死锁而失败。设置时
binlog_format=STATEMENT,XA 事务中的 DML 语句被标记为不安全并生成警告。当 设置binlog_format=MIXEDor 时binlog_format=ROW,XA 事务中的 DML 语句使用基于行的复制进行记录,并且不存在潜在问题。 -
DEFAULT引用非确定性函数的子句。 如果表达式默认值引用非确定性函数,则导致计算表达式的任何语句对于基于语句的复制都是不安全的。这包括诸如INSERT、UPDATE和 之类的语句ALTER TABLE。与大多数其他不安全语句不同,此类语句不能以基于行的格式安全地复制。当binlog_format设置为 时STATEMENT,将记录并执行该语句,但会在错误日志中写入一条警告消息。何时binlog_format设置为MIXED或ROW,则不执行该语句,并将错误消息写入错误日志。有关显式默认值处理的更多信息,请参阅 MySQL 8.0.13 的显式默认处理。
有关其他信息,请参阅 第 17.5.1 节,“复制功能和问题”。17.2.1.3 二进制日志中安全和不安全语句的确定
MySQL 复制中语句的“安全性”是指语句及其效果是否可以使用基于语句的格式正确复制。如果该语句为真,我们称该语句为 安全的;否则,我们将其称为unsafe。
一般来说,如果一个语句是确定性的,它就是安全的,如果不是,它就是不安全的。但是,某些非确定性函数 不被认为是不安全的(请参阅 本节后面的非确定性函数不被认为是不安全的)。此外,使用浮点数学函数结果的语句(依赖于硬件)总是被认为是不安全的(参见 第 17.5.1.12 节,“复制和浮点值”)。
处理安全和不安全的语句。 根据语句是否被认为是安全的,以及相对于二进制日志记录格式(即 的当前值 binlog_format),对语句的处理方式会有所不同。
- 使用基于行的日志记录时,对安全和不安全语句的处理没有区别。
- 使用混合格式日志记录时,标记为不安全的语句使用基于行的格式进行记录;被视为安全的语句使用基于语句的格式记录。
- 使用基于语句的日志记录时,标记为不安全的语句会为此生成警告。安全语句正常记录。
每个标记为不安全的语句都会生成一个警告。如果在源上执行了大量此类语句,则可能导致错误日志文件过大。为了防止这种情况,MySQL 有一个警告抑制机制。只要 50 个最近的 ER_BINLOG_UNSAFE_STATEMENT 警告在任何 50 秒内生成超过 50 次,就会启用警告抑制。激活后,这会导致此类警告不写入错误日志;相反,对于这种类型的每 50 个警告,一个注释The last warning was repeated *N* times in last *S* seconds写入错误日志。只要最近 50 个此类警告在 50 秒或更短时间内发出,这种情况就会持续;一旦速率降至此阈值以下,警告将再次正常记录。警告抑制对如何确定基于语句的日志记录的语句安全性没有影响,也对如何向客户端发送警告没有影响。MySQL 客户端仍然会为每个这样的语句收到一个警告。
有关更多信息,请参阅第 17.2.1 节,“复制格式”。
被认为不安全的语句。 具有以下特征的语句被认为是不安全的:
-
包含可能在副本上返回不同值的系统函数的语句。 这些函数包括
FOUND_ROWS(),GET_LOCK(),IS_FREE_LOCK(),IS_USED_LOCK(),LOAD_FILE(),MASTER_POS_WAIT(),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().有关更多信息,请参阅 第 17.5.1.14 节,“复制和系统函数”。
-
对系统变量的引用。 大多数系统变量没有使用基于语句的格式正确复制。请参阅 第 17.5.1.39 节,“复制和变量”。有关例外情况,请参阅第 5.4.4.3 节,“混合二进制日志记录格式”。
-
可加载函数。 由于我们无法控制可加载函数的作用,我们必须假设它正在执行不安全的语句。
-
全文插件。 这个插件在不同的 MySQL 服务器上可能表现不同;因此,依赖于它的语句可能会产生不同的结果。因此,所有依赖全文插件的语句在 MySQL 中都被视为不安全的。
-
触发器或存储程序更新具有 AUTO_INCREMENT 列的表。 这是不安全的,因为更新行的顺序在源和副本上可能不同。
此外,
INSERT进入具有复合主键的表包含AUTO_INCREMENT不是该复合键第一列的列是不安全的。有关更多信息,请参阅 第 17.5.1.1 节,“复制和 AUTO_INCREMENT”。
-
INSERT … ON DUPLICATE KEY UPDATE 对具有多个主键或唯一键的表的语句。 当对包含多个主键或唯一键的表执行时,此语句被认为是不安全的,它对存储引擎检查键的顺序敏感,这不是确定性的,并且选择的行更新由MySQL 服务器取决于。
INSERT ... ON DUPLICATE KEY UPDATE针对具有多个唯一键或主键的表 的 语句被标记为对于基于语句的复制不安全。(错误 #11765650、错误 #58637) -
使用 LIMIT 更新。 未指定检索行的顺序,因此被认为是不安全的。请参阅 第 17.5.1.18 节,“复制和限制”。
-
访问或引用日志表。 系统日志表的内容可能在源和副本之间有所不同。
-
事务操作之后的非事务操作。 在事务中,允许在任何事务性读取或写入之后执行任何非事务性读取或写入被认为是不安全的。
有关更多信息,请参阅 第 17.5.1.35 节,“复制和事务”。
-
访问或引用自记录表。 所有对自记录表的读取和写入都被认为是不安全的。在事务中,对自记录表进行读取或写入之后的任何语句也被认为是不安全的。
-
加载数据语句。 当语句以基于行的格式记录时,
LOAD DATA被视为不安全 。binlog_format=MIXED与其他不安全语句不同, When 不会生成警告。binlog_format=STATEMENTLOAD DATA -
XA 交易。 如果在源上并行提交的两个 XA 事务正在以相反的顺序在副本上准备,则基于语句的复制可能会发生锁定依赖关系,无法安全解决,并且复制可能会因副本上的死锁而失败。设置时
binlog_format=STATEMENT,XA 事务中的 DML 语句被标记为不安全并生成警告。当 设置binlog_format=MIXEDor 时binlog_format=ROW,XA 事务中的 DML 语句使用基于行的复制进行记录,并且不存在潜在问题。 -
DEFAULT引用非确定性函数的子句。 如果表达式默认值引用非确定性函数,则导致计算表达式的任何语句对于基于语句的复制都是不安全的。这包括诸如INSERT、UPDATE和 之类的语句ALTER TABLE。与大多数其他不安全语句不同,此类语句不能以基于行的格式安全地复制。当binlog_format设置为 时STATEMENT,将记录并执行该语句,但会在错误日志中写入一条警告消息。何时binlog_format设置为MIXED或ROW,则不执行该语句,并将错误消息写入错误日志。有关显式默认值处理的更多信息,请参阅 MySQL 8.0.13 的显式默认处理。
有关其他信息,请参阅 第 17.5.1 节,“复制功能和问题”。




