主从复制
异步复制
- 在主库开启 binlog 的情况下,如果主库有增删改的语句,会记录到 binlog 中。
- 主库通过 IO 线程把 binlog 里面的内容传给从库的中继日志(relay log)中,主库给客户端返回 commit 成功(不管从库是否已经收到了事务的 binlog)
- 从库的 SQL 线程负责读取 relay log 并应用到从库数据库中。
半同步复制
- 在主库开启 binlog 的情况下,如果主库有增删改的语句,会记录到 binlog 中。
- 主库通过 IO 线程把 binlog 里面的内容传给从库的中继日志(relay log)中,从库收到 binlog 后,发送给主库一个 ACK,表示收到了,主库收到这个 ACK 以后,才能给客户端返回 commit 成功
- 从库的 SQL 线程负责读取 relay log 并应用到从库数据库中。
主从切换
可靠性优先策略
- 判断备库的seconds_behind_master,如果小于某个值(比如5秒)继续下一步,否则持续重试这一步;
- 把主库改成只读状态,把readonly设为true;
- 判断备库的seconds_behind_master的值,直到这个值变成0为止;
- 把备库改成可读写状态,把readonly 设为false;
- 把业务请求切到备库B。
如果一开始主备延迟就长达30分钟,而不先做判断直接切换的话,系统的不可用时间就会长达30分钟,这种情况一般业务都是不可接受的。
可用性优先策略
不等主备数据同步,直接把连接切到备库B,并且让备库B可以读写,那么系统几乎就没有不可用时间了。
可能出现不一致的数据。
设置binlog_format=row,会出现duplicate key error并停止;
使用mixed或者statement格式的binlog时,数据很可能悄悄地就不一致了
备库并行复制策略
按库并行
用于决定分发策略的hash表里,key就是数据库名。
如果你的主库上的表都放在同一个DB里面,这个策略就没有效果了;
或者如果不同DB的热点不同,比如一个是业务逻辑库,一个是系统配置库,那也起不到并行的效果。
MariaDB的并行复制策略
利用redo log组提交特性:
能够在同一组里提交的事务,一定不会修改同一行;
主库上可以并行执行的事务,备库上也一定是可以并行执行的。
实现方法
- 在一组里面一起提交的事务,有一个相同的commit_id,下一组就是commit_id+1;
- commit_id直接写到binlog里面;
- 传到备库应用的时候,相同commit_id的事务分发到多个worker执行;
- 这一组全部执行完成后,coordinator再去取下一批。
MySQL 5.7.22的新增并行复制策略
基于WRITESET的并行复制。
参数binlog-transaction-dependency-tracking
,用来控制是否启用这个新策略。这个参数的可选值有以下三种。
- COMMIT_ORDER,根据同时进入prepare和commit来判断是否可以并行的策略。
- WRITESET,表示的是对于事务涉及更新的每一行,计算出它的hash值,组成集合writeset。如果两个事务没有操作相同的行,也就是说它们的writeset没有交集,就可以并行。
- WRITESET_SESSION,是在WRITESET的基础上多了一个约束,即在主库上同一个线程先后执行的两个事务,在备库执行的时候,要保证相同的先后顺序。
主从延迟
可能原因
- 大表 DDL
- 大事务
- 主库 DML 并发大
- 从库配置差
判断主从延迟
1、判断 Seconds_Behind_Master 是否等于 0。
2、对比位点,Master_Log_File 跟 Relay_Master_Log_File 相等,并且 Read_Master_Log_Pos 跟 Exec_Master_Log_Pos 相等。
3、对比GTID,对比 Retrieved_Gtid_Set 和 Executed_Gtid_Set 是否相等。
主备切换
基于位点的主备切换
考虑到切换过程中不能丢数据,所以找位点时,找一个“稍微往前”的。再通过判断,跳过那些在从库上已经执行过的事务。
通常情况下,我们在切换任务的时候,要先主动跳过这些错误,有两种常用的方法。
- 主动跳过一个事务。
- 通过设置slave_skip_errors参数,直接设置跳过指定的错误。
CHANGE MASTER TO
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
MASTER_LOG_FILE=$master_log_name
MASTER_LOG_POS=$master_log_pos
基于GTID的主备切换
GTID的全称是Global Transaction Identifier,也就是全局事务ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。在GTID模式下,每个事务都会跟一个GTID一一对应。
CHANGE MASTER TO
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
master_auto_position=1
主备切换逻辑,实例A’的GTID集合记为set_a,实例B的GTID集合记为set_b:
- 实例B指定主库A’,基于主备协议建立连接。
- 实例B把set_b发给主库A’。
- 实例A’算出set_a与set_b的差集,也就是所有存在于set_a,但是不存在于set_b的GITD的集合,判断A’本地是否包含了这个差集需要的所有binlog事务。
a. 如果不包含,表示A’已经把实例B需要的binlog给删掉了,直接返回错误;
b. 如果确认全部包含,A’从自己的binlog文件里面,找出第一个不在set_b的事务,发给B; - 之后就从这个事务开始,往后读文件,按顺序取binlog发给B去执行。
过期读
问题:由于主从可能存在延迟,客户端执行完一个更新事务后马上发起查询,如果查询选择的是从库的话,就有可能读到刚刚的事务更新之前的状态。
强制走主库方案
- 对于必须要拿到最新结果的请求,强制将其发到主库上
- 对于可以读到旧数据的请求,才将其发到从库上。
sleep方案
大多数情况下主备延迟在1秒之内,主库更新后,读从库之前先sleep一下。具体的方案就是,类似于执行一条select sleep(1)
命令。
以卖家发布商品为例,商品发布后,用Ajax(Asynchronous JavaScript + XML,异步JavaScript和XML)直接把客户端输入的内容作为“新的商品”显示在页面上,而不是真正地去数据库做查询。这样,卖家就可以通过这个显示,来确认产品已经发布成功了。等到卖家再刷新页面,去查看商品的时候,其实已经过了一段时间,也就达到了sleep的目的,进而也就解决了过期读的问题。
缺点
- 如果这个查询请求本来0.5秒就可以在从库上拿到正确结果,也会等1秒;
- 如果延迟超过1秒,还是会出现过期读。
判断主备无延迟方案
第一种确保主备无延迟的方法是,每次从库执行查询请求前,先判断seconds_behind_master是否已经等于0。
第二种方法,对比位点确保主备无延迟(show slave status):
Master_Log_File和Read_Master_Log_Pos,表示的是读到的主库的最新位点;
Relay_Master_Log_File和Exec_Master_Log_Pos,表示的是备库执行的最新位点。
如果Master_Log_File和Relay_Master_Log_File、Read_Master_Log_Pos和Exec_Master_Log_Pos这两组值完全相同,就表示接收到的日志已经同步完成第三种方法,对比GTID集合确保主备无延迟(show slave status):
Auto_Position=1 ,表示这对主备关系使用了GTID协议。
Retrieved_Gtid_Set,是备库收到的所有日志的GTID集合;
Executed_Gtid_Set,是备库所有已经执行完成的GTID集合。缺点:仍可能过期读。备库收到的日志都执行完成了。还有一部分日志,处于客户端已经收到提交确认,而备库还没收到日志的状态。
配合semi-sync方案
引入半同步复制,配合前面关于位点的判断。
在一主多从场景中,如果是查询落到其他从库上,它们可能还没有收到最新的日志,就会产生过期读的问题。
在持续延迟的情况下,可能出现过度等待的问题。
等主库位点方案
- trx1事务更新完成后,马上执行
show master status
得到当前主库执行到的File和Position; - 选定一个从库执行查询语句;
- 在从库上执行
select master_pos_wait(File, Position, 1)
; - 如果返回值是>=0的正整数,则在这个从库执行查询语句;
- 否则,到主库执行查询语句。
等待超时后是否直接到主库查询,需要业务开发同学来做限流考虑。
等GTID方案
- trx1事务更新完成后,从返回包直接获取这个事务的GTID,记为gtid1;
- 选定一个从库执行查询语句;
- 在从库上执行
select wait_for_executed_gtid_set(gtid1, 1)
; - 如果返回值是0,则在这个从库执行查询语句;
- 否则,到主库执行查询语句。
等待超时后是否直接到主库查询,需要业务开发同学来做限流考虑。