写点什么

谈谈 PhxSQL 的设计和实现哲学(下)

用户头像
OpenIM
关注
发布于: 3 小时前

摘要


上一章讨论了我们为什么要做 PhxSQL 和为什么这样做 PhxSQL。这里我们主要谈谈为什么不做某些特性。舍得舍得,有舍才有得。CAP 告诉我们只能三选二,俗话告诉我们天下没有免费的午餐。每个特性除了自身提供的功能,也有其代价。为了保证强一致的线性一致性、高可用、serializable 级别事务隔离、完全兼容 MySQL、和最小侵入 MySQL,PhxSQL 放弃了一些特性。

正文

7. Why Not?


7.1. 为什么不支持多写?


多写想想就很诱人。多写可以充分利用每台机器写时需要的资源。例如某些写操作可能非常耗费 CPU,多写可以把写操作分散在各台机器上,充分利用各个机器的 CPU 资源,极大提高写入的性能。多写使得换主没有存在的必要,也就没有换主时可能存在的不可写时间窗问题。多写还使得客户端可以就近写入,减少跨数据中心写入带来的网络延迟。


多写有两种:大家熟知的分 shard 或者组,各 shard 或者组间并行写入,以 Google Spanner[8]为典型代表;在 shard 或者组内并行,以 Galera 和 MySQL Group Replication 为代表。


7.1.1. 组间多写


组间多写是把数据分成多个不相交的 shard,每个组的机器负责一个 shard 。当一个事务涉及的数据(读集合和或写集合)都在某个组时,这种事务称为本地事务。当一个事务涉及的数据分布在超过一个组时,这种事务称为分布式事务。


本地事务可以在本组独立执行,组之间不需要任何通信。为了减少事务冲突带来的性能降低,一般都是由组内 leader 执行本地事务,通过 Paxos 等一致性协议保证组内机器的数据一致[8]。各个组间并行执行本地事务,可以极大提高本地型事务的写性能。


组间多写最大的阻碍是分布式事务,而分布式事务是非常昂贵的。在 SQL 的模型中,为了实现 read repeatable 级别的事务隔离,事务管理器需要检查两个并发事务的写数据集是否冲突;为了达到 serializable 级别的事务隔离,事务管理器需要检查两个并发事务的读数据集和写事务集是否冲突[10]。这一般通过严格两阶段锁(strict two-phase locking,严格 2PL)和/或者多版本并发控制 MVCC 实现。当这些数据集跨组时,就涉及到跨组的机器通信。


一个组同时遇到本地事务和分布式事务时,在本组需要根据事务的隔离级别,由事务管理器仲裁执行。


以 Google Spanner 为例,一个涉及两个机器组(Spanner 中的组是指 Paxos 组)事务就需要在 coordinator leader 和 non-coordinator-participant leader 之间两次通信,前者组内还涉及一次 Paxos 写操作,后者组内再加两次 Paxos 写操作[8,Sec. 4.2.1 Read-Write Transactions]。当跨机房部署时,机器之间的网络延迟使得通信代价更加高昂。Spanner 为了减少这种昂贵的跨组事务,要求所有数据都必须有 Primary key,并且其它数据尽量挂接在 Primary key 下面,使得事务尽量在一个组内、且由组内 leader 执行。


7.1.2. 组内多主多写


组内多主多写时每个机器都有完整的数据,但这份数据分成不相交的逻辑集合,每个机器负责一个集合的写入。这台机器称为这个集合的主机,这个集合称为这个主机负责的数据,其它机器称为这个集合的备机。客户端将写操作发到所涉及数据的主机,由主机通过 atomic broadcast 原子广播将更新请求发送给组内所有的机器,包括主机本身[9]。Galera 和 MySQL Group Replication 都是采用这种方法。



图 3:组内多主多写架构[9]


原子广播具有 3 个特性:


i) 如果一台机器执行一条消息所带的更新命令,那么所有的其它机器都执行这条命令(delivered)。这里“执行”指的是原子广播层将消息交给上层,真实的执行时刻由上层决定。在数据库中,这个上层一般是并发事务管理器,它决定这些消息的真实执行顺序。


ii) 所有机器以相同的顺序执行命令。


iii) 如果一台机器成功广播了一条消息,那么最终所有机器都将执行这条消息。


使用原子广播后,事务的生命周期从 prepare->committed/aborted 改变为 prepare->committing->committed/aborted。



图 4:事务生命周期状态转换[9]


当一个事务只涉及到一个集合的数据时,称为本地事务,由这个集合的主机的本地事务管理器先使用本地严格 2PL 仲裁,然后将 committing 状态的事务通过原子广播发给其它备机。


当一个事务涉及到多于一个集合的数据时,称为复合事务(complex transaction)。这个事务所涉及的某个数据集合的主机将 committing 事务状态,包括读集合(如果需要 serializable 级别隔离。这里的一个小优化是采用 dummy row 减少可能极其庞大的读集合[10])、写集合、以及 committing 状态,通过原子广播发给全组进行仲裁。Galera 和 MySQL Group Replication 都只是校验写集合,因此不支持 serializable 级别事务隔离[18]。


每台机器都可以通过本地事务状态和原子广播收到的消息,独立判定 committing 事务最终是提交还是终止。这种判定由原子广播的特性保证全局一致。



图 5:Deferred Update Replication 和 Certification-based Replication 事务执行时序[11]


强调一下:在机器通过原子广播进行数据同步时,事务的最终结果不能在广播前决定,而是在执行这条消息依赖的前置消息及这条消息后才能决定。这称为 Deferred Update Replication[9][14]推迟的更新复制或者 Certification-based Replication[11]。这里有个重要特点需要注意:只有收到一条消息的所有前置消息后,这条消息和所有未执行的前置消息才能由事务管理器并发执行。因此,这里引入了一定的串行化。


原子广播的突出优点是在低延迟局域网有很高的吞吐率。


但同时原子广播有不小的按机器个数放大的网络延迟,在非低延迟网络会显著放大网络延迟。MySQL Group Replication 使用的 Corosync[16]、Galera[15]默认使用的 gcomm 和支持的 Spread[17]都是基于 Totem[13]这个成员管理和原子广播协议。


Totem 是个为低延迟局域网设计的协议。在 Totem 中,所有机器组成一个环(ring)。无论一台机器是否需要广播,令牌(token)在机器之间都按照环顺序传递。只有拿到令牌的机器才可以进行广播,即发出提交事务请求。因此在下一台机器收到上一台机器令牌的网络延迟期间,整个系统处于等待状态。为了保证 Safe Order Delivery,消息(实际是 regular token,不是 regular message)需要在环中循环两圈,才能知道是否可以执行,因此,消息延迟是(4f+3)*RTT/2,其中(2f+1)是机器的数目、f 表示容错的机器数。


在一个典型的两地三中心部署中,这导致一次事务写操作延迟极其高昂。例如一地两中心的网络延迟一般可以控制在 2ms(单向),上海-深圳间网络延迟一般是 15ms(单向),则一次事务写操作的网络延迟是 64ms!在两地三中心的配置中,PhxSQL 的主和一个备一般分别在一地的两中心,另一个在异地。在通常情况下,master 的 Paxos 一次写入只需一个 accept,并且只等最快的备机返回。这时 PhxSQL 的写延迟只有 4ms!


相比 Paxos 这类协议,原子广播还有一个缺陷。当任意一台机器宕机或者网络中断时,Totem 此时会超时,在踢掉宕机的机器、重新确定组成员之前,整个集群的消息停止执行,即写操作暂停。


对于 read-only 事务,只有去数据集合的主机读取、或者昂贵地读取原子广播 Quorum 台机器、或者使用类似 Spanner 的 TrueTime[8]技术读取任一符合资格的机器,才能保证线性一致性。Galera 节点间有延迟,并且只读事务在本地执行,不支持线性一致性[15]。如果 MySQL Group Replication 支持线性一致性,请不吝告知。


了解基于原子广播的组内多主多写模式的原理和优缺点后,使用多写模式还需要根据业务仔细划分数据集,尽量减少公共数据的使用,同时处理好自增 key 等细节问题,以减少事务间的跨机冲突。


PhxSQL 建立在开源的 PhxPaxos 基础上,感兴趣的读者可以用 PhxPaxos 方便实现原子广播插件,加载到 MySQL 中,从而支持多写。


如果不要 read repeatable 或者 serializable 级别隔离的事务,例如简单的 key-value 操作,同时通过 lease 机制保证线性一致性,是可以做到高效率多写的。但这就违反了 PhxSQL 完全兼容 MySQL 和最小侵入 MySQL 的原则。


7.2. 为什么不支持分库分表?


分库分表也是个诱人的选择:可以平行无限扩展读写性能。分库分表就是分组,上个小节已经讨论了分布式事务的高昂成本。另外,为了保证完全兼容 MySQL、支持全局事务和 serializable 级别事务隔离,不大改 MySQL 就支持 sharding 是非常困难的。大改又违反了“最小侵入 MySQL”原则,并且可能引入新的不兼容性。在应用不要求全局事务和 serializable 级别事务隔离情况下,感兴趣的读者可以把 PhxSQL 作为一容错的 MySQL 模块,在上层构建支持分库分表的系统。因为 PhxSQL 本身的容错性,这样做比在 MySQL 基础上直接构建要简单,无需关心每个 sharding 本身的出错。如果以后有需求,PhxSQL 团队也可能基于 PhxSQL 开发一个分库分表的新产品。当然,这个产品难以提供 PhxSQL 级别的兼容性。


7.3. 为什么这么纠结于 serializable 级别事务隔离性,read repeatable 级别很多时候已经够用了啊?


我们在设计原则中已经提到,为了完全兼容 MySQL。我们认为一项好的技术是一项方便用户的技术,提供符合用户直觉预期、不用看太多注意事项的技术是我们的温柔。我们很诚恳,也是为了方便用户。我们不想说 PhxSQL 完全兼容 MySQL,然后在不起眼的地方 blabla 列出好几页蝇头小字的例外。事实上,对于关键业务来说,serializable 是必要的、read repeatable 是不足的。read repeatable 有个令人讨厌的 write-skew 异常[12]。


举个例子。小薇在一个银行有两张信用卡,分别是 A 和 B。银行给这两张卡总的信用额度是 2000,即 A 透支的额度和 B 透支的额度相加必须不大于 2000:A+B<=2000。


两个账户的扣款函数用事务执行分别是:


A 账户扣款函数:


sub_A(amount_a):


begin transaction


if (A+B+amount_a <= 2000)


{ A += amount_a }


Commit


B 账户扣款函数:


sub_B(amount_b):


begin transaction


if (A+B+amount_b <= 2000)


{ B += amount_b }


commit


假定现在 A==1000,B==500。如果小薇是个黑客,同时用 A 账户消费 400 和 B 账户消费 300,即 amount_a == 400,amount_b == 300。那么这个数据库会发生什么事情呢?


如果是 read repeatable 级别隔离,sub_a 和 sub_b 都会同时成功!最后 A 和 B 账户的透支额分别是 A=1000+400=1400,B=500+300=800,总的透支额 A+B=1400+800=2200>2000,超过了银行授予的额度!如果不是信用卡的两笔小消费,而是两笔大额转账,那么银行怎么办?


如果是 serializable 级别隔离,则 sub_a 和 sub_b 只有一个成功。具体分析有兴趣的读者可以自己完成。


7.4. 为什么不把显著提升 MySQL 性能作为一个主要目标?


事实上,PhxSQL 已经显著提升了 MySQL 主备的写入性能。与 semi-sync 比,在测试环境中,PhxSQL 的写入性能比 semi-sync 高 16%到 25%以上。读性能持平。这是在满足完全兼容 MySQL 和最小侵入 MySQL 原则下所能获得的结果。鉴于 PhxSQL 对 MySQL 的改动是如此之小,对性能有高要求的读者,可以方便地把 PhxSQL 中的 MySQL 换成其它高性能版本,获得更高性能。


7.5. 为什么编译时不支持 C++11 以下标准?


作为热爱新技术的码农,我们真的很喜欢 C++11 中期待已久、激动人心的新特性,例如极大增强的模板、lambda 表达式、右值和 move 表达式、多线程内存模型等,这将 C++带入了一个新的时代,大大提高了码农搬砖的速度、编码的正确性、和程序的性能。有了 C++11,PhxSQL 的开发效率提高了很多。


8. 与 Galera 及 MySQL Group replication 的比较


参见 7.1.2 小节。

结论

PhxSQL 是一个完全兼容 MySQL,提供与 Zookeeper 相同强一致性和可用性的 MySQL 集群。


感谢大家看完这么长的一篇文章。希望大家多阅读 PhxSQL 源码,多提技术性意见,甚至成为源码贡献者!

参考

  1. 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.

  2. L. Lamport. How to make a multiprocessor computer that correctly executes multiprocess programs. IEEE Trans. Computer. C-28,9 (Sept. 1979), 690-691.

  3. P. Hunt, M. Konar, F. P. Junqueira, and B. Reed. ZooKeeper: wait-free coordination for Internet-scale systems. USENIXATC'10, 2010.

  4. L. Lamport. Paxos Made Simple. ACM SIGACT News (Distributed Computing Column) 32, 4 (Whole Number 121, December 2001) 51-58.

  5. D. Ongaro and J. Ousterhout. In search of an understandable consensus algorithm. USENIX ATC ’14, 2014.

  6. 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.

  7. T. Chandra, R. Griesemer, and J. Redstone. Paxos made live – an engineering perspective. PODC’07, 2007.

  8. J. C. Corbett, J. Dean, M. Epstein, and etc. Spanner: Google's Globally-Distributed Database. OSDI'12, 2012.

  9. F. Pedone, R. Guerraoui, and A. Schiper. The database state machine approach. Journal of Distributed and Parallel Databases and Technology, 14:71–98, 2002

  10. V. Zuikeviciute and F. Pedone. Revisiting the database state machine approach. VLDB Workshop on Design, Implementation, and Deployment of Database Replication. 2005.

  11. Certification-based Replication. Visited at 2016/9/5.

  12. Snapshot isolation. Visited at 2016/9/5.

  13. 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.

  14. http://downloads.mysql.com/presentations/innovation-day-2016/Session_7_MySQL_Group_Replication_for_High_Availability.pdf. Visited at 2016/9/5.

  15. Replication API. Visited at 2016/9/5.

  16. Corosync by corosync. Visited at 2016/9/5.

  17. http://www.spread.org/. Visited at 2016/9/5.

  18. Isolation Levels. Visited at 2016/9/5

  19. L. Lamport. Fast Paxos. Technical Report, MSR-TR-2005-112.

  20. I. Moraru, D. G. Andersen, and M. Kaminsky. There is more consensus in egalitarian parliaments. SOSP’13, 2013.

  21. GitHub - tencent-wechat/phxpaxos: C++ Paxos library that has been used in Wechat production environment..


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

用户头像

OpenIM

关注

还未添加个人签名 2021.08.30 加入

还未添加个人简介

评论

发布
暂无评论
谈谈PhxSQL的设计和实现哲学(下)