图片

上图是一个读写分离的基本架构

读写分离的目的是什么,分摊主库的压力

常见的读写分离的实现是通过一个中间件,客户端只连接中间件,客户端连接proxy,由proxy根据请求类型和上下文来进行分发

图片

对于不同读写分离的架构的优缺点

1.客户端直连 因为不用转发,所以性能能较好,但是需要在主备切换/库迁移的时候,客户端进行手动调整,会比较麻烦,一般采用这种架构,都会有一个手动管理后端的组件,比如Zookeeper

2..带proxy的架构,对客户端比较友好,客户端不关心,但是性能并不高,现在来看,趋势是往着带proxy的架构方向去发展的

但是由于主备之间存在的一定的延迟,于是可能会出现如果主库上更新了一个事务,在从库上立刻查询可能查询不到

这就是最终一致性的问题

常用的解决方案有:

1.强制走主库的方案

2.sleep方案

3.判断主备延迟方案

4.配合semi-sync方案

5,主库位点方案

6.GTID方案

我们依次介绍方案

1.强制走主库的方案,就是对查询的请求进行分类,可以分为

对于必须要求最新结果的语句,强制发布到主库,对于没有要求的请求,可以发布到从库

2.sleep方案

其实就是对每一次请求,让其延迟查询一次,就是每次请求之前,都执行一次select selep(1),这个方案看起来对用户体验很不好,但其实并不是,比如商家发布商品,可以利用取巧的手段来进行实现,比如异步存库,可以默认其直接下发成功,在页面上直接显示成功了

3.判断主备无延迟

利用show slave status结果,其中提供的second_behind_master参数的值

可以采用每次查询判断second_behind_master来判断是否可以进行执行,如果不为0,就等这个参数为0再进行查询

4.利用位点比较

首先是一条命令

select master_pos_wait(file, pos[, timeout]);

逻辑如下

1.从从库执行

2.参数 file 和 pos 指的是主库上的文件名和位置;

3.timeout 可选,设置为正整数 N 表示这个函数最多等待 N 秒

这个命令正常返回的结果是一个正整数 M,表示从命令开始执行,到应用完 file 和 pos 表示的 binlog 位置,执行了多少事务。

返回的情况分为

1.如果执行期间,备库同步线程发生异常,则返回 NULL;

2.如果等待超过 N 秒,就返回 -1;

3.如果刚开始执行的时候,就发现已经执行过这个位置了,则返回 0

那么整体的流程就是

1.trx1 事务更新完成后,马上执行 show master status 得到当前主库执行到的 File 和 Position;

2.选定一个从库执行查询语句;

3.在从库上执行 select master_pos_wait(File, Position, 1);如果返回值是 >=0 的正整数,则在这个从库执行查询语句;否则,到主库执行查询语句。

说到底,还是可能去主库上进行查询的,压力都压在主库上了

5.GTID方案

如果数据库开启了GTID,那么可以等待GTID的方法\

类似的命令为

select wait_for_executed_gtid_set(gtid_set,1)

等待,直到这个库的事务包含了这个gtid_set,返回0

不然等待返回1

在前面的等待位点基础上,MySQL进行了优化

因为GTID会跟着事务的完成,在完成结果里面一并返回回去,这样,可以不用再去主库里面查询这个事务的position了

GTID在查询中返回的方式很简单,就是将session_track_gtids设置为OWN_GTID,通过API,从mysql_session_track_get_frist返回包中解析出GTID的值即可

于是整体流程为

1.trx1事务更新完成后,获取到这个GTID

2.选定一个从库执行查询语句

3.执行如上的语句

4.如果返回值是0.则在这个从库执行查询

5.不然,去主库执行查询

发表评论

邮箱地址不会被公开。 必填项已用*标注