Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  ...  /  Replication and System Functions

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模式中生成警告。

    请参见第19.5.1.33节,“Replication and Time Zones”

  • 以下限制仅适用于语句日志复制,不适用于行日志复制。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)