时间戳处理功能中断复制
Timestamp handling function breaks replication
我们需要一个永远不允许减少的增加的微秒时间戳,特别是如果时间被改变(例如 ntp)或重新启动等。我目前被迫使用 MariaDB 5.5.68(在 CentOS 上)。
基本上是https://en.wikipedia.org/wiki/Lamport_timestamp
的实现
目前是这样工作的:
Table创作:
CREATE TABLE `tblboxmicro` (
`microTime` BIGINT UNSIGNED NOT NULL -- the highest microtime used
) ENGINE=MYISAM DEFAULT CHARSET=utf8;
INSERT INTO `tblboxmicro` (`microTime`) VALUES (0);
我们创建这个函数是为了方便使用:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
UPDATE tblboxmicro SET microTime = GREATEST(round(@@SESSION.timestamp * 1000000, 0), microTime+1);
SELECT microTime into ret FROM tblboxmicro;
return ret;
end
//
delimiter ;
它是这样使用的(来自 PHP 的带有 PDO 的现实生活示例查询):
INSERT INTO tblboxusers
(microTime, roleNodeId,
userNodeId, boxId, roleId, email, name, notes, meta, cipher, accesscode)
VALUES (getLamportMicros(),0,?,?,?,?,?,?,?,?,?)
问题
现在我们要建立一个cross-master 复制 而这个函数总是会中断复制。它说
Slave SQL: Could not execute Update_rows event on table box.tblboxmicro; Can't find record in 'tblboxmicro', Error_code: 1032; handler error HA_ERR_END_OF_FILE;
有没有更聪明的方法可以在不破坏复制的情况下实现我们的目标?一定要快,当然...
编辑:我们使用 MIXED binlog 格式。
我们终于找到问题所在了。 Mariadb 无法正确复制@SESSION 时间戳。我们像这样更改了 getLamportMicros() 函数以使其工作:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
select GREATEST(CAST(1000000*UNIX_TIMESTAMP(current_timestamp(6)) AS UNSIGNED), microTime+1) into ret from tblboxmicro;
update tblboxmicro set microTime = GREATEST(ret, microTime);
return ret;
end
//
delimiter ;
这是我发现的:https://dev.mysql.com/doc/refman/5.7/en/replication-features-variables.html
In statement-based replication, session variables are not replicated
properly when used in statements that update tables. For example, the
following sequence of statements do not insert the same data on the
source and the replica:
SET max_join_size=1000; INSERT INTO mytable VALUES(@@max_join_size);
因此,通过将 @@SESSION.timestamp
替换为 UNIX_TIMESTAMP(current_timestamp(6))
函数,它现在似乎可以工作了。转换只是为了使其可用于我们的特定案例。
我们需要一个永远不允许减少的增加的微秒时间戳,特别是如果时间被改变(例如 ntp)或重新启动等。我目前被迫使用 MariaDB 5.5.68(在 CentOS 上)。
基本上是https://en.wikipedia.org/wiki/Lamport_timestamp
的实现目前是这样工作的:
Table创作:
CREATE TABLE `tblboxmicro` (
`microTime` BIGINT UNSIGNED NOT NULL -- the highest microtime used
) ENGINE=MYISAM DEFAULT CHARSET=utf8;
INSERT INTO `tblboxmicro` (`microTime`) VALUES (0);
我们创建这个函数是为了方便使用:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
UPDATE tblboxmicro SET microTime = GREATEST(round(@@SESSION.timestamp * 1000000, 0), microTime+1);
SELECT microTime into ret FROM tblboxmicro;
return ret;
end
//
delimiter ;
它是这样使用的(来自 PHP 的带有 PDO 的现实生活示例查询):
INSERT INTO tblboxusers
(microTime, roleNodeId,
userNodeId, boxId, roleId, email, name, notes, meta, cipher, accesscode)
VALUES (getLamportMicros(),0,?,?,?,?,?,?,?,?,?)
问题
现在我们要建立一个cross-master 复制 而这个函数总是会中断复制。它说
Slave SQL: Could not execute Update_rows event on table box.tblboxmicro; Can't find record in 'tblboxmicro', Error_code: 1032; handler error HA_ERR_END_OF_FILE;
有没有更聪明的方法可以在不破坏复制的情况下实现我们的目标?一定要快,当然...
编辑:我们使用 MIXED binlog 格式。
我们终于找到问题所在了。 Mariadb 无法正确复制@SESSION 时间戳。我们像这样更改了 getLamportMicros() 函数以使其工作:
delimiter //
create function getLamportMicros()
returns bigint
reads sql data
begin
declare ret bigint;
select GREATEST(CAST(1000000*UNIX_TIMESTAMP(current_timestamp(6)) AS UNSIGNED), microTime+1) into ret from tblboxmicro;
update tblboxmicro set microTime = GREATEST(ret, microTime);
return ret;
end
//
delimiter ;
这是我发现的:https://dev.mysql.com/doc/refman/5.7/en/replication-features-variables.html
In statement-based replication, session variables are not replicated properly when used in statements that update tables. For example, the following sequence of statements do not insert the same data on the source and the replica:
SET max_join_size=1000; INSERT INTO mytable VALUES(@@max_join_size);
因此,通过将 @@SESSION.timestamp
替换为 UNIX_TIMESTAMP(current_timestamp(6))
函数,它现在似乎可以工作了。转换只是为了使其可用于我们的特定案例。