复制流程
如上图所示,主库在进行数据改动时,将日志记录到bin-log
里,从库存在两一个线程,一个叫做IO 线程,一个叫做SQL 线程。IO 线程从主库的bin-log
里拉取日志,存入到从库的relay-log
,SQL 线程则从relay-log
中读取日志,进行回放、应用到从库,从而实现主从复制。
复制模式
异步复制
主库完成将日志记录到本地的bin-log
后,就返回成功给客户端,即不关心从库是否拉取日志完成,当然不更会关心从库上的数据是否更改完成。
半同步复制
主库不仅需要将日志记录到本地的bin-log
,而且还需要等待从库将日志拉取到relay-log
中后,才能返回成功给客户端。主库不需要关心从库上的数据是否更改完成,因为从库可能只是拉取、存入了relay-log
,但是SQL
线程还没有回放、执行。
在根据 主库等待从库返回ACk
在半同步复制流程中的顺序,还可以分为以下两种
-
AFTER_COMMIT
模式
先提交事务,后等待从库
ACK
返回 -
AFTER_SYNC
模式
先等待从库ACK
返回,后提交事务
bin-log
日志数据格式
1、STATEMENT
模式 (5.7.7以前的默认格式)
基于SQL
语句的复制,每一条会修改数据的SQL
语句会记录到bin-log
中,简称SBR
。
-
优点
不需要记录每一条
SQL
语句造成的数据变化,只需要记录SQL
语句即可 -
缺点
当
SQL
中存在内部函数、存储过程调用时,可能会存在主从双方数据不一致,比如调用函数last_insert_id()
2、ROW
模式 (5.7.7以后,包括5.7.7的默认格式)
基于行数据的复制,每一条被改动或者新增的数据都会记录到bin-log
中,简称RBR
。
-
优点
不会出现因为
SQL
中存在内部函数、存储过程的调用导致主从双方数据不一致的情况。 -
缺点
当进行表结构更改时,会产生大量日志,比如
alter table ...
3、MIXED
模式
STATEMENT
模式、ROW
模式混合使用。一般的复制使用STATEMENT
模式,对于STATEMENT
模式下下无法复制的操作采用ROW
模式保存,MySQL
会根据执行的SQL
语句选择适合的模式,简称MBR
。
如何解决主从模式下的读写一致性
在主从部署模式下的mysql
集群中,引入读写分离,即主库负责处理写请求,从库负责处理读请求,按照业务并发量,适当的添加从库节点,实现mysql
集群的高可用。但是读写分离也会带来一个问题,就是一致性问题,比如在一次请求中,先执行了写操作,后执行了读操作,写操作应用在了主库,读操作则路由到了从库,数据从主库同步到从库需要一定延迟,如何保证该情况下的读写一致性?
方案
方案一:主从采用半同步复制
将主从复制模式设置为半同步复制,从一定程度上可以降低主从同步的延迟,在SQL
线程忙碌时,也会存在一定的不一致性。
方案二:改造数据库中间件
所有的请求,不管是读请求,还是写请求,都是通过数据库中间件来执行完成的。可以通过改造数据库中间件,当进行写操作时,记录下写入数据的key
(这个key
不一定是表自增ID
、或者隐藏的row_id
,可以通过对写入数据的提取来生成,尤其适用于插入新数据场景),并为这个key
设置一个过期时间。
当后续有读请求过来时,判断该请求是否存在于上一步骤缓存的有效key
集合中,如果存在,则路由到主库进行读取,否则路由到从库读取。注意,这个缓存时间不是随意设置的,应大于主从同步延迟时间
方案三:缓存记录更新key
这种方案类似于方案二,只不过不同的是自己实现一个对写请求key
的缓存,这个缓存可以采用本地缓存实现,也可以采用分布式缓存实现,比如Redis
。实现原理等同于方案二,不再赘述。
该方案相比于方案二来说,不需要引入数据库中间价,成本较低。但是需要引入单独的key cache
模块,对于应用程序的渗入比较大。