在单个主服务器和从服务器之间使用MySQL 5.6和基于语句的复制,以下方案会创建复制偏差:
创建此表:
CREATE TABLE `ReplicationTest` (
`TestId` int(11) NOT NULL,
`Tokens` int(11) NOT NULL,
PRIMARY KEY (`TestId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
插入此数据:
Insert into ReplicationTest (TestId, Tokens) VALUES
(1, 0),
(2, 0),
(3, 0),
(4, 0);
创建此过程:
CREATE PROCEDURE `MyDatabase`.`ReplicationTestProc` (vTestId int, pSuccessful BIT, pAmount DECIMAL(19, 4))
BEGIN
START TRANSACTION;
Update MyDatabase.ReplicationTest Set Tokens = CASE WHEN pSuccessful THEN Tokens - (pAmount * 100) ELSE Tokens END
where TestId = vTestId;
COMMIT;
END
在一次执行中运行这4个语句
call `MyDatabase`.`ReplicationTestProc`(1, 1, 1);
call `MyDatabase`.`ReplicationTestProc`(2, 1, 1);
call `MyDatabase`.`ReplicationTestProc`(3, 1, 1);
call `MyDatabase`.`ReplicationTestProc`(4, 0, 1);
现在,您将在主服务器和复制之间的ReplicationTest表中具有不同的值。看起来pSuccessful变量被视为全局变量,最后一个值设置为复制服务器上使用的值。
答案 0 :(得分:4)
通过观察实际记录在二进制日志中的语句,您可以了解发生了什么。使用以下命令查看binlog:
$ mysqlbinlog <datadir>/mysql-bin.000001
(当然,包含这些语句的文件名在您的系统上可能有所不同。)
被调用过程中的语句被记录到二进制日志中,但稍微修改了基于一个过程变量的常量值。这在http://dev.mysql.com/doc/refman/5.6/en/stored-programs-logging.html:
中有解释要记录的语句可能包含对本地过程变量的引用。这些变量不存在于存储过程上下文之外,因此无法从字面上记录引用此类变量的语句。相反,每个对局部变量的引用都会被此构造替换以用于记录目的。
以下是我们在测试的binlog中看到的内容。对局部变量的引用已替换为BIT
值的字符串表示:
. . .
Update test.ReplicationTest Set Tokens = CASE
WHEN NAME_CONST('pSuccessful',_binary'' COLLATE 'binary')
THEN Tokens - ( NAME_CONST('pAmount',1.0000) * 100) ELSE Tokens END
where TestId = NAME_CONST('vTestId',1)
. . .
Update test.ReplicationTest Set Tokens = CASE
WHEN NAME_CONST('pSuccessful',_binary'' COLLATE 'binary')
THEN Tokens - ( NAME_CONST('pAmount',1.0000) * 100) ELSE Tokens END
where TestId = NAME_CONST('vTestId',2)
. . .
Update test.ReplicationTest Set Tokens = CASE
WHEN NAME_CONST('pSuccessful',_binary'' COLLATE 'binary')
THEN Tokens - ( NAME_CONST('pAmount',1.0000) * 100) ELSE Tokens END
where TestId = NAME_CONST('vTestId',3)
. . .
Update test.ReplicationTest Set Tokens = CASE
WHEN NAME_CONST('pSuccessful',_binary'\0' COLLATE 'binary')
THEN Tokens - ( NAME_CONST('pAmount',1.0000) * 100) ELSE Tokens END
where TestId = NAME_CONST('vTestId',4)
. . .
请注意BIT
值1如何替换为字符串文字_binary''
。
当你的表达式求值为true / false时,空字符串等于false,这就是语句不更新slave上的列的原因:
mysql> select false = _binary'';
+-------------------+
| false = _binary'' |
+-------------------+
| 1 |
+-------------------+
为什么将BIT
值1替换为空字符串? BIT
值本质上是一种字符串,就像使用BINARY
数据类型一样。下面是一个BIT
值的示例,该值对应于'Z'的ASCII代码,表示为'Z':
mysql> select b'01011010';
+-------------+
| b'01011010' |
+-------------+
| Z |
+-------------+
BIT
如何处理b'1'
的价值?它就像一个ASCII值0000001,它打印为一个不可打印的零长度字符串:
mysql> select b'1';
+------+
| b'1' |
+------+
| |
+------+
您可以通过在整数上下文中引用它来强制将此值转换为整数:
mysql> select b'1'+0;
+--------+
| b'1'+0 |
+--------+
| 1 |
+--------+
但即使我们要改变你的存储过程来使用这个技巧,也为时已晚。该值已在二进制日志中转换为空字符串_binary''
,并且该字符串在整数上下文中不能转换为除0之外的任何值。
mysql> select _binary''+0;
+-------------+
| _binary''+0 |
+-------------+
| 0 |
+-------------+
为什么BIT
值在二进制日志中不像真正的b'1'
字面值那样转换为BIT
?这似乎是故意的。存储过程的二进制日志代码中甚至有注释(文件sql / sp.c,函数sp_get_item_value()
)。
/* Bit type is handled as binary string */
我建议您忘记在MySQL中使用BIT
作为布尔值。 MySQL BIT
数据类型的意外行为有太多情况。有关详情,请参阅Why you should not use BIT columns in MySQL。
相反,使用BOOLEAN
或TINYINT
(BOOLEAN
或BOOL
实际上只是MySQL中TINYINT(1)
的别名。我测试了您的过程,参数更改为TINYINT
,并且按预期工作:
slave1 [localhost] {msandbox} (test) > select * from ReplicationTest;
+--------+--------+
| TestId | Tokens |
+--------+--------+
| 1 | -100 |
| 2 | -100 |
| 3 | -100 |
| 4 | 0 |
+--------+--------+