文档首页
MySQL 9.0 参考手册
相关文档 下载此手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 参考手册  /  ...  /  复制和系统函数

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 节,“CURRENT_USER() 的复制”。)这同样适用于 VERSION()RAND()

  • 对于 NOW(),二进制日志包含时间戳。这意味着 源上调用此函数返回的值 将被复制到副本。为了避免在不同时区之间复制 MySQL 服务器时出现意外结果,请在源和副本上都设置时区。有关更多信息,请参见 第 19.5.1.34 节,“复制和时区”

    为了解释在不同时区之间复制时可能出现的问题,假设源位于纽约,副本位于斯德哥尔摩,并且两台服务器都使用本地时间。进一步假设,在源上,您创建了一个名为 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。因此,如果您在复制了上面显示的 CREATE TABLEINSERT 语句之后从副本的 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,就可以安全地忽略该警告。

    使用基于行的复制时,SYSDATE() 会使用 MIXED 模式自动复制,并在 STATEMENT 模式下生成警告。

    另请参见 第 19.5.1.34 节,“复制和时区”

  • 以下限制仅适用于基于语句的复制,不适用于基于行的复制。 处理用户级锁的 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 中使用。

相同的想法适用于多行插入,但使用起来更繁琐。对于两行插入,您可以执行以下操作

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 模式下生成警告。(错误 #12092、错误 #30244)