前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大胆假设小心求证:MySQL双写+双向复制实战

大胆假设小心求证:MySQL双写+双向复制实战

原创
作者头像
DBA成江东
发布2023-07-20 19:25:01
1K1
发布2023-07-20 19:25:01
举报
文章被收录于专栏:数据库之巅数据库之巅

导语双主架构在MySQL中使用比较普遍,因为有故障后恢复方便的优点。但双写+双向复制的架构业界极少采用,这种架构下可能有什么问题?如何规避这种架构下的数据风险?本文根据实践经验做出了总结。

1. 双主结构

图片
图片

MySQL的复制大家应该都了解,而所谓双主结构,如上图所示,就是A和B这2台数据库主机互为主备,不论从哪一台写入数据,都会复制到另外一台。

采用双主结构的主要优点是当A(B)发生故障的时候,切换到B(A),后面A(B)恢复,一般情况下不需要找点change master恢复复制关系。

双主结构又分为2种,一种是Active-active,2台主机在同一时刻都有写入:

图片
图片

一种是Active-passive,某一时刻只有一边有写入,另一边只读:

图片
图片

实践中多采用Active-passive结构,主要原因是Active-active可能产生数据一致性问题。

2. 数据一致性问题

2.1 INSERT导致的不一致

Active-active架构下,因为是异步复制,如果更新同一条记录,会产生严重的数据一致性问题。

假设有以下表,ID为主键:

ID

NAME

BALANCE

1

张三

100

2

李四

60

如果在A插入记录

3

王二麻子

99

在记录还没有复制到B的时候,在B插入记录

3

王二麻子

50

则A和B之间的双向复制因为主键冲突都会中断。并且A和B的数据已经不一致!

如果ID是业务主键,则唯一性是由上层保证,一般有2种方法:

1.由发号器产生int或者bigint自增数字,保持全局唯一

2.根据一定的业务规则构成,比如交易凭证可以根据时间+商户号+产易类型+...

用发号器的风险是发号器成了业务的强依赖点,如果这个地方性能或者容灾有问题,会导致业务不可用。

2.2 自增主键冲突

如果没有业务主键,ID字段是自增字段,那么A,B同时写入几乎必然冲突,常规的解决方法是

代码语言:javascript
复制
A上设置:
auto_increment_offset = 1
auto_increment_increment = 2

B上设置:
auto_increment_offset = 2
auto_increment_increment = 2

 则A上产生的ID为1,3,5...,B上产生的ID为2,4,6...

但自增主键的问题是和业务无关,如果

在A插入记录

3

王二麻子

99

在记录还没有复制到B的时候,在B插入记录

4

王二麻子

50

则不会有复制冲突,但1个用户出现了2条记录,出现了严重的一致性问题!

所以自增主键在DB层面是无法防止重入的,不推荐使用!

2.3 update导致的不一致

1.假设张三用银行卡给余额充值100元:

UPDATE A SET BALANCE=BALANCE+100 WHERE ID=1;

如果A,B同时执行,则都会成功,对于基于行的复制,A,B上张三余额是200元,但对于基于语句的复制,A,B上张三余额都变为300元,如果张三用掉这300元,平台损失100元。

2.假设张三用余额支付100元,更新语句是:

UPDATE A SET BALANCE=BALANCE-100 WHERE ID=1;

如果是单独在A或者B上执行都没有问题,但如果在A,B上同时执行,则都会成功,商户收到2次支付成功的通知,给用户发货2次货值200元,但用户实际余额只有100元,平台损失了100元。

2.4 缺少锁的保护

双向复制导致数据不一致的原因是缺少了锁的保护,当并发写入/更新发生在同一个数据库主机上的INNODB表的时候,会有行锁(X锁)的保护,第1个事务会成功,其它事务等待,如果不发生锁等待超时,其它事务随后也会成功。

3. 数据不一致的预防方法

3.1 DB层面的预防

DB层面的预防最好的方法是将库表分开,A,B写入不同的库表。

可以垂直划分,比如A写入用户数据表,B写入商户数据表。

也可以水平划分,比如都是用户数据表,根据用户ID尾号hash为t0,t1,...t9,共10张表。

A写入t0-t4这5张表,B写入t5-t9这5张表,如下图所示:

为了进一步保护,A上的数据库应用账号只授与t0-t4的写入权限,B上的数据库应用账户只授与t5-t9的写入权限。

3.2 上层的防护

对于一套分布式事务系统来说,如下图所示,事务管理器负责分解子事务,然后调用不同的资源管理器,资源管理器和DB交互完成子事务,资源管理器和DB是1对1的关系。

从性能考虑,整条链路垂直化访问是最优的,但事务管理器这里做了兜底,对于t5-t9的访问如果发到IDC1的事务管理器上,仍然会路由到正确的资源管理器上,只不过这时调用变为了跨机房访问,所以从性能考虑还是要尽量避免!

资源管理器这里又做了一层防护,如果事务管理器将t5-t9的访问发送到了t0-t4对应的资源管理器,资源管理器会直接拒绝!

图片
图片

4.双写+双向复制的优点

前面谈了很多双写+双向复制的数据一致性风险及预防,有同学可能会问,这么多风险为什么还要采用这个方案?其实很简单,成本!

假设单台主机的写入性能是1万笔每秒,如果采用双写+双向复制架构,如下图所示,则6台DB可以支持2万笔每秒的写入(不考虑复制延迟),同时,当IDC1机房故障的时候,因为双向复制,B上有A的全部数据,可以顺利进行IDC切换。

图片
图片

如果不采用双向复制架构,则如下图所示,要具备IDC切换能力,需要12台服务器!服务器数量上升了1倍,事实上当时的情况是IDC中的机架位有限,根本不能支持此架构下的服务器数量。

图片
图片

5. 循环复制问题

我们知道MySQL的复制默认的机制是备机遇到和自己相同ServerID的, I/O 线程不会记录到relay log,也就不会执行。

如下图所示,建立1个4个节点的环性复制结构,当Paris的binlog依次传到London,Stockholm,MosSow,再传到Paris时,就会被过滤掉,不记录relay log,也就不会再继续往下传递。

但如果Moscow节点故障,从环形复制中剔除,而新的3个节点的环形复制中有Moscow节点发出的一个事件,因为3个节点都不能过滤掉该事件,该事件会在环中无限循环复制!

图片
图片

来源:《高可用MySQL》

我们以实际部署的情况来看,如下图所示,假设A1主机故障,A2接管,A2和B1重新建立双向复制关系,采用binlog往前推几个,冥等方式,则会导致循环复制问题!

注:冥等方式(SET GLOBAL SLAVE_EXEC_MODE = IDEMPOTENT):当遇到主键冲突的记录时,会先delete再insert,如果update或者delete找不到数据,会直接跳过,使用此方式所有表一定要有主键!

因为向前推几个binlog,则binlog一定包含serverid=A1的事务,在A2和B1构成的新的环形复制结构中,无论是A2还是B1都不能过滤掉serverid=A1的事务,则该事务会在A2和B1之间无限循环!

图片
图片

解决方法:

  1. 在恢复复制的时候,设置复制过滤,过滤掉serverid=A1的事务 CHANGE MASTER TO ... IGNORE_SERVER_IDS = (A1)
  2. 设置部分复制,只复制对方写入的数据 CHANGE REPLICATION FILTER REPLICATE_WILD_IGNORE_TABLE = ('db.t0', 'db.t1','db.t2','db.t3','db.t4');
  3. 升级到5.6以上版本,开启GTID

6. 其它问题

1, 授权时双向复制互相传递的问题:通过在同一个IDC针对所有主备采用不记binlog,分别独立授权的方式,避免此问题

2, 对于数据库修改单,必须保证和业务写入逻辑一致,在正确的主机上执行,否则有可能导致数据不一致

3, 监控必须保证事务,资源的配置,以及DB的授权没有问题,双写会导致资金损失,所以必须在最短的时候发现潜在的写入错误风险并报警

4, 无法使用pt-online-schema-change来改表结构,因为触发器在采用行复制的备机上不起作用

注:原因如下:

With statement-based replication, triggers executed on the master also execute on the slave. With row-based replication, triggers executed on the master do not execute on the slave. Instead, the row changes on the master resulting from trigger execution are replicated and applied on the slave.

This behavior is by design. If under row-based replication the slave applied the triggers as well as the row changes caused by them, the changes would in effect be applied twice on the slave, leading to different data on the master and the slave.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 双主结构
  • 2. 数据一致性问题
    • 2.1 INSERT导致的不一致
      • 2.2 自增主键冲突
        • 2.3 update导致的不一致
        • 2.4 缺少锁的保护
        • 3. 数据不一致的预防方法
          • 3.1 DB层面的预防
            • 3.2 上层的防护
            • 4.双写+双向复制的优点
            • 5. 循环复制问题
            • 6. 其它问题
            相关产品与服务
            云数据库 MySQL
            腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档