16.4.1.15 复制和系统功能
某些功能在某些情况下不能很好地复制:
- USER(),CURRENT_USER()(或CURRENT_USER),UUID(),VERSION()和LOAD_FILE()函数被复制而没有更改,因此除非启用了基于行的复制,否则它们无法在副本上可靠地工作。 (请参见第 16.2.1 节“复制格式”。)
使用MIXED
模式时,将使用基于行的复制自动复制USER()和CURRENT_USER(),并在STATEMENT
模式下生成警告。 (另请参见第 16.4.1.8 节“ CURRENT_USER()的复制”。)VERSION()和RAND()也是如此。
- 对于NOW(),二进制日志包括时间戳。这意味着将值在源上对该函数的调用返回的值复制到副本。为了避免在不同时区的 MySQL 服务器之间进行复制时出现意外结果,请在源和副本上都设置时区。有关更多信息,请参见第 16.4.1.31 节,“复制和时区”。
为了解释在不同时区的服务器之间进行复制时的潜在问题,假设源位于纽约,副本位于斯德哥尔摩,并且两台服务器都使用本地时间。进一步假设,在源代码上,创建一个 tablemytable
,对该 table 执行INSERT语句,然后从 table 中进行选择,如下所示:
mysql> CREATE TABLE mytable (mycol TEXT);
Query OK, 0 rows affected (0.06 sec)
mysql> INSERT INTO mytable VALUES ( 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 个小时;因此,如果您在同一 Moment 在副本上发出SELECT NOW()
,则将返回值2009-09-01 18:00:00
。因此,如果在刚刚显示的CREATE TABLE和INSERT语句被复制之后,从副本的mytable
副本中进行选择,则您可能希望mycol
包含值2009-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,就可以安全地将其忽略。
使用MIXED
模式时,将使用基于行的复制自动复制SYSDATE(),并在STATEMENT
模式下生成警告。
- *以下限制仅适用于基于语句的复制,不适用于基于行的复制.*处理用户级锁的GET_LOCK(),RELEASE_LOCK(),IS_FREE_LOCK()和IS_USED_LOCK()函数被复制,而副本不知道源上的并发上下文。因此,不应使用这些函数插入源 table,因为副本上的内容会有所不同。例如,请勿发出诸如
INSERT INTO mytable VALUES(GET_LOCK(...))
之类的语句。
使用MIXED
模式时,将使用基于行的复制功能自动复制这些功能,并在STATEMENT
模式下生成警告。
当基于语句的复制生效时,作为上述限制的解决方法,您可以使用以下策略:将有问题的函数结果保存在用户变量中,并在以后的语句中引用该变量。例如,由于引用了UUID()函数,因此以下单行INSERT是有问题的:
INSERT INTO t VALUES(UUID());
要变通解决此问题,请执行以下操作:
SET @my_uuid = UUID();
INSERT INTO t VALUES(@my_uuid);
由于@my_uuid
的值在INSERT语句之前作为用户变量事件存储在二进制日志中,并且可以在INSERT中使用,因此该语句序列得以复制。
同样的想法适用于多行插入,但使用起来比较麻烦。对于两行插入,可以执行以下操作:
SET @my_uuid1 = UUID(); @my_uuid2 = UUID();
INSERT INTO t VALUES(@my_uuid1),(@my_uuid2);
但是,如果行数很大或未知,则变通办法将很难或不可行。例如,您不能将以下语句转换为给定的单个用户变量与每一行相关联的语句:
INSERT INTO t2 SELECT UUID(), * FROM t1;
在已存储的函数中,只要在函数执行期间仅调用一次RAND(),即可正确复制。 (您可以将函数执行时间戳和随机数种子视为隐式 Importing,这些隐式 Importing 在源和副本上是相同的.)
使用基于语句的复制无法可靠地复制FOUND_ROWS()和ROW_COUNT()函数。一种解决方法是将函数调用的结果存储在用户变量中,然后在INSERT语句中使用该结果。例如,如果您希望将结果存储在名为mytable
的 table 中,则通常可以这样进行:
SELECT SQL_CALC_FOUND_ROWS FROM mytable LIMIT 1;
INSERT INTO mytable VALUES( FOUND_ROWS() );
但是,如果要复制mytable
,则应使用选择...进入,然后将变量存储在 table 中,如下所示:
SELECT SQL_CALC_FOUND_ROWS INTO @found_rows FROM mytable LIMIT 1;
INSERT INTO mytable VALUES(@found_rows);
这样,用户变量将作为上下文的一部分进行复制,并正确地应用于副本。
使用MIXED
模式时,将使用基于行的复制功能自动复制这些功能,并在STATEMENT
模式下生成警告。 (缺陷#12092,缺陷#30244)
在 MySQL 5.7.3 之前,如果副本上启用了诸如--replicate-ignore-db和--replicate-do-table之类的任何过滤选项,则不能正确复制LAST_INSERT_ID()的值。 (缺陷号 17234370,缺陷号 69861)