注意:我确实找到了答案,并将其放在首位。答案下方是其他一些咆哮(我最初的答案),它们仍然可以解释这一点。
由于您的查询将行数加倍,因此您的语句INSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) FROM t1;
可以在服务器 1 和服务器 2 上插入不同数量的行。所有使用自动增量列的语句都将其 INSERT_ID 与复制一起发送,并且该值在服务器 2 上不会为真如果 a 语句也已在那里运行。
让我们看一个例子。我会stop slave
模拟一个长时间运行的查询或一个坏的网络。
- 创建两个数据库并设置主主复制
- 创建表并插入初始行
- 在服务器 2 上停止复制
- 在服务器 1 上运行使行数加倍的语句。2 次就足够了,但我做了 3 次。
检查show binlog events
(警告,不要在旧数据库上执行此操作,这将需要很长时间)。这就是我所看到的。
查询 | 开始Intvar
| INSERT_ID=3
查询 | 使用test
;INSERT INTO t1(c1,d1) SELECT ...
查询 | 提交
查询 | 开始Intvar
| INSERT_ID=5
查询 | 使用test
;INSERT INTO t1(c1,d1) SELECT ...
查询 | 提交
查询 | 开始Intvar
| INSERT_ID=9
查询 | 使用test
;INSERT INTO t1(c1,d1) SELECT ... 查询 | 犯罪
请注意,每次我运行重复 INSERT_ID 都会相应地更改。在第二次插入时,它是 5,这意味着第一次插入插入了 1 行(请记住,增量为 2)。第三次插入 INSERT_ID 为 9 表示第二次插入插入了 2 行。这一切都说得通。让我们继续
在服务器 2 上进行一次复制,不要开始复制。正确执行 a select * from t1
now 会显示两行,id 为 1 和 2。
现在再次启动从站并运行SHOW SLAVE STATUS \G
. 它已停止,重复 id 为 5。再次从 t1 中选择所有值显示四行。第一个是最初的。第二个是我们在服务器 2 上所做的,最后一个 ID 为 3 和 5 的两个来自服务器 1 上的第一个语句,该语句仅添加了 1 行。
复制的下一部分是这个
查询 | 开始Intvar
| INSERT_ID=5
查询 | 使用test
;INSERT INTO t1(c1,d1) SELECT ...
查询 | 犯罪
发生这种情况时,在服务器 1 上的 INSERT_ID 为 5,这就是复制将要使用的内容,但是,在服务器 2 上,我们已经有了 id 5,因为我们在得到它之前将行复制了一次。所以复制中断。
底线是这样的。在进行主-主复制时,每个语句都需要以相同的方式影响数据库。除其他外,添加或删除相同数量的行。
也就是说,如果您需要执行此类操作,则可以轻松解决此特殊情况。
将 server_id 添加到数据并创建一个像这样的表
CREATE TABLE t1
(
id
int(11) NOT NULL AUTO_INCREMENT,
server_id
int(1) DEFAULT NULL,
c1
varchar(50) DEFAULT NULL,
d1
date DEFAULT '1970-01-01', PRIMARY KEY ( id
)) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 ;
准备两行,每个服务器 id 一行
INSERT INTO t1(server_id, c1,d1) SELECT 1, LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) ; INSERT INTO t1(server_id, c1,d1) SELECT 2, LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) ;
对于每个重复项,只需考虑在您的服务器上创建的行。
INSERT INTO t1(server_id, c1,d1) SELECT server_id, LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) FROM t1 where server_id = 1个;
以下是原始答案
首先,当您假设您将有两组 id 范围为 1、3、5、.. 和 2、4、6 时,首先您的错误是,如果 Auto_increment 始终是,则无论该语句在哪个服务器上运行该值最大(id)+1。因此,如果您在服务器 1 上执行两次插入,它将获得奇数值 1 和 3。如果您随后在服务器 2 上执行一次插入,它将获得偶数值 4(4 是满足auto_increment_offset +的下一个大于 3 的数字N × auto_increment_increment)。
您可以通过运行查看 Auto_increment 值show table status;
其次,您在第一次插入之后的每个插入都会使表中的行数翻倍,这很快就会使其成为一个非常慢的操作,如果这与每个查询都非常慢有关,我不会感到惊讶。
也就是说,这就是我测试它的方式(并得到了同样令人惊讶的结果)。
- 我用两台服务器和 master 创建了一个新的空设置
make_replication_sandbox --master_master mysql-5.5.17-osx10.6-x86_64.tar.gz
。他们都开始了,还有奴隶。它们会在您进行设置时自动配置。
- 然后我根据您的问题创建了表格并插入了第一行。Auto_increment 现在在两台服务器上都是 2,并且表中有一行
- 然后我
while (true) do ./n1 test -e "INSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) FROM t1;"; done;
同时在两台服务器上运行(另一台上是./n2)。
我有一个理论。
假设您在表中有 1000 行,并且您同时在两台服务器上启动了相同的复制。简而言之,您将在两台服务器上获得 4000 行,并且它们都是相同的。
但是发生的情况是,您复制每个数据库上的行,以便服务器 1 看到 2000 行和服务器 2000 行,但只有前 1000 行是相同的,另外 1000 行在两台服务器上生成不同。
然后复制开始。这是基于语句的复制,因此相同的语句运行意味着在两台服务器上,行再次复制到 4000,这是正确的计数,但仍然只有 1000 是相同的,其他 3000 会不同。
只要每台服务器运行相同数量的查询,这可能会起作用(没有重复,但数据不同)但是如果一台服务器设法在复制缓存之前运行两个查询,那么您会在复制中获得一条语句,在服务器 2 上添加了 1000 行(如果之前有 1000 行)但在服务器 1 上添加了 4000 行(因为服务器 1 已经将 1000 翻了两次)。如果下一条语句在服务器 2 上添加了另外 2000 行,并且二进制日志包含“服务器上使用的第一个自动增量”之类的内容,那么您将遇到冲突。
我知道这是抽象而奇怪的,而且比思考它更难写出来:)
我希望这有帮助,我希望这就是问题所在……师父很难,这绝对是我在师父中不会做的事情之一。