geo*_*kis 3 mysql replication auto-increment master-slave database-replication
我正在使用主-主复制进行一些测试,并且遇到了一些奇怪的问题,我将尝试描述我遵循的过程,以便有人可以重现该问题。
我在 2 个虚拟机上和每个虚拟机的配置文件中设置了复制:
-- Master1 --
auto_increment_increment = 2
auto_increment_offset = 1
-- Master2 --
auto_increment_increment = 2
auto_increment_offset = 2
Run Code Online (Sandbox Code Playgroud)
这些设置应该导致自动增量列的算术级数:
- Master1: 1,3,5,7,9,11,13 ...
- Master2: 2,4,6,8,10,12,14 ...
Run Code Online (Sandbox Code Playgroud)
Master1 得到奇数,Master2 得到偶数。然后我创建一个测试数据库并添加一个具有以下定义的表:
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`c1` varchar(50) DEFAULT NULL,
`d1` date DEFAULT '1970-01-01',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
Run Code Online (Sandbox Code Playgroud)
当然,数据库是在两台服务器上创建的。之后,我执行
START SLAVE;
Run Code Online (Sandbox Code Playgroud)
在两台服务器上,以便开始复制。为了生成数据,我使用以下过程:
必须插入单个记录才能使进程起飞
INSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) ;
然后从同一个表中使用 INSERT - SELECT ,它将以 2 n的速率开始插入,n是您执行查询的次数:
INSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) from t1;
提示:我发现这里描述的这种方法对于为您的表生成随机数据也非常方便。
因此,当我开始在两台服务器上同时执行这些查询时,这总是会导致自动增量列的复制重复键错误。如果有人有任何想法,我将不胜感激!
PS:当然这种查询在生产应用中很少发生,但我相信它仍然证明了一点。
注意:我确实找到了答案,并将其放在首位。答案下面是一些其他的咆哮(我的初始答案),它们仍然可以解释这一点。
由于您的查询使行数加倍,您的语句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模拟一个长时间运行的查询或一个坏的网络。
检查show binlog events(警告,不要在旧数据库上执行此操作,这将花费很长时间)。这就是我所看到的。
查询 | BEGIN
Intvar | INSERT_ID=3
查询 | 使用test; INSERT INTO t1(c1,d1) SELECT ...
查询| 提交
查询 | BEGIN
Intvar | INSERT_ID=5
查询 | 使用test; INSERT INTO t1(c1,d1) SELECT ...
查询| 提交
查询 | BEGIN
Intvar | INSERT_ID=9
查询 | 使用test; INSERT INTO t1(c1,d1) SELECT ...查询| 犯罪
请注意,每次我运行重复的 INSERT_ID 都会相应地更改。在第二个插入时它是 5 意味着第一个插入插入了 1 行(请记住,增量是 2)。在第三个插入中 INSERT_ID 是 9 意味着第二个插入插入了 2 行。这都是有道理的。让我们继续
在服务器 2 上复制一次,不要开始复制。执行 a select * from t1now 会正确显示两行,id 为 1 和 2。
现在再次启动从站并运行SHOW SLAVE STATUS \G. 它已停止,重复 id 为 5。再次选择 t1 中的所有值显示四行。第一个是最初的。第二个是我们在服务器 2 上所做的,最后一次使用 id 3 和 5 来自服务器 1 上的第一个语句,该语句只添加了 1 行。
复制的下一部分是这样的
查询 | BEGIN
Intvar | INSERT_ID=5
查询 | 使用test; INSERT INTO t1(c1,d1) SELECT ...
查询| 犯罪
发生这种情况时,服务器 1 上的 INSERT_ID 为 5,这就是复制将要使用的内容,但是,在服务器 2 上,我们已经有了 id 5,因为我们在获得该行之前额外复制了一次行。所以复制中断。
底线是这个。在进行主-主复制时,每条语句都需要以相同的方式影响数据库。添加或删除相同数量的行等。
也就是说,如果您需要做这样的事情,对于这种特殊情况有一个简单的解决方法。
在数据中添加一个 server_id 并创建一个这样的表
创建表t1(
idint(11) NOT NULL AUTO_INCREMENT,
server_idint(1) DEFAULT NULL,
c1varchar(50) DEFAULT NULL,
d1date DEFAULT '1970-01-01', PRIMARY KEY ( id) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT1CHARSET ;
准备两行,每个服务器 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 是下一个大于 3 且满足auto_increment_offset + N × auto_increment_increment)。
您可以通过运行查看 Auto_increment 值 show table status;
其次,在第一个之后的每个插入都会使表中的行数增加一倍,这很快使它成为一个非常缓慢的操作,如果这与每个查询如此缓慢有关,我也不会感到惊讶。
也就是说,这就是我测试它的方式(并得到了同样令人惊讶的结果)。
make_replication_sandbox --master_master mysql-5.5.17-osx10.6-x86_64.tar.gz。他们都开始了,所以有奴隶。它们会在您进行设置时自动配置。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 行,并且二进制日志包含“服务器上使用的第一个自动增量”之类的内容,那么您将发生冲突。
我知道这是抽象和奇怪的,甚至比想象它更难写出来:)
我希望这会有所帮助,我希望这就是问题所在...... Master-master很难,这绝对是我在master-master中不会做的事情之一。
| 归档时间: |
|
| 查看次数: |
9041 次 |
| 最近记录: |