谈谈 PhxSQL 的设计和实现哲学(上)
开源地址
摘要
微信 PhxSQL 是一个提供 Zookeeper 级别强一致和高可用的 MySQL 集群。PhxSQL 完全兼容 MySQL,建立在简单可逻辑证明的一致性模型之上,架构、部署、运维简单。文章还讨论了 PhxSQL 与相关技术方案的区别,并比较了各自的优缺点。
文章比较长,正文分成上下两章。上章主要讨论 why,即我们为什么要做 PhxSQL 以及为什么这样做。下章着重探讨 why not,即我们为什么不支持若干特性,例如多主多写和分库分表,及与 Galera 和 MySQL Group Replication 的比较等。
正文
PhxSQL[22]发布以来,受到很多关注。作为热爱技术的码农,我们感谢大家的关心和支持,欢迎一切基于技术出发点的讨论。“Show you the code”之后,我们在这里谈谈 PhxSQL 的设计和实现哲学,也同时回答大家提出的一些疑问。
1. PhxSQL 是什么?
PhxSQL 是一个通过 Paxos 保证强一致和高可用的的 MySQL 集群。PhxSQL 建立在 Paxos 的一致性和 MySQL 的 binlog 流水基础上。主要原理简单来说:
i) Paxos 选出主机
ii) 主机把本机 MySQL 设置成可写的 MySQL 主机,在 MySQL 写 binlog 流程中拦截 binlog 流水、发送到 Paxos,形成全局的 binlog 流水
iii) 备机把本机的 MySQL 设置成只读的 MySQL 备机,MySQL 备机从全局 binlog 中拉取流水,重放和执行,从而主备 MySQL 一致
iv) 针对常见的业务场景,PhxSQL 提供两个服务端口:强一致读写端口(ReadWritePort)和只读端口(ReadonlyPort);对数据要求强一致的业务,通过 ReadWritePort 来读写;只要求能读取但不要求最新数据的读请求(比如一些定时对账业务),可以通过 ReadonlyPort 来读取
只要有多于一半机器工作和互联,PhxSQL 就可以正常工作。
图 1:PhxSQL 架构
2. PhxSQL 的”强一致“和”高可用“是什么级别?
很多 MySQL 集群方案都宣称强一致和高可用。PhxSQL 这方面有什么不同?
大家熟知的 Zookeeper 提供强一致和高可用。一致性有很多级别,从强到弱分别是:Strict(严格一致性),Linearizable(线性一致性)[1],Sequential(序列一致性)[2],Causal(因果一致性),Eventual 等。具体定义请参见参考文献。严格一致性只是一个理论模型。根据相对论,由于信息传播的速度不可能高于光速,严格一致性在实际中几乎无法实现。线性一致性的理论定义很复杂,不太严谨直观地讲,就是任何一个客户端都可以读到别的客户端写入的最新内容。这也是大家通常理解的强一致。
Zookeeper 的强一致指“线性一致性”[3]。高可用是说只要多于一半机器工作和互联即可在保证线性一致性的质量下正常工作。PhxSQL 的强一致是指“线性一致性”,高可用是指只要多于一半机器工作和互联即可在保证线性一致性质量下工作。即,
PhxSQL 提供和 Zookeeper 相同的强一致性和高可用性!
PhxSQL 提供和 Zookeeper 相同的强一致性和高可用性!
PhxSQL 提供和 Zookeeper 相同的强一致性和高可用性!
重要事情说三遍:)。大家可以把 PhxSQL 当 Zookeeper 使用,例如用来选主!
在数据库事务隔离方面,PhxSQL 支持最高级别的 serializable。在性能方面,PhxSQL 提供明显优于 MySQL 半同步的写性能(在基准测试中提升 16%到 25%)和几乎相同的读性能[22]。
细心的读者可能注意到,和通常的“高可用、强一致”说法顺序相反,这个小节的标题中,“强一致”有意放在了“高可用”的前面。在这里需要澄清一个误区。当谈到高可用时,有时会有意无意忽略或者降低一致性。严格意义上来讲,可用性和一致性必须一块讨论,满足一致性要求前提下的可用性才有意义。打个不严谨的比方,一致性就像汽车的“安全性”,可用性就是汽车的“可用性”。如果一辆车号称“可用性”很好,可以连续工作 10 年,但从来不提“安全性”,那么这辆车的品质是值得怀疑的。
3. 为什么要做 PhxSQL?
虽然有很多 NoSQL、NewSQL 系统、以及多主多写 MySQL 集群、支持分库分表的 MySQL 集群,MySQL 传统主备同步方案仍然具备很多新系统难以企及的优点。MySQL 主备在主机上支持完整 SQL、全局事务、以 repeatable read 和 serializable 级别的事务隔离,在金融、帐号等关键业务中有巨大的价值。同时,现有复杂 MySQL 应用迁移到新的非兼容系统成本也很高。
但是 MySQL 传统主备方案也有其缺点。最明显的就是主机故障后的自动换主和新旧主数据一致性(以及衍生的换主后主备一致性、各个备机之间一致性,这里就不详加讨论了),即所谓的一致性和可用性。
为了解决这个问题,有传统流派:用 Zookeeper、etcd、或者其它第三方来检测心跳、选主、和切换。改造 MySQL client 让其能感知新的主机。或者为了能让传统 MySQL client 不加修改就能感知新的主机,部署 MySQL 代理服务器,让其将连到自身的 MySQL client 请求透明转发给新的主机。为了减少主备间数据的落后,从而降低旧主机故障、某台备机被提升成新主机时,新旧主机之间、新主机和其它备机之间的差异,很多方案在主备同步机制上做了很多有益的工作。例如 semi-sync 等待多数派备机应答,通过优化线程和网络、主备多通道、备机并行执行 binlog 流水等尽量减少主备之间差异。如果主备间任何时刻都完全一致,那么任何时刻换主都是强一致的。这句话的另外一个意思是,如果无法保证主备间任何时刻完全一致,那么当有持续不断的更新时,任何时刻的换主都是无法保证强一致的。
传统流派另外一个分支就是将 MySQL 本地磁盘换成更高可靠性的 SAN,当 MySQL 主机故障时,将 SAN 挂接到备机上提供服务。而当 SAN 故障时怎么办?当需要跨机房部署时怎么办?
意识到传统流派的局限性,新出了 Galera 和 MySQL Group Replication 等。除了宣称提供强一致性和高可用性外,还支持 master-master 多点写入等诱人新特性。
世界上目前已知的经过理论证明和实际检验的一致性算法屈指可数:两阶段提交 two-phase commit (2PC)、Paxos[4]、Raft[5]、Zookeeper Atomic Broadcast (ZAB)[3]、Viewstamped Replication[5]等。2PC 虽然可以保证一致性,但在主机故障时无法工作,存在可用性问题。目前被工业界广泛认可和应用的是 Paxos 和 Raft,2PC 一般在前两者帮助选主下使用。这里的一个小建议就是:如果一个分布式系统宣称支持线性一致性级别的强一致和高可用,请先检查它使用的一致性算法。如果是新算法,请检查它的形式化证明或者逻辑证明。
因此,为支持线性一致性和高可用,同时完全兼容 MySQL,我们在 MySQL 的基础上应用 Paxos,设计和开发了 PhxSQL。
4. PhxSQL 的设计原则是什么?
从实际需求出发,除了前述强一致、高可用、完全兼容 MySQL 这 3 个明显、必须的设计原则,我们还提出以下 3 原则。
4.1. 简单可逻辑证明的一致性模型
这可能是明显区别 PhxSQL 和其它方案的一个特点。一个经过逻辑证明的模型才是可靠的,一个建立在可靠模型基础上的系统也才是可信赖的。PhxSQL 一致性模型建立在两个前提上:
i) Paxos 保证一致性。这个大家都可以接受。
ii) 各个 MySQL 的 binlog 流水一致,则各个 MySQL 机器之间数据“一致”。这个假设小有争议。例如即使 binlog 流水一致,由于不同的 binlog 格式、备机重放流水的不同配置,也会导致主备之间、不同备机之间的数据产生不同级别的“不同”,例如一条和当前时间相关的 insert 操作。这种不一致有些应用可以容忍,有些则不能。在这里,PhxSQL 保证流水一致,而把格式和配置的自由留给应用和 DBA 根据具体场景确定。在最严格的配置下,binlog 一致,则数据一致。
在这两个前提下,PhxSQL 的一致性模型通过 Paxos,使得主机写入 Paxos 的 binlog 流水与备机从 Paxos 里拉取的 binlog 流水一致,从而保证 MySQL 数据的一致性。详细证明过程我们将另外提供。模型和证明过程都很简单,大家在读完源码后也可以尝试:)。
但即使模型正确,PhxSQL 正确实现了这个模型吗?用通俗的话讲,没有 bug。码农都知道这是个巨大的挑战。从一个算法和模型到正确的实现之间差距是巨大的。例如,正确实现 Paxos 挑战就很大[7]。为了尽量减少 bug,我们选择了简单和易理解同时应用广泛的 Paxos 作为一致性协议。为了实现一个“生产”级别的 Paxos,三位主要码农各自独立实现了 Paxos,在各自测试完正确性后,三套 Paxos 之间作为一组 Paxos 的独立节点互操作检验正确性,最后再集体实现一个发行版本 PhxPaxos[21]!除了单元测试、系统测试外,测试环境还随机高频率独立重启机器、对网络包进行乱序、延迟、重复等以对系统进行充分测试。
有读者可能问,Paxos 慢且网络延迟大,PhxSQL 为什么不实现一个“优化”版?Paxos 有各种版本,例如 Fast Paxos[19]、EPaxos[20]。但这些都是 Paxos,遵循 Paxos 的基本操作,所作的改变,都做了严格的形式化或者逻辑证明。PhxPaxos 严格遵照 Paxos 算法[4]实现,没有做任何改变或者“优化”。PhxPaxos 的正常写操作的网络延迟是一个网络 RTT,已经是任何算法理论上能达到的最快速度。
现在,对于 PhxSQL 来说,切换主机是一件很平常和容易的操作,就像往 MySQL 里插入一条数据一样平常和容易。
4.2. 最小侵入 MySQL 原则
MySQL 是个巨大的快速演进的生态系统,保证 PhxSQL 中的 MySQL 与官方 MySQL 的兼容性与可升级性是必须的。这使得 MySQL 应用可以快速迁移到和运行在 PhxSQL 上。这也使得 PhxSQL 可以迅速响应官方 MySQL 的升级,将 PhxSQL 中的旧版本 MySQL 升级到新的官方 MySQL,从而获得新的特性、性能、稳定性、和安全性提升。
这就要求 PhxSQL 中的 MySQL 对官方 MySQL 改动尽量少。实际上,PhxSQL 版 MySQL 只更改了三个小地方:修改了 binlog 插件接口中一个函数的参数;在 MySQL 启动时新增了一个插件函数用于检查 MySQL 本地的 binlog 文件,在 MySQL 协议中透传了真正的客户端 IP 以兼容授权功能(如果应用不需要可以不修改)。这几个改动是如此小,且涉及的是几乎稳定不变的流程,使得 PhxSQL 中 MySQL 跟随官方 MySQL 升级可以无缝完成。实际上,我们正和 MySQL 社区沟通,希望把这几处修改并入官方版本,从而使得 PhxSQL 以后可以完全使用官方版本 MySQL,也使得更多人可以方便采用 PhxSQL。
(a)
(b)
图 2:PhxSQL 对 MySQL 的关键修改。(a)是 MySQL。(b)是 PhxSQL 中的 MySQL。PhxSQL 修改了 after_flush 这个函数的参数,新增了在 MySQL 启动时调用的 before_recovery 这个函数。
也正是因为这个原因,我们没有因为可以减少模块个数而把 PhxSQLProxy、PhxBinlogSvr 放到 MySQL 进程内。为了保证和应用最广泛的 MySQL 单机版兼容,也没有在事务层和存储层介入或修改。
4.3. 简单的架构、部署、和运维
在满足要求的前提下,简单的架构有很多好处,例如开发、维护、诊断、维护、可靠性等等都变得容易。PhxSQL 只有 3+1 个模块。PhxSQL 中的 MySQL 是必须的。PhxBinlogSvr 负责全局 binlog 存储和同步、选主、集群成员管理等关键功能。在很多 MySQL 主备集群方案中,使用 Zookeeper、etcd、心跳检测、Agent 等承担选主的功能。PhxSQLProxy 则作为传统 MySQL client 访问 PhxSQL 中 MySQL 服务的代理,使得 PhxSQL 的换主操作对于传统 MySQL client 透明。在其它集群方案中,一般也是通过代理 MySQL proxy、或者虚拟网关 VIP,向传统 MySQL client 屏蔽集群的换主操作,提供透明访问。可选模块是 PhxSQL client lib,它修改了 MySQL client 库中的连接初始化函数,允许传入一个集群的 PhxSQLProxy 列表,从而在一个 PhxSQLProxy 没有响应时、自动访问其它可用 PhxSQLProxy,进一步提高可用性。开发人员可以直接链接 PhxSQL client lib。
PhxSQL 的部署和运维都很简单。在部署时,只要在目标机各自装好 PhxSQL,在配置中指定集群的机器的 IP 列表,PhxSQL 即可运行。换主这个操作已经从运维层面转移到 PhxSQL 正常的工作流程。通常 MySQL 集群中换主后可能需要人肉检查数据一致性、人肉“闪回”(这可能违反一致性保证,导致“幻读”);无法”闪回“导致必须清除某台 MySQL,重新拉取数据备份,和追流水等。这些耗时、繁重、易错的操作在 PhxSQL 中已经完全不需要。
至于热升级和热变更集群成员更简单。PhxSQL 支持 rolling-update,可以逐步升级每台机器。变更集群成员是指往集群添加机器、撤出机器、和替换机器(原子操作)。在保证强一致和高可用前提下,热变更(不停服变更)是非常困难的。PhxSQL 通过在 Paxos 中实现成员变更解决了这个难题。PhxSQL 提供了一个变更操作命令。当在新机器安装和启动 PhxSQL 后(要求 MySQL 已经加载一份较新的数据备份),可以在现有集群中一键将新机器引入、剔除一台旧机器、或者同时做两者。这三种操作都是原子操作!新机器会自动追流水。想想看,相当于 Zookeeper 支持热变更成员,是不是很令人激动的特性?
PhxSQL 由于在同步层使用 Paxos,天然支持多数据中心、多机房部署,两地三中心这种部署更是不在话下。对于 PhxSQL 来说,多数据中心和多机房部署与机房内部署没有区别。PhxSQL 的性能取决于多数派机器之间的网络延迟。
5. 为什么要开源?
作为热爱技术的码农,我们相信开源的技术可以使得这个世界更美好。从我们日常开发使用的 Emacs/Vim、GCC、GDB,间接为大众提供社交、电子商务、信息服务的 Linux、Apache、MySQL、PHP,到大众每日使用来沟通和娱乐的 Android 等,开源是整个互联网的基石,为全世界提供许多关键不可或缺的基础服务。我们充分享受了开源带来的技术进步、经济发展、和社会前进,我们也希望开源的 PhxSQL 可以回馈社区,帮助更多有需要的人。
另外,我们希望通过开源更好地改进 PhxSQL。我们欢迎技术性讨论和志愿者提交修改。我们承诺开源的 PhxSQL 会一直更新。除了一些和内部运维支撑系统进行集成的功能(PhxSQL 把这些功能抽象成插件,我们针对内部运维支撑系统实现了这些插件),开源版和内部版本将保持一致。
6. PhxSQL 的局限性
在一个不完美的世界里,完美是不存在的。我们很坦诚指出 PhxSQL 存在的两个局限:
6.1. MySQL 主机在执行 SQL DDL 命令(例如建库和建表命令)时可能存在一致性风险。
由于 MySQL 的 innodb 引擎不支持 DDL 回滚,如果主机在 innodb 已经 commit 这条 DDL 命令,但是这条命令的 binlog 还没到达 PhxSQL 的拦截点前宕机,则这条 DDL binlog 会在全局 binlog 中缺失,从而备机也不会收到这条 binlog。而为了保证线性一致性、serializable 级别事务隔离、及“最小侵入 MySQL”原则,我们也不想修改 MySQL 源码,提前截获 DDL 命令。考虑到 DDL 命令频度较低,我们后续准备在 PhxSQLProxy 加入检查和后续审计告警。也欢迎大家提出更好方案。
**6.2. 在写入请求量很大的系统中,MySQL 备机流水可能落后较多;如果这个时候主机死机,备机暂时无法提升成新主机,造成系统在一段时间内不可写。
**
为了保证线性一致性,对于要求读取最新数据的请求(通过 ReadWritePort 发起的读请求)也将失败;需要等至少一台备机追完流水,被提升为主机才能响应读取最新数据的请求。对于不需要读取最新数据的请求(通过 ReadonlyPort 发起的请求),可以从任意备机执行,但不保证线性一致性。(注意:PhxSQL 保证无论 MySQL 主机流水领先 MySQL 备机多少,MySQL 主机 binlog 流水和全局 binlog 流水是一致的,不会导致数据丢失和破坏线性一致性。)
MySQL 备机追流水落后是基于 binlog 复制这种模式的一个潜在问题。事实上,不仅 MySQL 主备,任何一个多副本系统,只要每个写操作不等待所有副本返回,都会出现类似的有些副本落后的问题;而那些等待所有副本返回的模式,在耗时和可用性方面又存在问题。可喜的是 MySQL 5.7 版本实现了并行复制机制,显著地提高了备机追流水的性能。PhxSQL 将很快支持 MySQL 5.7,对于写入请求量很大的场景也可以很大程度上避免备机追流水落后的情况。
谈完 why,敬请期待《PhxSQL 设计和实现哲学》的下章:why not,即我们为什么不支持若干特性,例如多主多写和分库分表,及与 Galera 和 MySQL Group Replication 的比较等。
参考
M.P. Herlihy and J. M. Wing. Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems (TOPLAS), Volume 12 Issue 3, July 1990, Pages 463-492.
L. Lamport. How to make a multiprocessor computer that correctly executes multiprocess programs. IEEE Trans. Computer. C-28,9 (Sept. 1979), 690-691.
P. Hunt, M. Konar, F. P. Junqueira, and B. Reed. ZooKeeper: wait-free coordination for Internet-scale systems. USENIXATC'10, 2010.
L. Lamport. Paxos Made Simple. ACM SIGACT News (Distributed Computing Column) 32, 4 (Whole Number 121, December 2001) 51-58.
D. Ongaro and J. Ousterhout. In search of an understandable consensus algorithm. USENIX ATC ’14, 2014.
B. M. Oki and B.H. Liskov. Viewstamped replication: a New primary copy method to support highly-available distributed systems. PODC’88, 8-17, 1988.
T. Chandra, R. Griesemer, and J. Redstone. Paxos made live – an engineering perspective. PODC’07, 2007.
J. C. Corbett, J. Dean, M. Epstein, and etc. Spanner: Google's Globally-Distributed Database. OSDI'12, 2012.
F. Pedone, R. Guerraoui, and A. Schiper. The database state machine approach. Journal of Distributed and Parallel Databases and Technology, 14:71–98, 2002
V. Zuikeviciute and F. Pedone. Revisiting the database state machine approach. VLDB Workshop on Design, Implementation, and Deployment of Database Replication. 2005.
Certification-based Replication. Visited at 2016/9/5.
Snapshot isolation. Visited at 2016/9/5.
Y. Amir, L. E. Moser, P. M. Melliar-smith, D. A. Agarwal, and P. Ciarfella. The totem single ring ordering and membership protocol. ACM Transactions on Computer Systems. 13 (4): 311–342.
http://downloads.mysql.com/presentations/innovation-day-2016/Session_7_MySQL_Group_Replication_for_High_Availability.pdf. Visited at 2016/9/5.
Replication API. Visited at 2016/9/5.
Corosync by corosync. Visited at 2016/9/5.
http://www.spread.org/. Visited at 2016/9/5.
Isolation Levels. Visited at 2016/9/5
L. Lamport. Fast Paxos. Technical Report, MSR-TR-2005-112.
I. Moraru, D. G. Andersen, and M. Kaminsky. There is more consensus in egalitarian parliaments. SOSP’13, 2013.
OpenIMgithub 开源地址:
https://github.com/OpenIMSDK/Open-IM-Server
OpenIM 官网 : https://www.rentsoft.cn
OpenIM 官方论坛: https://forum.rentsoft.cn/
更多技术文章:
开源 OpenIM:高性能、可伸缩、易扩展的即时通讯架构https://forum.rentsoft.cn/thread/3
【OpenIM 原创】简单轻松入门 一文讲解 WebRTC 实现 1 对 1 音视频通信原理https://forum.rentsoft.cn/thread/4
评论