MySQL读写分离,写完读不到问题如何解决( 二 )


•seconds_behind_master,表示落后主节点秒数,如果此值为0,则表示主从无延迟•Master_Log_File 和 Read_Master_Log_Pos,表示的是读到的主库的最新位点,Relay_Master_Log_File 和 Exec_Master_Log_Pos,表示的是备库执行的最新位点 。如果这两组值相等,则表示主从无延迟•Auto_Position=1 ,表示使用了 GTID 协议,并且备库收到的所有日志的 GTID 集合 Retrieved_Gtid_Set 和 执行完成的 GTID 集合 Executed_Gtid_Set 相等,则表示主从无延迟 。
在进行读操作前,先根据上述方式来判断主从是否有延迟,如果有延迟,则一直等待到无延迟后执行 。但是这类方案在判断是否有延迟时存在着假阳和假阴的问题:
•判断无延迟,其他延迟了 。因为上述判断是基于从节点的状态,当主节点的 Dump Thread 尚未将最新状态发送给从节点的 IO SQL 时,从节点可能会错误的判断自己和主节点无延迟 。•判断有延迟,但是读操作读取的最新状态已经同步 。因为 MySQL 主从复制是一直在进行的,写后直接读的同时可能还有其他无关写操作,虽然主从有延迟,但是对于第一次写操作的同步已经完成,所以读操作已经可以读到最新的状态 。
对于第一个问题,需要使用主从复制的 semi-sync 模式,上文中讲解介绍的是默认的异步模式,semi-sync 模式的流程如下图所示:
•当主节点事务提交的时候,Dump Thread 把 binlog 发给从节点;•从节点的 IO Thread 收到 binlog 以后,发回给主节点一个 ack,表示收到了;•主节点的 Dump Thread 收到这个 ack 以后,再通知 Client Thread ,此时才能给客户端返回执行成功的响应 。
这样,写操作执行后,就确保从节点已经读取到主节点发送的 binglog 数据,即 Master_Log_File、 Read_Master_Log_Pos 或 Retrieved_Gtid_Set 是最新的,这样才能与执行的相关数据进行对比,判断是否有延迟 。
可惜的是,上述 semi-sync 模式只需要等待一个从节点的ACK,所以一主多从的模式该方案将会无效 。
虽然该方案有种种问题,但是对于一致性要求不那么高的场景也能适用,比如 MyCat 就是用 seconds_behind_master 是否落后主节点过多,如果超过一定阈值,就将其从有效从节点列表中删除,不再将读请求路由到它身上 。
在 MyCAT 的用于监听从节点状态,发送心跳的 MySQLDetector 类中,它会读取从节点的 seconds_behind_master,如果其值大于配置的 slaveThreshold,则将打印日志,并将延迟时间设置到心跳信息中 。

MySQL读写分离,写完读不到问题如何解决

文章插图
下面,我们就介绍能够解决第二个问题的方案,即判断有延迟,但是读操作读取的特定最新状态已经同步 。
 
等GTID 方案首先介绍一下 GTID,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识 。它由MySQL 实例的uuid和一个整数组成,该整数由该实例维护,初始值是 1,每次该实例提交事务后都会加一 。
MySQL 提供了一条基于 GTID 的命令,用于在从节点上执行,等待从库同步到了对应的 GTID(binlog文件中会包含 GTID),或者超时返回 。
MySQL读写分离,写完读不到问题如何解决

文章插图
MySQL 在执行完事务后,会将该事务的 GTID 会给客户端,然后客户端可以使用该命令去要执行读操作的从库中执行,等待该 GTID,等待成功后,再执行读操作;如果等待超时,则去主库执行读操作,或者再换一个从库执行上述流程 。
MariaDB 的 MaxScale 就是使用该方案,MaxScale 是 MariaDB 开发的一个数据库智能代理服务(也支持 MySQL),允许根据数据库 SQL 语句将请求转向目标一个到多个服务器,可设定各种复杂程度的转向规则 。
MySQL读写分离,写完读不到问题如何解决

文章插图
MaxScale 在其 readwritesplit.hh 头文件和 rwsplit_causal_reads.cc 文件中的 add_prefix_wait_gtid 函数中使用了上述方案 。
MySQL读写分离,写完读不到问题如何解决

文章插图
举个例子,原来要执行读操作的 SQL 和添加了前缀的 SQL 如下所示:
MySQL读写分离,写完读不到问题如何解决

文章插图
当 WAIT_FOR_EXECUTED_GTID_SET 执行失败后,原 SQL 就不会再执行,而是将该 SQL 去主节点执行 。
 
参考 https://time.geekbang.org/column/article/77636•https://www.cnblogs.com/rickiyang/p/13856388.html•https://www.cnblogs.com/paul8339/p/7615310.html•https://github.com/mariadb-corporation/MaxScale




推荐阅读