MySql主从复制

主从集群

在MySql的生产环境中,由于单台MySql不能满足高可用性需求,一般通过主从复制(Master-Slave)方式同步数据,再通过读写分离(MySql-Proxy)来提升数据库并发负载能力。

MySql主从集群作用:

  • 提高数据库负载能力,主库执行写任务,备库用于查询

  • 提高系统写性能,可扩展性和高可用性

  • 数据库备份与容灾,备库在异地,主库不在了,备库可以立即接管,无需恢复时间

主从同步

binlog日志保存了MySql实力上数据修改的日志信息,包含全量MySql增删改查数据。

  • 用于主从复制,binlog作为操作记录从master发送到slave,slave从master接收到日志保存到relay log中

  • 用于数据备份,数据库备份文件生成后,binlog保存了数据库备份后详细信息,以便下一次备份从备份点开始

MySql主从复制需要三个线程:

  • master的binlog dump thread

  • slave的IO thread

  • slave的Sql thread

binlog dump thread:

主库有数据更新时,根据binlog格式,将更新的事件类型写入到主库binlog文件中,并创建log dump线程通知slave有数据更新。当slave的io线程请求日志内容时,将此时binlog名称和当前更新的位置传递给salve的io线程。

io thread:

该线程链接到master,向log dump线程请求一份指定binlog文件位置的副本,并将请求回来的binlog存在本地relay log中。

sql thread:

线程检测到relay log更新后,读取并在本地做redo 操作,将发生在master上的事件本地执行,保证主从数据同步。

过程如下:

过程解析:

  1. 主库写入数据并且生成binlog文件。该过程中MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。

  2. 在事件写入二进制日志完成后,master通知存储引擎提交事务。

  3. 从库服务器上的IO线程连接Master服务器,请求从执行binlog日志文件中的指定位置开始读取binlog至从库。

  4. 主库接收到从库的IO线程请求后,其上复制的IO线程会根据Slave的请求信息分批读取binlog文件然后返回给从库的IO线程。

  5. Slave服务器的IO线程获取到Master服务器上IO线程发送的日志内容、日志文件及位置点后,会将binlog日志内容依次写到Slave端自身的Relay Log(即中继日志)文件的最末端,并将新的binlog文件名和位置记录到master-info文件中,以便下一次读取master端新binlog日志时能告诉Master服务器从新binlog日志的指定文件及位置开始读取新的binlog日志内容。

  6. 从库服务器的SQL线程会实时监测到本地Relay Log中新增了日志内容,然后把RelayLog中的日志翻译成SQL并且按照顺序执行SQL来更新从库的数据。

  7. 从库在relay-log.info中记录当前应用中继日志的文件名和位置点以便下一次数据复制。

DRC中间件

很多DRC中间件,也就是跨数据中心或跨机房数据同步服务,多采用主从复制方式实现的。比如将server伪装成一个MySql Slave,通过MySql主从同步拉取协议,拉取到数据,实时获取数据库变更并通过消息方式发布出来,供各业务线订阅。

整个系统模拟了主从同步,内部增量消费流程如下:

  1. DRC Server伪装成MySql Slave节点,链接MySql服务器,通过replication协议获得binlog日志

  2. DRC Server进行解析,过滤,存储数据增量变化,同时内存中缓冲部分数据

  3. DRC Client链接到DRC Server端,进行数据增量消费

系统中包含了Relay,Bootstrap服务和客户端库,Bootstrap中包括了Producer和Server。通过数据库日志方式,将数据库变更实时,可靠的从数据库拉取出来,业务可以通过定制化client实时获取日志变更,并且通过内存分区提高吞吐,通过提高消费线程数量,提高消费能力。

主从延迟

通过主从复制原理,我们知道了Master和Slave之间一定时间内会存在数据不一致情况,也就是主从延迟。

  1. 主库A执行完一个事务,写入binlog,记录时刻T1

  2. 传递给从库B,从库接受这个binlog记录时刻T2

  3. 从库执行完这个事务,记录时刻T3

主从延迟就是同一个事务,从库执行完成的时间和主库执行完成的时间差,即:T3-T1.

我们可以通过从库show slave status,返回结果会显示seconds_behind_master,表示从库当前延迟了多少秒(不能做到毫秒的监控)。

second_behind_master计算方式:

  • 每一个事务的binlog都有一个时间字段,用于记录主库上写入的时间

  • 从库取出当前正在执行的事务的时间段,跟当前系统时间进行相减,得到seconds_behind_master

有的时候机器的时钟回拨会导致提示主从延迟,但是其实对业务无影响。

为什么会出现主从延迟?

正常情况下日志从主库给到从库时间很短,基本忽略。最直接影响的就是从库消费relaylog的时间,可能造成延迟,主要原因如下:

  1. 从库性能比主库差:如果把多个从库放在一台机器上,在高并发读场景下,导致从库机器上多个从库争抢资源,造成耗时延迟,大部分不会这样部署

  2. 从库压力大:正常读写分离,主库提供写能力,从库提供读能力,一般会将大量读放到从库上,导致从库耗费了大量cpu,进而影响同步速度,造成延迟,可以通过一主多从,分担读压力,也可以将binlog输出到外部,比如hdfs提供查询能力

  3. 大事务执行:一旦有大事务,主库必须等到数据完成之后才写入binlog。如果一个事务过后,大量binlog传输到从节点,会导致从节点解析binlog,做redo操作延迟增加,因此不要一次性用delete进行大量数据删除,尽量控制数量,分批进行

  4. 主库DDL(alter,drop,repair,cereate):从节点与主库的DDL同步是串型的,如果DDL操作在主库执行较长时间,那么从库同样需要消耗较长时间。如果从库有一个长时间执行的查询,这个查询会阻塞来自于主库的DDL,从库被锁,直到查询结束为止,进而导致从节点数据延迟。

  5. 锁冲突:锁冲突导致从库sql线程执行慢,比如使用了select ... for update,或者在MyISAM引擎的表锁等。

  6. 从库复制能力差:正常情况下,一会的延迟,之后从库会追上,但是如果从库执行效率低于主库,且主库持续压力,导致长时间主从延迟,很可能是从库复制能力问题。

再看下主从复制的流程,主要看下红色的箭头:

  1. 上面两个箭头分别表示的是客户端写入主库和sql_thread执行relaylog,若粗细表示并发度,可见主库明显高于从库。

  2. 从库上的执行,即sql_thread更新逻辑,在5.6版本之前,是只支持单线程,那么在主库并发高、TPS高时,就会出现较大的主从延迟。

因此,在随后演进的版本中,官方的 MySQL提出了不断改进的多线程复制方法,用于减少主从延迟

解决主从延迟

多线程复制

图中的coordinator是上图的sql_thread,但其功能已不再是更新数据,而是负责读取中转日志和分发事务,进行更新操作的是work线程,该线程数量由slave_parallel_workers控制。

coordinator作为重要的一环,那么其进行分发是具有一定的要求:

  1. 不能造成更新覆盖,要求更新同一行的两个事务须分配到同一个work

如:更新同一行的两个事务被分配给了两个work,由于各个work之间是独立执行,就有可能出现第二个事务比第一个事务先执行,结果两个事务在主库和从库的执行顺序不一致,导致主从不一致。

  1. 同一个事务不能被拆分,须分配到同一个work

如:同一个事务更新表1和表2的各一行,分配到两个work,最终执行结果一致,但如果在表1执行完成的瞬间,来一个查询请求,则就会看到事务执行到一半的结果,破坏了事务的隔离性。

按库并行

这是MySQL最先推出的并行复制策略,模型如下:

如图所示,每个 worker 线程对应一个 hash 表,用于保存当前正在这个worker的执行队列里的事务所涉及到的库。其中hash表里的key是数据库名,用于决定分发策略。该策略的优点是构建hash值快,只需要库名,同时对于binlog的格式没有要求。

但这个策略的效果,只有在主库上存在多个DB,且各个DB的压力均衡的情况下,这个策略效果好。因此,对于主库上的表都放在同一个DB或者不同DB的热点不同,则起不到多大效果。

redo log 组提交 (group commit) 优化

最先使用这个特性的是MariaDB,该特性如下:

  1. 能够同一组里提交的事务,定不会修改同一行;

  2. 主库上可以并行执行的事务,从库上也一定可以并行执行。

具体是如何实现:

  1. 在同一组里面一起提交的事务,会有一个相同的commit_id,下一组为commit_id+1,该commit_id会直接写道binlog中;

  2. 在从库使用时,相同commit_id的事务会被分发到多个worker并行执行,直到这一组相同的commit_id执行结束后,coordinator再取下一批。

该实现逻辑,就好比模拟了主库的并行模式,但仔细分析对比,其并没有真正做到模拟主库的并行模式,可以看下图:

从图中可以看出主库在事务提交完后下一组事务很快就会进入commit状态,而从库需要等到第一组事务完全执行完成后,第二组事务才能开始执行。这种模式下,大事务的劣势尤为明显,比如TRA2是个大事务,在从库执行时,其他两个事务均已完成,但TRA2未完成,那么需等待他完全执行完,下一组才能执行,导致这段时间内只有一个work线程运行,造成资源浪费。

后续MySQL也提供了相应的类似功能,由参数slave-parallel-type进行控制,当其配置为LOGICAL_CLOCK即运行类似于MariaDB的策略,但MySQL在其并行策略基础上进行了优化。

MariaDB的核心是”所有处于commit“状态的事务可以并行,而MySQL认为只要是”同时处于prepare状态,或处于prepare与commit状态之间“的事务,就可以在从库里并行执行。因为只要是到达了prepare阶段,表示事务已经通过了锁冲突的检测。

具体怎么提升并发度,这里就涉及到了binlog组提交的两个参数(可以去了解一下binlog提交的过程):

  1. binlog_group_commit_sync_delay ,表示延迟多少微秒调用fsync;

  2. binlog_group_commit_sync_no_delay_count, 表示累积多少次以后才调用fsync。

通过控制这两个参数,制造更多同时处于prepare的事务,也就是让主库提交慢点,从库执行快点,从而增加从库复制的并行度。

WRITESET的并行复制

MySQL5.7.22提出了一个基于WRITESET的并行复制,通过binlog-transation-dependency-tracking进行控制,有三种模式:

  • COMMIT_ORDER,即redo log 组提交 (group commit) 优化。

  • WRITESET,表示对于事务中关联到的每一行,计算出hash值,组成writeset。如果两个事务没有操作相同的行,即writeset没有交集,可以并行。hash值通过”库名+表名+索引名+值“计算,若表上还有其他唯一索引,那么对每一个唯一索引,在insert语句上会多一个hash值。

  • WRITESET_SESSION,在WRITESET上多一层限制,即在主库上同一个线程执行的两个事务,在从库执行时,也要保证相同的先后顺序。

该策略的优点是:

  • WRITESET在主库生成后写在binlog中,在从库执行时,不需要解析,节省工作量

  • 不需扫整个binlog来决定分发到哪个worker

  • 从库的分发策略不依赖于binlog内容,对于statement格式也适合

怎么减少主从延迟

主从同步问题永远都是一致性和性能的权衡,得看实际的应用场景,若想要减少主从延迟的时间,可以采取下面的办法:

  1. 降低多线程大事务并发的概率,优化业务逻辑

  2. 优化SQL,避免慢SQL,减少批量操作,建议写脚本以update-sleep这样的形式完成。

  3. 提高从库机器的配置,减少主库写binlog和从库读binlog的效率差。

  4. 尽量采用短的链路,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。

  5. 实时性要求的业务读强制走主库,从库只做灾备,备份。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值