Documentation Home

16.4.1.15 复制和系统功能

某些功能在某些情况下不能很好地复制:

  • , (or ), USER(), , 和 函数被复制而没有变化,因此除非启用基于行的复制,否则不能在副本上可靠地工作。(请参阅 第 16.2.1 节,“复制格式”。) CURRENT_USER()CURRENT_USERUUID()VERSION()LOAD_FILE()

    USER()并 在使用模式CURRENT_USER()时使用基于行的复制自动复制MIXED,并在模式中生成警告STATEMENT。(另见 第 16.4.1.8 节,“CURRENT_USER() 的复制”VERSION() 。)对于 和也是如此RAND()

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

    为了解释在不同时区的服务器之间进行复制时的潜在问题,假设源位于纽约,副本位于斯德哥尔摩,并且两台服务器都使用本地时间。进一步假设,在源上,您创建一个表 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则会返回该值。出于这个原因,如果您在刚刚显示 的and 语句被复制mytable之后 从副本的副本中进行选择 ,您可能希望包含值 。然而,这种情况并非如此; 当您从 的副本副本中进行选择时 ,您会获得与源上完全相同的结果: CREATE TABLEINSERTmycol2009-09-01 18:00:00mytable

    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

    另见第 16.4.1.31 节,“复制和时区”

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

在 MySQL 5.7.3 之前, 如果在副本上启用了LAST_INSERT_ID()任何过滤选项(例如 --replicate-ignore-db和 ),则不会正确复制 的值。--replicate-do-table(错误#17234370,错误#69861)