Mysql主从复制方式以及可能出现的问题

一.MySQL主从复制原理的是啥?

大致流程:主库将变更写binlog日志,然后从库连接到主库之后,从库有一个IO线程,将主库的binlog日志拷贝到自己本地,写入一个中继日志 relay日志中。接着从库中有一个SQL线程会从中继日志读取binlog,然后执行binlog日志中的内容,也就是在自己本地再次执行一遍SQL,这样就可以保证自己跟主库的数据是一样的。

二.主从复制的问题

2.1 主从同步数据丢失

如果主库突然宕机,然后恰好数据还没同步到从库,那么有些数据可能在从库上是没有的,这时候从库成为了主库,那么有些数据可能就丢失了。

2.1.1主从同步数据丢失问题

开启半同步复制 semi-sync,用来解决主库数据丢失问题;

这个所谓半同步复制,semi-sync复制,指的就是主库写入binlog日志之后,就会将强制此时立即将数据同步到从库,从库将日志写入自己本地的relay log之后,接着会返回一个ack给主库,主库接收到至少一个从库的ack之后才会认为写操作完成了。如果过程出现失败,那么我们的客户端就可以进行重试了

为什么称之为半同步?因为在从库收到数据并且进入replay log里的时候我们就返回ack了,并没有一直到落库才返回ack,这个过程呢其实只进行了一半;至于为什么不进行全复制,直到从库数据也落盘之后才进行确定这就是效率问题了。

2.1.2 一个题外话,Mysql单体的丢失数据问题
2 Redo log
Redo log 称为重做日志,用于记录事务操作变化,记录的是数据被修改之后的值。

Redo log 由两部分组成:

内存中的重做日志缓冲(redo log buffer)
重做日志文件(redo log file)
每次数据更新会先更新 redo log buffer,然后根据 innodb_flush_log_at_trx_commit 来控制 redo log buffer 更新到 redo log file 的时机。innodb_flush_log_at_trx_commit 有三个值可选:

0:事务提交时,在事务提交时,每秒触发一次 redo log buffer 写磁盘操作,并调用操作系统 fsync 刷新 IO 缓存。

1:事务提交时,InnoDB 立即将缓存中的 redo 日志写到日志文件中,并调用操作系统 fsync 刷新 IO 缓存;

2:事务提交时,InnoDB 立即将缓存中的 redo 日志写到日志文件中,但不是马上调用 fsync 刷新 IO 缓存,而是每秒只做一次磁盘 IO 缓存刷新操作。

innodb_flush_log_at_trx_commit 参数的默认值是 1,也就是每个事务提交的时候都会从 log buffer 写更新记录到日志文件,而且会刷新磁盘缓存,这完全满足事务持久化的要求,是最安全的,但是这样会有比较大的性能损失。

将参数设置为 0 时,如果数据库崩溃,最后 1秒钟的 redo log 可能会由于未及时写入磁盘文件而丢失,这种方式尽管效率最高,但是最不安全。

将参数设置为 2 时,如果数据库崩溃,由于已经执行了重做日志写入磁盘的操作,只是没有做磁盘 IO 刷新操作,因此,只要不发生操作系统奔溃,数据就不会丢失,这种方式是对性能和安全的一种折中处理。

3 Binlog
二进制日志(binlog)记录了所有的 DDL(数据定义语句)和 DML(数据操纵语句),但是不包括 select 和 show 这类操作。Binlog 有以下几个作用:

恢复:数据恢复时可以使用二进制日志
复制:通过传输二进制日志到从库,然后进行恢复,以实现主从同步
审计:可以通过二进制日志进行审计数据的变更操作
可以通过参数 sync_binlog 来控制累积多少个事务后才将二进制日志 fsync 到磁盘。

sync_binlog=0,表示每次提交事务都只write,不fsync
sync_binlog=1,表示每次提交事务都会执行fsync
sync_binlog=N(N>1),表示每次提交事务都write,累积N个事务后才fsync
比如要加快写入数据的速度或者机器磁盘 IO 瓶颈时,可以将 sync_binlog 设置成大于 1 的值,但是如果设置为 N(N>1)时,如果数据库崩溃,可能会丢失最近 N 个事务的 binlog。

4 怎样确保数据库突然断电不丢数据?
`通过上面的讲解,只要 innodb_flush_log_at_trx_commit 和 sync_binlog 都为 1(通常称为:双一),就能确保 MySQL 机器断电重启后,数据不丢失。

因此建议在比较重要的库,比如涉及到钱的库,设置为双一,而你的测试环境或者正式业务不那么重要的库(比如日志库)可以将 innodb_flush_log_at_trx_commit 设置为0,sync_binlog 设置成大于100 的数值,提高更新效率。

2.2 主从延迟

主从延迟对于读写分离的涉及影响比较大
这里有一个非常重要的一点,就是从库同步主库数据的过程是串行化的,也就是说主库上并行的操作,在从库上会串行执行。所以这就是一个非常重要的点了,由于从库从主库拷贝日志以及串行执行SQL的特点,在高并发场景下,主库大量的写,那么从库的数据一个个的读,那么就会导致从库同步一定会比主库慢一些,是有延时的。所以经常出现,刚写入主库的数据可能是读不到的,要过几十毫秒,甚至几百毫秒才能读取到。(主库并发写的量级越高,从库积压的同步数据越多,延迟越高)

2.2.1主从延迟解决方案

我们可以用show status看看Seconds_Behind_Master参数,你可以看到从库复制主库的数据落后了几ms,但是这个也不是完全准确,可以看Seconds_Behind_Master的

对于解决主从延迟,解决方案可以从以下方面考虑

  • 1.在Mysql5.7之后可以用并行复制解决主从同步延时问题。
    所谓并行复制,指的是从库开启多个sql线程,并行读取relay log中不同库的日志,然后并行重放不同库的日志,这是库级别的并行,如果单库的并发很高的话也不行,但是打开肯定比不打开好。

  • 2.考虑分库,大多数情况主从延迟是因为主库的写并发太高,从库同步不过来,如果我们分库之后每个库压力就小多了,主从延迟可以低到1ms或者0.1ms这样忽略不计的程度。

  • 3.代码层面避免改后就查:最好不要有先DDL就立即要DQL到这条数据的这种操作(比如先插入一条数据,在查出来这条数据,再更新这条数据,这里就可能存在插入成功后,从库延迟,我们从从库查出来的id为null,然后更新时候再按id更新这条数据时候就是where id =null 就会出现问题)

  • 4.直连主库:如果必须有上述3的这种写完就读的操作,我们必须设置他这里查询的时候就要直连主库,从主库查数据。当然这里可能就会失去了读写分离的意义了。

你可能感兴趣的:(Mysql主从复制方式以及可能出现的问题)