最近和同事交流,经常被问到分库分表和分布式数据库如何选择。图片来自Pexels。网上也有很多关于中间件+传统关系型数据库(分库分表)和NewSQL分布式数据库的文章,但有些观点和判断在我看来是偏激的。脱离环境来评价方案的好坏,其实是不公平的。本文对两种模式的关键特性原理进行对比,希望尽可能客观中立地阐明它们真正的优缺点和适用场景。NewSQL数据库先进在哪里?首先关于“中间件+关系型数据库分库分表”是否算NewSQL分布式数据库有一篇国外论文pavlo-newsql-sigmodrec:https://db.cs.cmu.edu/papers/2016/pavlo-newsql-sigmodrec2016.pdf按照本文的分类,Spanner、TiDB、OB可以看作是第一个新的架构类型,Sharding-Sphere、Mycat、DRDS等中间件解决方案可以看作是第二种(还有第三种云数据库,本文不做详细介绍)。基于中间件(包括SDK和Proxy)+传统关系数据库(分库分表)模式的模型是分布式架构吗?我是这么认为的,因为存储确实是分布式的,也可以实现横向扩展。但它不就是一个“伪”分布式数据库吗?从高级架构的角度来说,这样说是有道理的。“伪”主要体现在中间件层和底层DB之间重复的SQL解析和执行计划生成,存储引擎是基于B+Tree等,这在分布式数据库架构中实际上是冗余和低效的。为了避免因分布式数据库的真实性而引起的口水战,本文中的NewSQL数据库特指这种新架构的NewSQL数据库。NewSQL数据库相比中间件+分库分表到底有多先进?画一个简单的架构对比图:传统数据库是为磁盘设计的,基于内存的存储管理和并发控制没有NewSQL数据库高效。中间件模式SQL解析、执行计划优化等都是在中间件和数据库中重复工作,效率比较低。与XA相比,NewSQL数据库的分布式事务进行了优化,性能更高。新架构NewSQL数据库存储设计基于Paxos(或Raft)协议的多副本。相对于传统的数据库主从模式(半同步转异步后同样存在数据丢失问题),实现了真正的高可用和高可靠。(RTO<30s,RPO=0)。NewSQL数据库天然支持数据分片。数据迁移和扩容自动化,大大减少了DBA的工作量。同时对应用透明,不需要在SQL中指定分库分表键。这些大部分也是NewSQL数据库产品的主要宣传点,但是这些看似美好的功能真的如此吗?下面我就以上几点我的理解进行阐述。分布式事务首先要说的是分布式事务:这是一把双刃剑。CAP限制想想为什么早先出现的NoSQL数据库不支持分布式事务(最新版的MongoDB等也开始支持了),是不是缺乏理论和实践支持?不,原因是CAP定理仍然是分布式数据库头颈上的魔咒,在保证强一致性的同时,必然会牺牲可用性A或者分区容错性P。为什么大多数NoSQL不提供分布式事务?那么NewSQL数据库是否突破了CAP定理限制呢?不是,GoogleSpanner,NewSQL数据库的中流砥柱(目前大多数分布式数据库都是按照Spanner架构设计的),提供了大于5个9的一致性和可用性,号称是“真正的CA”。其真正含义是系统处于CA状态的概率很高,因为网络分区导致服务中断的概率很小。真正的原因是它创建了一个私有的全局网络,以确保不会因网络中断而导致网络分区。再一个就是其高效的运维团队,这也是CloudSpanner的卖点。详见CAP提议者EricBrewer写的《Spanner, TrueTime 和 CAP 理论》:http://dockone.io/article/2129推荐一篇关于分布式系统的有趣文章,《站在巨人的分布式肩膀上》:https://queue.acm.org/detail.cfm?id=2953944其中提到:在分布式系统中,你可以知道工作在哪里,也可以知道工作什么时候完成,但不能两者都知道;两阶段协议本质上是反可用性协议。完备性两阶段提交协议是否严格支持ACID,能否覆盖各种异常场景?2PC在Commit阶段发送异常。实际上,存在一些类似于best-effortone-phasecommit的可见问题。严格来说,不能在一段时间内实施。保证A的原子性和C的一致性(Recovery机制可以保证故障恢复后最终的A和C)。完整的分布式事务支持不是一件简单的事情。需要能够处理网络和各种硬件的各种异常,包括网卡、磁盘、CPU、内存、电源等,并通过严格的测试。之前和一个朋友交流过,他们甚至说目前已知的NewSQL在分布式事务支持方面是不完善的,都有逃不掉的情况。圈内人都这么肯定,这也说明了分布式事务支持的完备性。事实上,这是不平衡的。但是分布式事务是这些NewSQL数据库非常重要的底层机制。跨资源的DML、DDL等都依赖于它的实现。如果该块的性能和完整性受到损害,将影响上层跨分片SQL执行的正确性。大的影响。性能传统关系型数据库也支持分布式事务XA,但为什么很少用在高并发场景呢?由于XA的基本两阶段提交协议存在网络开销大、阻塞时间长、死锁等问题,这也导致其在基于传统关系型数据库的OLTP系统中实际上很少被大规模使用。NewSQL数据库的分布式事务实现仍然大多基于两阶段提交协议,比如googlepercolator分布式事务模型,采用原子钟+MVCC+SnapshotIsolation(SI)。这种方式通过TSO(TimestampOracle)保证全局一致性,通过MVCC避免加锁,通过主锁和二级锁将部分提交转化为异步,相对于XA确实提高了分布式事务的性能。SI是乐观锁。在热点数据场景下,可能会出现大量提交失败的情况。另外,SI的隔离级别和RR的隔离级别也不完全一样。它不会有幻读,但会有写偏斜。但是无论怎么优化,相比1PC,2PC额外的GID获取、网络开销、prepare日志持久化等方面还是会带来不小的性能损耗,尤其是在跨节点数较多的情况下。比如你在银行场景做批量扣款,可能会上传一个文件到W账户。这样的场景,无论怎么做,吞吐量都不会很高。Spanner给出的分布式事务测试数据:虽然NewSQL分布式数据库产品标榜全面支持分布式事务,但这并不意味着应用完全不需要关心数据拆分。这些数据库的最佳实践还是会写的,应用的大部分场景尽量避免分布式事务。既然强一致性事务的性能成本太高,我们可以反思一下,我们是否真的需要这样的强一致性分布式事务?尤其是微服务拆分后,不太可能会有很多系统放在一个统一的数据库中。尽量弱化一致性要求,即灵活事务,放弃ACID(Atomicity,Consistency,Isolation,Durability),改用BASE(BasicallyAvailable,Softstate,Eventuallyconsistent)。例如Saga、TCC、可靠消息等模型保证最终一致性。对于大规模高并发的OLTP场景,我个人更推荐使用灵活事务而不是强一致性分布式事务。关于柔性事务,笔者之前也写过一个技术组件。近年来,出现了一些新的模型和框架(比如Fescar,刚刚被阿里开源)。只能用两阶段提交协议来解决分布式事务吗?OceanBase1.0中通过updateserver避免分布式事务的思路很有启发,但是2.0版本之后也变成了2PC。业界的分布式事务不仅仅是两阶段提交方案,还有其他方案its-time-to-move-on-from-two-phase:https://www.jdon.com/51588HA和remotemulti-activemaster-slave模式不是最优的。即使是半同步复制,在极端情况下(半同步转异步),依然存在数据丢失的问题。目前业界公认较好的解决方案是基于Paxos分布式共识协议或Raft等其他类Paxos方法。GoogleSpanner、TiDB、cockcoachDB、OB都采用了这种方式。基于Paxos协议的多副本存储遵循多半写入原则,支持自动选主,解决了数据的高可靠性,缩短了故障转移时间,提高了可用性,尤其是降低了运维的工作量。该方案在技术上已经非常成熟,也是NewSQL数据库底层的标准配置。当然,这种方法也可以用在传统的关系型数据库中。阿里和微信团队也改造了MySQL存储,支持Paxos多副本。MySQL也推出了正式版的MySQLGroupCluster。预计主从模式或将成为历史。分布式共识算法本身并不难,但是在工程实践中,需要考虑很多异常情况,需要做很多优化。实现生产级可靠成熟的共识协议并不容易。比如在实际使用中,必须转化为multi-paxos或者multi-raft,需要通过批处理和异步的方式来减少网络、磁盘IO等开销。需要注意的是,很多NewSQL数据库厂商宣传可以基于Paxos或者Raft协议实现【多活远程站点】。这其实有一个前提,就是异地站点之间的网络延迟不能太高。以银行“两地三中心”为例。异地之间的距离通常是几千里,延迟可达几十毫秒。没有OLTP系统是可以接受的。在数据库层面做异地多活是一个美好的愿景,但是对于距离带来的延迟目前还没有很好的解决方案。之前和Ant团队交流过,Ant的异地多活方案是在应用层通过MQ同步双写事务信息,远端DC将事务信息保存在分布式缓存中。一旦发生远程切换,数据库同步中间件会通知数据延迟时间,应用会从缓存中读取交易信息,并将该时段涉及的业务对象,如用户、账户等列入黑名单,等待数据同步赶上这些业务对象然后从黑名单中删除。由于双写的不是所有的数据库操作日志,只是事务信息,所以数据延迟只影响一定时间段内的数据。这是目前我认为比较靠谱的远程多活方案。另外,有些系统进行了单元改造,Paxos选择master时也应该考虑到这一点。这也是目前很多NewSQL数据库所欠缺的功能。Scale水平扩展和分片机制Paxos算法解决了高可用和高可靠的问题,但是没有解决Scale水平扩展的问题,所以必须支持分片。NewSQL数据库天生内置分片机制,会根据每个分片的数据负载(磁盘使用率、写入速度等)自动识别热点,然后进行分片、迁移数据、合并。无形中,节省了DBA大量的运维工作量。以TiDB为例,它将数据切分成Region,当Region达到64M时,会自动迁移数据。分库分表模式下,需要指定拆分键、拆分方式(Range、取模、一致性哈希或自定义路由表)、路由规则、拆分数据库表数、扩展方式等。比较对于NewSQL数据库,这种模式给应用带来了很大的侵入性和复杂性,这对大多数系统来说也是一个很大的挑战。分库分表模式也可以实现在线扩展。基本思路是先通过异步复制添加数据,然后设置只读完成路由切换,最后释放写操作。当然,这些都需要中间件和数据库端的配合才能完成。这里的问题是,NewSQL数据库内置的统一分片策略(比如基于Range的TiDB)可能并不是最高效的,因为它与领域模型中的分片元素不一致,从而导致事实那么多事务会产生分布式事务。比如银行的核心业务系统以客户为维度,也就是说客户表、客户的账户表、流量表在大多数场景下是写在一起的。但是,如果按照每张表的主键Range进行分片,这个事务就无法在一个分片上完成,在高频OLTP系统中会造成性能问题。分布式SQL支持常见的单分片SQL,都可以很好的支持。由于NewSQL数据库的定位和定位是通用型数据库,所以支持的SQL更加完备,包括跨分片Join、聚合等复杂SQL。中间件模式多为应用需求而设计,但大多也支持带分键的SQL、数据库表遍历、单库join、聚合、排序、分页等,但对跨库join和聚合的支持不够.NewSQL数据库一般不支持存储过程、视图、外键等功能,而中间件模型的底层是传统的关系型数据库。如果这些功能只涉及一个单一的数据库,那么支持它们就更容易了。NewSQL数据库往往选择兼容MySQL或PostgreSQL协议,因此SQL支持仅限于这两者。驱动模型等中间件往往只需要做简单的SQL解析、计算路由、SQL重写,因此可以支持更多类型的数据库SQL。SQL支持的不同主要在于分布式SQL执行计划生成器。由于NewSQL数据库有底层数据的分布和统计信息,可以作为CBO使用,生成的执行计划更高效。但是中间件模式没有这些信息,只能基于规则RBO(Rule-Based-Opimization)。这也是为什么中间件模式一般不支持跨库join的原因,因为效率往往不高,还不如交给应用来做。从这里也可以看出,中间件+分库分表模式的架构风格体现了一种折衷和平衡,是一种面向应用的设计;而NewSQL数据库要求更高,“大包大包”。它是一种通用的底层技术软件,因此后者的复杂度和技术门槛要高得多。存储引擎传统关系型数据库的存储引擎设计都是面向磁盘的,大部分都是基于B+树的。B+树通过降低树的高度来减少随机读,从而减少磁盘寻道次数,提高读性能。但是大量的随机写会导致树分裂,造成随机写,导致写性能下降。NewSQL底层存储引擎多采用LSM。与B+树LSM相比,将磁盘的随机写入改为顺序写入,大大提高了写入性能。但是由于需要合并数据,LSM的读取性能比B+树差。一般来说,LSM更适合写大于读的场景。当然,这只是从纯数据结构的角度进行比较。在数据库实际实现中,会通过SSD、buffer、BloomFilter等优化读写性能,所以读性能基本不会下降太多。由于多副本和分布式事务的开销,NewSQL数据的响应时间并不优于单机关系数据库SQL。但由于集群弹性扩容,整体QPS提升还是比较明显的。这也是为什么NewSQL数据库供应商说分布式数据库比单个SQL响应时间更看重吞吐量的原因。成熟度与生态系统分布式数据库是一种新型的通用底层软件,准确的度量和评估需要多维度的测试模型。需要包括开发状态、使用情况、社区生态、监控运维、周边配套工具、功能满意度、DBA人才、SQL兼容性、性能测试、高可用测试、在线扩展、分布式事务、隔离级别、在线DDL等。等待。NewSQL数据库的发展虽然经历了一定时期的考验,但多集中在互联网和传统企业非核心交易系统中。目前还处于快速迭代和规模使用不断优化提升的阶段。相比之下,传统的关系数据库已经发展了很多年。通过完整评估,它们在成熟度、功能、性能、周边生态、风险控制、相关人才积累等方面具有明显优势。系统兼容性也更好。对于互联网公司来说,数据量压力越来越大,追求新技术的基因,会更倾向于尝试NewSQL数据库,无需考虑数据库分表、应用改造、扩容、事务一致性等问题。它是一个非常有吸引力的解决方案。.对于银行等传统企业、风险意识较高的行业,未来一段时间,NewSQL数据库可能仍处于探索和审慎试点阶段。基于中间件+分库分表模式,架构简单,技术门槛较低。虽然没有NewSQL数据库全面,但是大部分场景的核心需求是SQL拆分后的正确路由,而这个功能的中间件模式还是绰绰有余的,可以说在大部分OLTP中已经足够了场景。限于篇幅,其他特性如在线DDL、数据迁移、运维工具等特性本文不再对比。总结如果看了上面的内容,你还是不知道选择哪种模式,那么结合下面的问题,想想NewSQL数据库解决的点是不是自己真正的痛点:强一致性事务必须要解决吗在数据库层?数据增长速度不可预测?扩容的频率是否超出了自身的运维能力?吞吐量比响应时间更重要吗?是否有必要对应用程序完全透明?有熟悉NewSQL数据库的DBA团队吗?2到3是肯定的,那么可以考虑使用NewSQL数据库。虽然前期可能需要一定的学习成本,但是是数据库的发展方向,未来收益会更高,尤其是互联网行业。随着数据量的快速发展,分库分表带来的痛苦会与日俱增。当然,如果选择NewSQL数据库,就要做好承担一定风险的准备。如果你还没有做出决定,请思考以下问题:最终一致性能否满足实际场景?能否预估未来几年的数据总量?扩容、DDL等操作是否有系统维护窗口?是响应时间是否比吞吐量更敏感?它是否需要与现有的关系数据库系统兼容?是否有传统数据库DBA人才积累?分库分表对应用的入侵能容忍吗?如果这些问题大部分都是肯定的,那我们就分库分表吧。软件领域几乎没有完美的解决方案,NewSQL数据库也不是数据分布式架构的灵丹妙药。相比之下,分库分表是一种成本更低、风险更小的方案。最大程度的复用了传统的关系型数据库生态。通过中间件,也可以满足分库分表后的大部分功能。定制能力更强。在NewSQL数据库尚未完全成熟的现阶段,分库分表可以说是一种上限低下限高的解决方案,尤其是对于传统行业的核心系统。如果你还是打算将数据库作为黑盒产品使用,实用且擅长分库分表,算是一个稳妥的选择。很多时候,软件的选择取决于领域的特点和架构师的风格,而这又受限于作者的知识和所属行业的特点。以上只是一些粗浅的个人看法,欢迎讨论。作者:蚊子松鼠编辑:陶佳龙来源:https://www.jianshu.com/p/9131edd8fd2c
