OneProxy :: 多点强一至性只读流量分离集群,全活避免中间层单点故障!

在前面已经实现了单个Proxy节点的强一制性读写分离,让大家无须顾虑单个OneProxy节点上只读流量的数据一致性问题。但在真实的生产环境中,还需要考虑多个OneProxy节点集群布署的问题,以避免单个节点故障的问题;同时也要考虑单个OneProxy节点的处理能力,是否要实现双活或多活结构,即应用可以通过负载均衡设备(F5/LVS等)连接到任何一个OneProxy结点来访问数据。这时就必须考虑多个节点之间的数据强一致性读问题,如下图所示:

问题的本质在于,如果写流量分摊在多个OneProxy结点,如何保证应用读到最新的数据版本?或者是理论上无法保证,需要向应用给出明确的使用规则?这里面涉及几个重要的问题:

  • 应用端会使用连接池,同一个连接池中前后两次获得的连接能否保证数据的强一致性?
  • 应用从连接池中获得连接,使用同一个连接时,能否保证当前连接内的数据强一致性?
  • 多活模式下多个OneProxy节点都会接受更新语句和事务,节点之间如何同步版本信息?
  • 即使在主备单活模式下,主备切换的瞬间如何保证应用查询的数据强一致性?
  • 多个OneProxy节点之间如何比较数据的一致性版本?时间或GTID?

如果不需要分担读流量,全部从主库查询,那一切都没有问题。为了解决集群的读一致性问题,就需要进行版本比较,为只读流量挑选合适的可读MySQL结点,或者直接选择Master进行查询。OneProxy针对上述问题做了针对性的改进和处理,包括以下几点:

  • 在单个OneProxy实例内,记录每个表的最后更新时间点(微秒精度)。
  • 同一个OneProxy的同一连接,可以保证能看到前面的更新操作或事务的结果。
  • 同一个OneProxy的不同连接,可以保证能看到其他会话的更新操作或事务的结果。
  • 每个节点遇到DML或事务后,会通过集群心跳,主动通知其他结点相关表有更新,不同OneProxy节点之间的不同连接,可以实现非常高的一致性读,在毫秒级后(集群心跳时间)可以看到其他OneProxy结点的更新操作或事务的结果。
  • 主动通知机制,保证了单活模式下,主备切换后的数据强一致性,内置VIP机制使得布署异常简单。
  • 为了兼容各种不同的MySQL版本,采用了基于时间点的版本控制。会在主结点写入时间信息,在备库查询时间信息,进行版本比较。即OneProxy可以在各种MySQL版本上搭建强一致性读集群。
  • 多个OneProxy节点可以分别标记自己的版本,或者由主节点进行全局版本处理。从备库查询版本的频率要比标记版本的频率快,以便让备库及时知道最新的版本号。
  • 全局版本控制时,不同节点之间考虑到了时间差异,不同节点之间会和主节点比较时间偏差,在版本比较时进行纠正。
  • OneProxy增加了多节点选主功能,最早启动的结点会被自动选为主节点,相对比较稳定(集群多个Proxy节点之间的网络质量比较关键)。

集群多个OneProxy节点之间的通信主要做以下三件事情:

  • 进行OneProxy选主,选主会影响全局版本控制,以及高可用VIP的切换。如果是要多活,则不需要配置VIP,以前端使用LB分流即可。
  • 进行表更新广播,每个OneProxy节点在收到DML或事务后,会通知其他节点相应的表有更新操作,以实现集群的读一致性。
  • 进行时间差计算,不同的OneProxy节点之间会有时间差异,以便全局版本控制时进行版本校正。

做了这么多的工作,还是需要对应用提出一点研发要求,在OneProxy多活的情况下,应用最好使用同一个连接池中的连接来处理不同事务之间的强一制性要求,采用如下代码逻辑:

get_connection();
start transaction;
update ..... where ...;
insert ..... where ...;
commit;
select ...... from ... where ...;
select ...... from ... where ...;
release_connection();

即应用应当使用Session一致性的编程习惯,以保证事务或更新后读操作的数据一致性,应用可以较长时间地持有到OneProxy的连接,不用急着归还到连接池(如果处在事务中,则还是需要尽快处理;如果不在事务中,请随便持有)。