麒麟v10 上部署 TiDB v5.1.2 生产环境优化实践
759
2023-04-28
谈谈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流水基础上。主要原理简单来说:
Paxos选出主机主机把本机MySQL设置成可写的MySQL主机,在MySQL写binlog流程中拦截binlog流水、发送到Paxos,形成全局的binlog流水备机把本机的MySQL设置成只读的MySQL备机,MySQL备机从全局binlog中拉取流水,重放和执行,从而主备MySQL一致针对常见的业务场景,PhxSQL提供两个服务端口:强一致读写端口(ReadWritePort)和只读端口(ReadonlyPort);对数据要求强一致的业务,通过ReadWritePort来读写;只要求能读取但不要求最新数据的读请求(比如一些定时对账业务),可以通过ReadonlyPort来读取
只要有多于一半机器工作和互联,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半同步的写性能和几乎相同的读性能[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多点写入等诱人新特性。
因此,为支持线性一致性和高可用,同时完全兼容MySQL,我们在MySQL的基础上应用Paxos,设计和开发了PhxSQL。
4. PhxSQL的设计原则是什么?
从实际需求出发,除了前述强一致、高可用、完全兼容MySQL这3个明显、必须的设计原则,我们还提出以下3原则。
4.1. 简单可逻辑证明的一致性模型
这可能是明显区别PhxSQL和其它方案的一个特点。一个经过逻辑证明的模型才是可靠的,一个建立在可靠模型基础上的系统也才是可信赖的。PhxSQL一致性模型建立在两个前提上:
Paxos保证一致性。这个大家都可以接受。各个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命令(例如建库和建表命令)时可能存在一致性风险。
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的比较等。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。