本文由云+社区发表

本文作者:孙旭,腾讯数据库开发工程师,9年数据库内核开发经验;熟悉数据库查询处理,并发控制,日志以及存储系统;熟悉PostgreSQL(Greenplum,PGXC等)、Teradata等数据库内核实现机制。

CynosDB 是腾讯数据库研发团队推出的自研数据库,有PostgreSQL和MySQL两个版本。本文以兼容PostgreSQL版CynosDB为例,介绍我们的架构设计和优化思路。

1、概述

PostgreSQL是世界上最先进的开源数据库,始于1986年,有30多年的社区演进历史。其先进的架构、可靠性以及丰富的功能已经获得业界高度认可。同时,PostgreSQL能够在多种操作系统上运行,支持多种索引类型和扩展,特别是对PostGIS扩展的支持,可以让PostgreSQL轻松的处理地理信息数据。

兼容PostgreSQL版CynosDB作为PostgreSQL在NewSQL领域的一个产品,也具有良好的扩展性。由其架构特点带来的资源池化,可以让用户付出更少的成本而获得同等的性能,并且不损失PostgreSQL数据库原有的功能特性。

2、基础架构

现有共有云上的数据库存在一些不足:

1.网络IO重。传统云上的主备架构下,会有大量数据需要写到磁盘,主要包括:WAL LOG、脏页数据、防止页部分写的Double Write或者Full Page Write。

2.主从实例不共享数据。一方面浪费了大量存储,另一方面进一步加重了网络IO。这样会导致磁盘利用率低、CPU闲置等问题。

而CynosDB可以通过日志下沉、共享存储来解决上述问题,以实现共有云数据库的高性价比、高可用性以及弹性扩展。其基础架构如下:

架构中的组件:

\1. master是数据库的主实例,负责接收应用的读写事务请求。

\2. slave是数据库的只读实例,负责处理应用的读请求,可以支持多个slave实例。

\3. CynosStore Client提供访问分布式存储(CynosStore)的接口。DB引擎通过这些接口访问存储,完成数据文件的读写等操作。

\4. CynosStore是一个分布式存储系统,存放数据库的数据和日志,并负责日志到数据页面的转换。

\4. 集群管理服务负责整个系统的管理,例如:存储扩容,实例创建等。

\5. 冷备存储用来存储系统的日志。

master实例将数据的变更以日志方式发送到存储系统(CynosStore)中,同时CynosStore会定期将日志合并到数据页面上。因此,CynosDB无需将脏页写入到存储中,这点与传统数据库是不同的。slave数据库实例没有写事务,不会向存储发送日志,但是会从存储中读取页面,也会接收master实例的日志来刷新内存中的数据页面;如果收到的日志所对应的页面没有在slave的内存中,则会丢弃这些日志。

从架构上看,CynosDB实现了存储和计算分离,并把资源进行池化,因此适合云上部署。而且计算和存储传输数据的仅有日志流,无需写脏页面,因此也减少了系统中的网络量。总的来说,CynosDB具有如下优势:

1.计算能力弹性扩展。可以快速增加slave节点来扩展读能力,而不必进行全量的数据拷贝。

\2. 存储能力弹性扩展。不像传统数据库那样受单机存储能力的限制。

\3. 充分利用硬件资源。缓解传统主备架构中的CPU闲置、磁盘利用率不高等问题。

\4. 备份容易。备份完全由后台持续进行,用户无需干预。

3、兼容PostgreSQL版CynosDB的计算层架构

CynosDB实现了计算与存储分离,系统也因此被分成两大块:计算层和存储层。计算层负责SQL解析、日志生成等;存储层负责数据存储、日志归档以及日志合并等。本节以CynosDB的PostgreSQL兼容版本为例来介绍计算层架构。其计算层架构如下图所示。为了实现这种NewSQL架构,我们对PostgreSQL内核做了新设计:

灰色部分是PostgreSQL内核原生模块:

\1. SQL:PostgreSQL的SQL引擎,包括词法/语法分析、语义分析、查询重写/优化和查询执行。CynosDB的设计不涉及SQL层改动,因此它兼容PostgreSQL原来的SQL语法和语义。

\2. Access:数据库的访问层,定义了对象的组织方式和访问方法。其中包括:

lHeap:表实现以及访问方法,包括扫描、更新、插入、删除等。

lbtree/gin/gist/spgist/hash/brin:索引实现,包括各种索引的实现和操作方式,如索引扫描、插入等。

lCLOG/MultiXACT:与事务提交状态以及并发等。

Access是设计和优化的重点模块。当表和索引等数据库对象被修改时,原生的PostgreSQL会生成XLog,并写入到日志文件中。在CynosDB中,这些对象修改时也会生成日志,但是这些日志不会写本地的日志文件,而是发送到CynosStore中。

\3. storage/buffer:buffer pool和存储管理,调用文件接口对数据文件进行读写。在CynosDB中使用CynosStore Client对CynosStore中的文件进行操作。

\5. CynosStore Client提供访问CynosStore的接口,以完成数据库对数据文件的操作。包括数据页面读取接口、日志发送接口等。

\6. 分布式存储CynosStore是一个基于日志的分布式的块存储,在本文中不做重点介绍。

CynosDB的计算层把数据文件修改所生成的日志,通过CynosStore Client发送到分布式存储CynosStore中,而CynosStore会将日志定时合并到数据页面上。这里比较重要的一点是,计算层写出日志并不是PostgreSQL原生的XLog,而是我们自己重新设计的日志系统和日志格式。因此CynosDB不依赖于PostgreSQL的原生日志系统,这种设计也可以让我们有机会在CynosDB上做更多的性能优化。具体可以参见下节。

4、架构优化

CynosDB计算层的架构设计遵循了如下思路:

1.“极简IO”。即,降低网络/磁盘IO

\2. 高效的系统设计。异步的日志设计、降低计算层CPU负载

通过这些设计,使CynosDB的性能比云上的同等配置性能要高。本节主要介绍计算层所做的优化手段。

4.1 日志系统

兼容PostgreSQL版CynosDB的底层存储CynosStore是一个支持日志写的、可以提供多版本读的、分布式的块设备,DB引擎对存储中文件的修改,都是以日志的方式发送到存储中。其日志格式是:<页面号,页面偏移,修改内容,修改长度>,含义是:在页面的哪个偏移做了什么内容的修改。这样设计的日志是幂等的。

以表插入元组为例,PostgreSQL原来的XLog日志格式可能是:

<relfilenode, pageno, offsetnum,informask2,infomask,hoff,tuple_data>:代表在页面(由relfilenode和pageno来确定一个页面)的offsetnum位置插入一条元组,插入的元组是在恢复时由informask2, infomask, hoff, tuple_data等信息进行重构。

同样的操作,在CynosDB中生成的日志可能如下。假设在页面号为n的页面上插入元组tuple:

<n,10,(char *) &pd_flag,2> -- 保存页面头pd_flag到日志

<n,12,(char *) &pd_lower,2> -- 保存页面头pd_lower到日志

<n,14,(char *) &pd_upper,2> -- 保存页面头pd_upper到日志

<n,36,(char *) &ItemIdData,4> -- 保存ItemIdData数组的第3个元素到日志

<n,7488,(char *) tuple,172> -- 保存tuple到日志

这些条目记录了页面在插入元组时的所有修改,它们最终会在CynosStore Client中形成一个MTR(mini-transaction record:多条日志的集合,代表对数据库存储结构的一次原子修改,例如:btree结构、页面结构的修改;在日志重放的时候需要将一个MTR的所有日志都应用完毕,否则会导致数据库存储结构的破坏),并放到日志流中发送到存储。当存储需要将这个MTR合并到页面时,要保证MTR中的所有日志应用完毕,任何不完全的应用都会导致页面结构不正确。

利用日志特点,我们对PostgreSQL 的内核进行了优化,而优化之后的日志大小开销与PostgreSQL的原生XLOG差不多。这些优化和设计包括:

\1. 移除原本PostgreSQL中full page write(FPW)特性。为了保证系统crash再重启之后,那些部分写的页面(torn page)可以被正确恢复,PostgreSQL在Checkpoint之后,对页面执行第一次被修改时,会将整个页面记录到日志中,这种特性就是FPW,类似MySQL的double write。当crash recovery时,系统会以这个全页作为基页面进行日志回放,并将恢复好的页面写到存储,而不必关心存储页面中的页面是否是半页。由于CynosDB日志的幂等性,当出现半页写时,系统直接重新在此页面上直接进行日志回放,即可将页面修复到一致状态。因此CynosDB中无需原生的FPW,从而减少了日志量。

\2. 移除系统中脏页面刷盘操作。CynosDB通过日志保存页面的修改,并且可以通过在基页上合并日志而得到最新页面,因此无需原本系统的刷脏操作,仅仅刷日志就足够。

通过如上优化,可以很大程度上减少网络IO和日志量。

\3. 除了以上对PostgreSQL内核的优化,CynosDB对日志的记录方式也进行了精简和压缩。CynosDB的日志都有日志头(LogHeader),如果修改同一个页面的多条日志共享一个日志头,则可以省去多个日志头的开销,如下图所示:

LH代表LogHeader,Log Element代表对页面的页一次修改。如上图,有两条对Block1的修改日志,并且每个修改都有一个日志头(LH),经过日志头合并优化后,形成新的MTR中,修改Block1的那些日志共享了同一个日志头。

如果修改同一个页面的两条日志是相邻的,那么可以将两条日志进一步合并成一条日志。这种方式减少了日志条目,从而可以提高日志合并和页面生成速度。

4.2 页面CRC

在PostgreSQL中,页面在刷盘前会计算并填充页面的CRC属性,而在CynosDB中,如果为CRC也生成了一条日志写入到存储中的话,会增加计算节点的CPU负担和日志条数。为了解决这个问题,我们将CRC的计算任务下放到存储中,从而减轻了计算层的CPU负担,以及日志条数。

4.3 异步表扩展

原生的PostgreSQL数据库使用的是本地文件系统存储数据,其文件扩展操作同步并实时的反映到磁盘文件上。但是CynosDB的扩展操作是通过日志实现,如果每次扩展都对日志做一次flush操作,让扩展实时的反应到存储上,势必会影响系统的性能。因此,我们实现了文件的异步扩展,即文件扩展的日志先保留在系统的日志buffer中,而不是每次扩展都实时的刷新到存储中,当事务提交的时候再把这些日志刷到存储上,对数据批量导入的性能提升很明显。另外,扩展操作可以一次性在文件中扩展出多个页面,减少调用扩展操作的次数。

后续

后续我们会在新硬件、多Master架构等领域作更多探索,为云上的数据库产品形态带来更多惊喜和亮点。

此文已由作者授权腾讯云+社区发布


做优化的数据库工程师请参考!CynosDB的计算层设计优化揭秘的更多相关文章

  1. 硬件工程师必须掌握的PCB叠层设计内容

    总的来说叠层设计主要要遵从两个规矩: 1. 每个走线层都必须有一个邻近的参考层(电源或地层); 2. 邻近的主电源层和地层要保持最小间距,以提供较大的耦合电容; 下面列出从两层板到八层板的叠层来进行示 ...

  2. 优化MySchool数据库(一)

    <优化MyShcool数据库>:能够的独立的分析|设计|创建|运营|你的独立的数据库系统 设计--->实现--->TSQL--->查询优化---->性能优化技术-- ...

  3. [TcaplusDB知识库]数据库支撑底盘引擎计算层介绍

    在上次的TcaplusDB知识库中,TcaplusDB君为大家讲解了TcaplusDB所用的基于HASH表的Key-value存储引擎TXHDB.存储引擎作为数据库的支撑底盘,其重要性无可置疑,而在本 ...

  4. 【已解决】phpMyAdmin中导入mysql数据库文件时出错:您可能正在上传很大的文件,请参考文档来寻找解决办法

    期间,用phpMyAdmin去导入90M左右的mysql数据库文件时出错: 您可能正在上传很大的文件,请参考文档来寻找解决方法. [解决过程] 1.很明显,是文件太大,无法导入.即上传文件大小有限制. ...

  5. 要做linux运维工程师的朋友,必须要掌握以下几个工具才行 ...

    要做linux运维工程师的朋友,必须要掌握以下几个工具才行 ...  [复制链接]   发表于 2013-12-13 15:59 | 来自  51CTO网页 [只看他] 楼主           本人 ...

  6. 优化MySchool数据库设计之【巅峰对决】

    优化MySchool数据库设计 之独孤九剑 船舶停靠在港湾是很安全的,但这不是造船的目的 By:北大青鸟五道口原玉明老师 1.学习方法: 01.找一本好书 初始阶段不适合,可以放到第二个阶段,看到知识 ...

  7. SQL Server 数据库对象命名参考

    一. 引言 编码规范是一个优秀程序员的必备素质,然而,有很多人非常注重程序中变量.方法.类的命名,却忽视了同样重要的数据库对象命名.这篇文章结合许多技术文章和资料,以及我自己的开发经验,对数据库对象的 ...

  8. MySQL常用指令,java,php程序员,数据库工程师必备。程序员小冰常用资料整理

    MySQL常用指令,java,php程序员,数据库工程师必备.程序员小冰常用资料整理 MySQL常用指令(备查) 最常用的显示命令: 1.显示数据库列表. show databases; 2.显示库中 ...

  9. 从零到千万用户,我是如何一步步优化MySQL数据库的?

    写在前面 很多小伙伴留言说让我写一些工作过程中的真实案例,写些啥呢?想来想去,写一篇我在以前公司从零开始到用户超千万的数据库架构升级演变的过程吧. 本文记录了我之前初到一家创业公司,从零开始到用户超千 ...

随机推荐

  1. 全网首发! Odoo 订单分解执行及供应链管理免费开源解决方案

    引言 前一篇介绍了佛山王朝家具的案例背景.佛山王朝家具公司在全国有30多家门店,三个生产工厂.王朝家具有六大痛点问题: 订单迫切需要实现电子化管理及在线签名 总部分单工作量大,供应链效率低 配送和售后 ...

  2. [FromBody]与[FromForm]区别

    [FromBody]与[FromForm]区别 1,fromBody:在cation方法传入参数后添加[frombody]属性,参数将以一个整体的josn对象的形式传递. 2,fromform:在ca ...

  3. REST API disable / enable service auto start by API

    how to disable service auto start by API as the following how to enable service auto start by API as ...

  4. 一份.NET 容器化的调查小结

    小编在上个月在微信公众号"dotnet跨平台" 做了一个针对.NET 容器化的调查:https://mp.weixin.qq.com/s/oszbuIORT0G8XLLgMZzkn ...

  5. Scanner类详解

    Scanner类用于获取键盘输入(是一个基于正则表达式的文本扫描器),它可以从文件.字符串.输入流中解析出基本类型值和字符串值.Scanner类提供了多个构造器,不同的构造器可以接收文件.字符串和输入 ...

  6. Java的设计模式

    一.什么是设计模式: 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. ...

  7. springboot项目上传文件出现临时文件目录为空

    最近写文件上传到服务器读取的代码,前端使用FormData上传,服务端用MultipartFile接收,自己测试了下MultipartFile对象有什么东西,结果一般属性都能出来,测试getInput ...

  8. MySQL学习(一)日志与索引 --- 2019年1月

    1.MySQL的架构 1).连接器 先根据Ip和端口号,用户名和密码,连接MySQL数据库,连接后如果没有下一步动作,连接就处于空闲状态,此时有一个连接超时时间的设置 wait_timeout默认8小 ...

  9. shell打印 倒等腰三角形

    #!/bin/bash read -p "input the length: " n方法一:内循环中的一个for循环shell写法for i in `seq 1 $n`do    ...

  10. ArrayBlockQueue源码解析

    清明节和朋友去被抖音带火的一个餐厅,下午两点钟取晚上的号,前面已经有十几桌了,四点半餐厅开始正式营业,等轮到我们已经近八点了.餐厅分为几个区域,只有最火的区域(在小船上)需要排号,其他区域基本上是随到 ...