随着各行各业电子信息化的不断加深,线上交易数据保持了长时间高速增长的态势,对数据存储的需求越来越大,数据库管理系统(DBMS)面临越来越大的性能、空间和稳定性压力。在此过程中,得利于计算&存储&网络等硬件领域的不断进步,业界流行的数据库管理系统逐步从单机架构向分布式架构演变。笔者希冀从梳理数据库管理系统所面临的一个又一个实际挑战及业界所提出的诸多解决方案的过程中,发现片缕灵感以指引未来的数据库开发工作。
业界起步阶段诞生的第一代交易型数据库具有以下主要特点:和程序一起运行在大型机/小型机为代表的高端计算机上; 利用硬件层面大量冗余设计带来的强大可靠性来保障数据库可用性,通过直接升级硬件规格的方式来扩容数据库性能容量。即使到今天仍然有很多银行核心系统以这样的方式运行。
很快,这种堆硬件模式来获取提升的模式局限性就暴露出来了。首先,数据库可用性对硬件层面冗余的依赖抬高了大型机等存储型设备的造价,动辄单台上千万美元的价格是诸多中小客户难以承受的负担。次之,由于性能扩容只能通过提升硬件规格的方式实现,而硬件规格的10%性能容量提升,往往需要客户付出成倍的购买成本,且整体性能极限也受限于当时的硬件发展的天花板。由于上述所说的诸多成本原因,业界一直没有停止过尝试以x86服务器为代表的“廉价”硬件替换大型机来提供交易型数据库服务的努力。
首当其冲要解决的是,如何保障数据库服务的可用性。由于交易型业务往往都是企业中较为核心的往往会涉及金钱相关的线上业务,因此对数据库服务的可用性有比较高的要求。最简单地,通过把数据库架设在共享存储系统,将数据文件存储在共享存储,实现数据库实例和存储介质的解耦,从而实现数据库服务的高可用。当数据库所在机器出现故障时,另一台备用机器通过接管共享存储的数据文件并快速对外继续提供数据库服务,从而保障数据库可用性。
不过,传统的共享存储也是一种高昂的硬件设备,其本身的成本&可用性&容量同样面临着硬件的限制,所以采用共享存储的数据库方案一直难以大规模流行开来。直到后来分布式文件存储系统的日益成熟,这种数据库方案才真正地流行开来。时至今日,事实证明云盘产品能在云分布式环境提供稳定可靠的存储服务,以RDS为代表的各类云数据库产品普遍通过云盘的可用性来保障数据库服务的可用性。
共享存储方案的缺点也很明显。一方面,数据放在远端的共享存储意味着数据库需要通过网络才能访问数据,这导致比单机数据库更明显的访问延迟和性能开销。数据库方案引入共享存储,意味着额外引入了对网络&共享存储等更多的外部依赖,进而导致整个数据库的稳定性有所降低。
另一方面,共享存储方案需要另一台备用机器时刻等待着接管数据库服务,意味着一个数据库服务需要两台机器但却只能有一台机器在提供服务,无法充分利用备用机器资源。因此,如何更好地将闲置的备用机器利用起来,也是一个重要的技术课题。与此同时,当前互联网时代流量特点除海量数据/海量吞吐外,还包括写入修改只占据请求总览的一小部分,用户请求绝大部分都是对内容的查询展示,内容查询场景对数据实时性的要求并不高。基于上述特点,既然另一台备用机器本身空闲又能访问到所有的数据,那是不是可以让它也同时提供一些历史版本的只读查询服务呢?是的,云盘存储方案就能通过只读节点的方式把原本灾备用的节点对外提供只读服务,从而提升整个数据库系统的查询能力。
然而,云盘存储方案只是相对简单地将数据库服务架构在云盘上,只读节点无法和读写节点同时访问共享存储的同一份数据文件,只能通过单独使用一份共享存储文件后再通过额外机制来和主节点进行同步来实现。共享存储中的数据文件本身就是有多副本的,这意味着数据库层面的多节点会带来乘法效应,浪费更多的存储资源。这和提高机器资源利用率的初衷是想违背的,而且没有利用上共享存储可以被多个机器访问到的天然优势。那么,有没有办法让数据库实例的主节点和多个只读节点同时使用一份共享存储数据文件呢?
共享盘存储方案,通过在数据库层实现多节点间的协调同步,规避多个节点各自对数据文件的变更冲突,从而实现一份数据存储多个节点服务的数据库架构。共享盘存储方案在利用存储产品高可用性保障数据库可用性之余,又很好地利用了共享存储的数据共享优势提供多节点服务。
共享盘存储方案的查询性能可以通过多个只读实例进行扩展,但写入请求只有一个读写节点可以提供服务。而读写节点的性能极限仍然受限于单机硬件的性能上限,特别是IO设备的性能上限,不仅没从分布式架构中获得收益,甚至由于共享存储的额外开销反而有所下滑。那么,要怎么样才能让数据库服务的写入性能从分布式共享存储中获益呢?
腾讯云国产数据库TDSQL-C(CynosDB)即是业界对这一问题给出的一份答卷。经过长时间的观察研究,工程师们发现线上数据库服务的写入性能瓶颈通常都在网络IO。特别是RDS数据库在运行期间需要不停刷写预写日志(write ahead log)/数据文件(data file)等多种数据到远端的分布式共享存储,占用了大量机器IO容量。与此同时,TDSQL-C的工程师们还指出,RDS数据库持久化到分布式共享存储的预写日志/数据文件等数据中其实包含了大量的冗余信息。比如,针对MySQL/innodb数据库架构下的binlog/redolog这两种预写日志,虽然日志格式各不相同(逻辑日志/物理日志),但二者都是对同一份数据修改的记录,却占用两倍IO流量。再比如数据库以页为单位持久化数据文件,而数据文件的页大小通常是16KB,意味着即使只修改某一页中的一行记录,在写数据文件的时候数据库实例也会产生16KB IO流量,存在比较大的写放大现象。甚至更近一步地,这一行记录的数据变更其实已经随着之前的预写日志写到分布式共享存储,写数据文件的脏页数据完全没有必要,是一种浪费!!
单机数据库之所以采用预写日志技术,是为了通过把数据脏页的随机写操作变成预写日志的顺序写操作,从而提升单机数据库整体性能。但预写日志技术只能推迟并尽可能合并对数据文件的随机写入操作,却不能完全避免对数据文件的随机写入。单机数据库需要不断地把脏页逐步刷写到数据文件来推进检查点,从而释放出更多的日志容量&控制实例故障的恢复时长。而刷脏页通常属于磁盘随机写操作,特别在随机写入的业务场景下,非常影响数据库性能,是单机数据库的一大性能瓶颈点。
因此,工程师们提出了Log is Database的技术理念,这也是TDSQL-C的核心理念。数据库读写节点只通过必要的预写日志把数据库数据变更信息传递给分布式共享存储,而不再需要传递数据文件脏页等额外信息,读写节点机器的IO开销大大减少。那么,数据文件的数据变更怎么办呢?既然预写日志里面已经有所有的数据变更信息,那分布式共享存储系统可以直接从预写日志里获取到数据变更信息,直接将其更新到相应的数据文件中。针对MySQL/innodb数据库架构,TDSQL-C更是可以进一步把binlog日志直接省略,单单传递redolog日志就足以把整个数据库的数据变更传递到共享存储系统,大大减少了RDS MySQL数据库实例的IO流量,有效提升了MySQL数据库的性能上限。
在 TDSQL-C 中,读写节点通过只传递必要的预写日志,把数据文件更新维护的任务完全交由分布式共享存储系统来处理,就能从维护数据文件的高昂开销中脱身。而分布式共享存储系统得益于自身的架构优势,天然可以通过水平扩展的方式来不断提升整个系统的吞吐性能上限。更进一步的,由于分布式存储系统拥有整个数据库的完全历史数据信息,当数据库节点需要读取历史版本的数据页内容时,也可以直接从分布式共享存储系统获取历史数据页,把从数据文件和回滚日志处理生产历史版本数据页的任务交由分布式存储系统来进行。这就是可计算存储。
至此可以看到,与共享盘存储方案相比,虽然写入请求只有一个读写节点可以提供服务,但该读写节点只需要负责请求解析、查询执行、事务管理、缓存管理等任务,而数据文件更新维护、数据页版本维护这部分数据库中开销极重的部分流程却拥有了水平扩展的能力。这意味着 TDSQL-C 在保证可用性的基础上,已经在一定程度上摆脱了传统单机数据库面临的硬件瓶颈,大大提升了服务能力上限。
文章篇幅所限,本文简单地介绍了基于共享存储的架构下数据库产品从单机数据库逐步向分布式数据库眼睛的发展历程中逐步面对的问题和对应的解决思路。后续文章中,笔者将尝试探讨数据库的另一条发展路线,即基于独立存储的架构下数据库产品如何从单机数据库逐步走向分布式数据库。
本人水平有限,本文目的只是总结下个人日常的一些经验思考,文中缪误甚多,还请各位大神多加批判,共同讨论交流共同进步。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。