19.5.1.14 复制和系统函数
某些函数在某些情况下不太好地复制:
-
函数
USER()
、CURRENT_USER()
(或CURRENT_USER
)、UUID()
、VERSION()
和LOAD_FILE()
函数在不启用行级复制的情况下无法可靠地在副本上工作。 (参见第19.2.1节,“复制格式”。)USER()
和CURRENT_USER()
在使用MIXED
模式时自动使用行级复制,并在STATEMENT
模式下生成警告。 (参见第19.5.1.8节,“当前用户复制”。)这也适用于VERSION()
和RAND()
。 -
对于
NOW()
,二进制日志包括时间戳。这意味着在源服务器上对该函数的调用返回的值将被复制到副本上。为了避免在不同时区之间复制MySQL服务器时出现意外结果,请在源服务器和副本服务器上设置相同的时区。更多信息,请参见第19.5.1.33节,“复制和时区”。为了解释在不同时区之间复制可能出现的问题,假设源服务器位于纽约,副本服务器位于斯德哥尔摩,两个服务器都使用本地时间。假设在源服务器上创建了一个表
mytable
,对该表执行了一个INSERT
语句,然后对该表进行了选择,示例如下所示: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小时,因此,如果在该瞬间在复制服务器上执行
SELECT NOW()
语句,返回的值为2009-09-01 18:00:00
。因此,如果在复制服务器上选择自从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
语句在二进制日志中的影响,并且在使用语句日志时是非确定性的。这不是问题,如果使用行日志。一个alternative是使用
--sysdate-is-now
选项,使SYSDATE()
变为NOW()
的别名。这必须在源服务器和复制服务器上执行,以工作正确。在这种情况下,这个函数仍然会发出警告,但可以安全地忽略,只要在源服务器和复制服务器上都使用--sysdate-is-now
。SYSDATE()
在使用MIXED
模式时自动复制使用行日志,并在STATEMENT
模式中生成警告。 -
以下限制仅适用于语句日志复制,不适用于行日志复制。
GET_LOCK()
、RELEASE_LOCK()
、IS_FREE_LOCK()
和IS_USED_LOCK()
函数处理用户级锁,可以在复制服务器上复制,但这些函数不应该用于插入源表,因为在复制服务器上将不同。例如,不要执行语句INSERT INTO mytable VALUES(GET_LOCK(...))
。这些函数在使用
MIXED
模式时自动复制使用行日志,并在STATEMENT
模式中生成警告。
为了解决前面的限制,当语句级别复制在生效时,您可以使用将问题函数结果保存在用户变量中,并在后续语句中引用该变量。例如,以下单行INSERT
由于对UUID()
函数的引用而存在问题:
INSERT INTO t VALUES(UUID());
要解决问题,可以这样做:
SET @my_uuid = UUID();
INSERT INTO t VALUES(@my_uuid);
该语句序列可以复制,因为@my_uuid
的值在将INSERT
语句之前作为用户变量事件存储在二进制日志中,并且在INSERT
语句中可用。
同样,对于多行插入,idea也适用,但使用起来更为繁琐。对于两个插入,可以这样做:
SET @my_uuid1 = UUID(); @my_uuid2 = UUID();
INSERT INTO t VALUES(@my_uuid1),(@my_uuid2);
然而,如果行数很大或未知,解决方案变得困难或不实用。例如,您不能将以下语句转换为每个行关联一个给定的用户变量:
INSERT INTO t2 SELECT UUID(), * FROM t1;
在存储函数中,RAND()
在函数执行过程中只被调用一次时可以正确复制。 (您可以考虑函数执行时间戳和随机数种子作为隐式输入,这些输入在源和副本中是相同的。)
函数FOUND_ROWS()
和ROW_COUNT()
函数在语句级别复制中不可靠。解决方案是将函数调用结果保存在用户变量中,然后在INSERT
语句中使用该变量。例如,如果您想将结果存储在名为mytable
的表中,您可能会这样做:
SELECT SQL_CALC_FOUND_ROWS FROM mytable LIMIT 1;
INSERT INTO mytable VALUES( FOUND_ROWS() );
然而,如果您正在复制mytable
,您应该使用SELECT ... INTO
,然后将变量存储在表中,如下所示:
SELECT SQL_CALC_FOUND_ROWS INTO @found_rows FROM mytable LIMIT 1;
INSERT INTO mytable VALUES(@found_rows);
这样,用户变量在上下文中被复制,并且在副本中正确应用。
这些函数在使用MIXED
模式时自动复制,并在STATEMENT
模式中生成警告。 (Bug #12092, Bug #30244)