PhxSQL 总结

前不久微信开源了它的 PhxSQL。最近一直在看相关的文档,并且经过对它编译、安装、部署和测试之后,对它有所了解。本文将对这段时间看过的资料做一个简要的总结,整理出 MySQL 目前半同步存在的缺陷,PhxSQL 解决的问题以及 PhxSQL 的工作机制。

MySQL

MySQL 是一种常用的数据库系统。在实际应用中,通常搭建一主一备 MySQL 服务来保证可用性。主备之间就涉及到数据的一致性问题了,MySQL 现在有异步复制和半同步复制两种方式来同步两台主机上的 binlog ,保障数据一致。

MySQL 半同步复制

MySQL 主备进行半同步复制的过程如图
MySQL 半同步复制过程

MySQL的半同步复制和Master切换都存在一些不足。数据复制存在回滚难题,Master切换存在多Master难题。[1]

MySQL 半同步复制
上图是根据MySQL背后的数据一致性分析整理出来的MySQL主从工作时的一些场景。

PhxSQL

PhxSQL 是为解决MySQL 半同步复制的不足,使 MySQL 集群在 Master 切换过程中保证数据的一致而开发的。

版本

PhxSQL 基于 Percona 5.6 开发。Percona 是 MySQL的一个分支,功能和实现与 MySQL 基本一致。[2]

架构

官方文档介绍 PhxSQL 有三个组件和一个插件组成的,它的架构图如下:

PhxSQL 架构

PhxSQL 是在 MySQL 基础上了增加了两个模块(Phxbinlogsvr、Phxsqlproxy)和一个MySQL插件(Phxsync)。它们的作用分别是:

  • Phxbinlogsvr负责处理MySQL的Binlog复制和Master管理
  • Phxsqlproxy负责透传Client请求到Master
  • Phxsync插件负责MySQL和Phxbinlogsvr的交互[2]

读写流程

Phxsqlproxy 负责透传 client 请求到 master 。


  • 写

client 发出写请求到集群后,Phxsqlproxy 将该请求透传到 master 的读写端口。

从读写端口,经过 phxsqlproxy 连接数据库,所有的请求都会被 phxsqlproxy 透传到 master 的 MySQL。所有的写的数据都将先在 master 的 MySQL 落地。
此时,如果位于 master 的 MySQL 服务挂掉,将出现什么状况?
经过测试发现:集群会重新选择 master ,然后 phxsqlproxy 会将所有的写请求转发到新的 master 的 MySQL 上。


  • 读

在介绍读操作之前,需要知道一个配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@Vm-template-100G:~/phxsql/etc# cat phxsqlproxy.conf
[Server]
MasterForkProcCnt = 1
MasterWorkerThread = 30
MasterIORoutineCnt = 100
SlaveForkProcCnt = 1
SlaveWorkerThread = 30
SlaveIORoutineCnt = 100
MasterEnableReadPort = 0
IP = 192.168.4.115
Port = 54321
LogLevel = 3
LogFilePath = /tmp/data//log
LogMaxSize = 1600

该配置文件是 master 所在主机的phxsqlproxy.conf。MasterEnableReadPort 配置项表示 master 只读端口是否可用。如果值为 1,则为可用;否则,不可用。
相应地,如果此值为 1,则集群收到客户端的读请求后,Phxsqlproxy 不会透传请求到其它主机;
如果此值为 0,且收到请求的机器为 master,Phxsqlproxy 将透传请求到非 master 主机;
如果此值为 0,且收到请求的机器非 master,Phxsqlproxy 不进行透传。

经过测试发现:如果一个读请求从只读端口经过 phxsqlproxy 连接到 master,且 phxsqlproxy 将该请求透传到其它备机上(假设为B);那么读取的数据将是从 B 主机的数据库获得的。
不久后,如果 B 主机的 MySQL 服务停掉了,那么下一个读请求将返回错误信息。phxsqlproxy 也不会将请求转发到其它可用的备机上。
这种情况,需要客户端自己去选择可以提供服务的主机。

1
2
3
4
5
mysql> select * from test_phxsql;
No connection. Trying to reconnect...
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
ERROR:
Can't connect to the server

proxy 其实只是负责请求转发到 master,而不是当作一个 vip 之类的路由器使用。

针对常见的业务场景,PhxSQL 提供两个服务端口:强一致读写端口(ReadWritePort)和只读端口(ReadonlyPort);对数据要求强一致的业务,通过ReadWritePort来读写;只要求能读取但不要求最新数据的读请求(比如一些定时对账业务),可以通过ReadonlyPort来读取。[3]

一致性机制

PhxSQL的设计是为了解决MySQL半同步复制的不足,使MySQL集群在Master切换过程中保证数据的一致。

而MySQL半同步复制的不足是:在 Master 进行切换的场景下,数据难以保证一致。
PhxSQL 强调它是“强一致,高可用”的,那么它是怎样那保证一致性的呢?
PhxSQL 通过 Paxos 协议进行复制,使 Phxbinlogsvr 形成一个可靠的日志存储。PhxSQL 可以看成是为 MySQL 增加了一个用 Paxos 实现的可靠 Binlog 存储,只要集群中多数派机器存活,就可以解决半同步复制的回滚问题。[3]

也就是说,PhxSQL 的数据一致性的保障来自于它能够保证多数派主机上的 binlog 是一致的。phxbinlogsvr 通过 paxos 协议进行同步 binlog,要等多数派的 ack 成功才进行 commit。

在写入请求量很大的系统中,MySQL备机流水可能落后较多;如果这个时候主机死机,备机暂时无法提升成新主机,造成系统在一段时间内不可写。

为了保证线性一致性,对于要求读取最新数据的请求(通过ReadWritePort发起的读请求)也将失败;需要等至少一台备机追完流水,被提升为主机才能响应读取最新数据的请求。对于不需要读取最新数据的请求(通过ReadonlyPort发起的请求),可以从任意备机执行,但不保证线性一致性。(注意:PhxSQL保证无论MySQL主机流水领先MySQL备机多少,MySQL主机binlog流水和全局binlog流水是一致的,不会导致数据丢失和破坏线性一致性。)

master 切换

集群中当前 master 不可用时将重新选举出新的 master 。当前 master 中的任一组件停止工作都将导致重新选举出新的 master。

问题

  1. PhxSQL 能否退化成 MySQL。
    PhxSQL 添加了两个组件和一个插件。组件都是以独立运行的进程来提供服务。假如 phxsqlproxy 或 binlogsvr 挂掉,phxssync 插件能够随时停止使用让 PhxSQL 变成 MySQL 来提供存储服务?

  2. phxsqlproxy 作用是透传客户端过来的请求。
    在业务中我们需要可用的存储服务。如果 phxsqlproxy 只是透传请求,那么业务层还需要可以判断集群中的主机是否在工作。如果 phxsqlproxy 能支持透传请求到可用的主机上,业务层就可以少去一些判断。

  3. paxos 协议在 PhxSQL 中哪些地方起到了作用。
    paxos 协议是一个确保一致性的协议。PhxSQL 提到的“强一致性”就有它的功劳。它是怎么在 PhxSQL 中发挥作用的?

本文有很多语句直接参考了 PhxSQL 的文档,为的是使表达更官方,更准确。如果有误,请多指教 :-)

参考

  1. MySQL背后的数据一致性分析
  2. 高可用、强一致的MySQL集群-微信PhxSQL
  3. 谈谈PhxSQL的设计和实现哲学(上)