作者介绍:林锦,腾讯云数据库团队高级工程师,曾任云计算初创公司系统架构师,从事分布式系统研发7年,2017年加入腾讯云,从事NewSQL研发工作,目前主要负责CynosDB for PostgreSQL开发工作。
导语
CynosDB是新一代高性能高可用的企业级分布式云数据库,采用共享存储架构,作为腾讯云NewSQL数据库家族成员之一,由腾讯云数据库产品中心和TEG数据平台部联合打造,满足企业按需分配计算和存储资源,实现弹性调度,动态扩容,节约用户成本,将结合新硬件,新网络,全球分布,自动化等不断演进。本文简要介绍CynosDB for PostgreSQL架构,事务并发机制,缓存管理及数据加载,写数据流程,以及恢复等方面,后续将进一步补充相关信息,本文仅供参考,交流和学习,感谢您阅读!
前言
NewSQL 是一项系统工程,而系统工程是组织管理NewSQL系统规划,研究,设计,研发,测试和使用的科学方法,其综合计算机系统,软件工程,网络工程,项目及产品管理,安全,数据科学,机器学习,运维体系等方方面面,而架构是系统工程的蓝图,供系统在生命周期内的各个阶段(包括研发,管理,实施,运营和运维)提供参考和指导。NewSQL架构与现有系统架构并非完全不同,其中多数技术已存在于传统DBMS,其创新之处在于将这些技术重新梳理,统一整合到一个管理平台中,通过结合当前新硬件,新网络特点,达到高可靠,高可用,高性能,安全,自动化,充分利用资源的目的。
CynosDB 是新一代高性能高可用的企业级分布式云数据库,采用共享存储架构,作为腾讯云NewSQL数据库家族成员之一,由 腾讯云数据库产品中心和TEG数据平台部 联合打造,满足企业 按需分配计算和存储资源,实现弹性调度,动态扩容,节约用户成本,具有多租户,水平扩展存储,融合传统关系数据库、云计算与新硬件,新网络优势,100%兼容PostgreSQL。
CynosDB 架构
[ 图1 CynosDB 全局视图 ]
CynosDB 由两类数据库实例组成:
- 主实例(读写): 除查询外,还能执行数据库更新(包括写入Insert 修改Update 删除Delete),以及DDL和DCL操作,每个CynosDB数据库集群均有一个主实例,数据库实例 安装在独立的VPC,多个客户的数据库实例不会产生 计算资源(包括内存)的竞争,可理解为物理隔离 计算资源。
- 副本(只读): 每个数据库集群可拥有一个支持读写操作的主实例,以及多个副本,多个副本将均衡 客户应用程序的 读操作,还可通过将 副本置于单独的可用区中来提高数据库可用性。
每个数据库实例可以在1个腾讯云虚拟专用云(VPC) 和 3个专用网络(1个管理专用网 2个存储专网) 进行通信:客户应用程序通过 客户VPC 与数据库实例 进行交互,数据库实例 所在节点上的Agent 进程通过 管理专用网络 与 管控平台 进行交互,数据库引擎 通过 存储专用网络 与 存储服务 进行交互,而 存储服务 与 冷备存储系统 通过存储专用网络进行传输,各个专用网络 相互隔离,遵从 1987年,关系数据库设计者C.J.Date在《Distributed Database: A Closer Look》中提出12条规则的 本地自治 位置透明和独立性 可持续操作 硬件独立性 网络独立性 等原则。
[ 图2 CynosDB 架构 ]
CynosDB使用 腾讯云数据库运营平台 作为控制面板,由 实例管理器(Instance Manager) 向 资源管理器(Resource Manager) 注册申请一个对象池Pool,然后创建和部署 CynosDB 数据库实例,并在该数据库实例的 VPC 上安装一个代理(Agent)进程,负责监视数据库实例的运行状况,其根据实例运行情况进行故障切换或更换实例。 资源管理器(Resource Manager) 根据Pool注册信息初始化一个 称为 段组 SegmentGroup 的调度单元,根据调度规则选择最佳节点做为该数据库实例的物理存储,调度规则将参考Pool信息,根据 装箱算法 从可用区开始,依次筛选数据中心,机架,物理节点,磁盘容量,以及结合当前整个CynosDB 运行情况(IO 网络 容量),选择最佳节点。 存储管理器(Storage Manager) 负责管控 CynosDB物理存储资源以及备份和恢复数据需要的详细信息,对于长时间运行的操作,如存储节点故障后的数据库恢复或修复(重新复制)等操作,使用 异步机制 通过后端作业进行调度,为保持高可用性,指标采集服务持续监控存储操作的所有关键方面,积极主动,自动化的探测实际的和潜在的问题,如关键性能或可用性指标发现问题就会触发警报而引起关注。存储服务 部署在 访问管理CAM 上,并配置多个存储磁盘(SSD),存储节点之间采用RDMA技术进行数据的高效传送,存储节点维护本地SSD与数据库引擎实例,其他对等存储节点以及备份/恢复服务进行交互,备份/恢复服务把数据库物理日志持续备份到COS平台,并定期增量数据 备份到COS平台,这样可以按时间点进行数据的快速恢复。
CynosDB 特点:
- 可管理性: 一键式部署启动或停止计算资源和内存资源,计算数据库实例扩展操作通常在几分钟内完成,标准PostgreSQL 导入和导出工具与 CynosDB for PostgreSQL 配合使用进行迁移,可使用 实例管理器 查看有关数据库实例的关键运营指标,包括计算、内存、存储、查询吞吐量、缓存点击率以及活动连接等信息。可使用最新修补程序不断更新的数据库实例,通过数据库引擎版本管理,控制是否修补实例,以及何时进行修补。灵活的数据库事件通知机制,通过电子邮件,短信,微信等方式通知重要数据库事件,如自动故障转移,通过 实例管理器 订阅数据库相关的事件。
- 可扩展性: 计算按需扩展,而存储服务自动扩容,随着数据库存储需求的增长而自动提高数据库容量大小,其容量将以 10GB(不含存储副本)增加,最大可增加到 256TB,无需为数据库预配置多余的存储空间。
- 可靠性: 数据库实例上的Agent持续监控 数据库实例及其运行状况,发生数据库故障时,Agent将自动重启数据库及相关进程,而不需要对数据库重做日志进行崩溃恢复回放,从而大大减少启动时间。存储采用类似的监控机制来监控存储集群的监控状态,而存储副本缺失将自动修补,存储副本之间采用 Batch Pipeline 的 Raft库来保证存储副本数据的一致性,而存储副本分配控制 由 资源管理器 统一分配和调度,如副本数为 3,则允许一个副本丢失,内部自动修复。自动、连续、增进式备份和时间点恢复,能够将数据库恢复到保留期内任何一秒钟的状态,保留期可设置,如周,月,年等。
- 安全性: CynosDB在腾讯云 VPC 中运行,将数据库隔离在用户的虚拟网络中,并使用行业标准加密 IPsec V** 与用户本地 IT 基础设施连接,可以配置防火墙设置并控制对数据库实例的网络访问,CynosDB 与 访问管理CAM 集成,通过身份管理和策略管理控制资源(例如,数据库实例、数据库快照、数据库参数组、数据库事件订阅、数据库选项组)执行操作。
- 性能: 低抖动高吞吐,使用各种不同的软硬件技术组合,如将重做日志写入存储,异步合并数据页,数据库备份和重做恢复下推到存储系统进行异步化处理,从而确保数据库引擎充分发挥计算、内存和联网资源,写入操作可通过数据库引擎批量进行,同时存储系统基于 SPDK和RDMA 的零拷贝技术,减少操作系统上下文切换引起的性能损耗。
- 专业性: 由专业团队负责PostgreSQL数据库内核优化,跟进数据库最新动态,定期升级补丁。
CynosDB 数据模型
[ 图3 CynosDB 数据模型 ]
说明:
- Pool:资源池,每个Pool对应1个数据库实例,其由1个或多个Segment Group组成,每个Segment Group 由1个主Segemnt 多个副本Segment组成。
- Segment:数据管理(复制、迁移)的最小单元,每个Segment 大小固定(10GB)。
- Block:数据组织的最小单元(8KB),包含 结构PageHeaderData定义的页头数据(大小24个字节) 行指针数组(行指针长4个字节,并保存指向每个堆元组的指针) 元组Tuples(一条数据记录,从页面底部按顺序向空闲空间增长)
PageHeaderData 字段:
代码语言:javascript复制
pd_lsn: identifies xlog record for last change to this page.
pd_checksum: page checksum.
pd_flags: flag bits.
pd_lower: offset to start of free space.
pd_upper: offset to end of free space.
pd_special: offset to start of special space.
pd_pagesize_version: size in bytes and page layout version number.
pd_prune_xid: oldest XID among potentially prunable tuples on page.
CynosDB 存储模型
在1992年在LFS首次出现日志结构存储,通过只写数据页的变更来减少写放大,以REDO日志方式,跟踪事务确认回复最大的LSN,而CynosDB 存储 采用 日志写 块读方式 实现 日志结构的存储系统,对于写是日志流,而读操作支持MVCC的块操作。
[ 图4 CynosDB 存储IO ]
CynosDB IO 流:
- 存储节点接收日志记录并添加到内存队列。
- 通过CynosDB Raft 同步日志副本到 Peer 存储节点,成功后向主实例发送ACK确认,同步日志走SPDK/RDMA环境。
- 合并线程定期合并本地日志到数据,并备份到冷备系统。
- 定期将新页面存储到冷备系统。
- 周期性地回收旧版本的数据。
- 定期验证数据页面上的CRC码。
RDMA/SPDK 主要用于存储 数据块 传输,以及RAFT member 之间数据传输。
CynosDB 事务与并发
在RDBMS中采用并发控制保证数据的一致性和隔离性, 三种并发控制机制:Multi-version Concurrency Control(MVCC), Strict Two-Phase Locking (S2PL), 和 Optimistic Concurrency Control (OCC), PostgreSql 使用 Serializable Snapshot Isolation (SSI) 的 MVCC,新数据项将直接插入相关表页面,在读数据项时,通过应用 可见性检查规则 来选择合适版本的数据项来响应单个事务,使用SSI 进行DML(数据操作语言,例如SELECT,UPDATE,INSERT,DELETE),使用2PL进行DDL(数据定义语言,例如CREATE TABLE等)。
CynosDB 缓存管理
缓存管理器管理共享内存和对象存储之间的数据传输,包括 缓存表(buffer table), 缓存描述符(buffer descriptors), 缓存池(buffer pool) ,所有数据文件的每个页面分配唯一标记,即缓冲标记buffer_tag,buffer_tag包含三个值:RelFileNode,ForkNumber,BlockNumber。 如buffer_tag '(201808,0,9)' 表示第9个块中的页面,其OID和fork号分别为201808和0。参考 buf_internals.h
CynosDB 加载数据页
[ 图5 CynosDB 加载新页 淘汰算法]
假设访问的数据页不在缓存中,且当前缓存满,需淘汰页(如是脏页,则需刷新脏页),然后加载新页
- 根据要读取的数据页构造一个buffer_tag, 如(TAG_Q),然后通过内置的哈希函数 计算出 bucket slot,获取 BufMappingLock 中该Slot对应区域的共享锁,检查缓存表 中是否存在该数据页,没有该数据页,则释放 共享锁。
- 使用时钟扫描算法(clock-sweep)选择要淘汰页所在缓存池的Slot,从缓存表中获得旧的包含buffer_id的数据项,如“Tag_F,id = 5”,并设置该缓存描述符的状态为PIN。
- 如果该数据页不是脏页,则进入步骤4,否则需把该页刷新到对象存储,从该缓存描述符中获取 shared content_lock 和 exclusive io_in_progress lock,修改缓存描述符状态,设置 io_in_progress 位 为 '1', 而 valid 位为 0,通过XLogFlush() 把 WAL 缓存中的WAL 数据 进行 翻译 转换成 CynosDB 的SLOG(Segemnt LOG),然后并行提交 SLOG 到 CynosDB存储服务,然后修改缓存描述符状态,设置 io_in_progress 位 为 '0', 而 valid 位为 1,释放 content_lock 和 io_in_progress lock。
- 获取包含旧数据条目的 BufMappingLock 的分区,并设置为排他模式。
- 获取新数据条目的 BufMappingLock 的分区,并插入新数据条目(TAG_H, id=5)到缓存表。
- 删除旧数据条目,并释放 旧BufMappingLock 的分区。
- 从 存储服务 加载 数据页 到 缓存池中 对应的slot中,更新 该slot对应的缓存描述符 相关标志信息flags,设置 dirty bit 为0,并初始化其它 bit。
- 释放 BufMappingLock 中该Slot对应区域的排它锁。
- 后端进程读取 缓存池 buffer_id = 5的数据。
CynosDB 写流程
[ 图6 CynosDB 写入过程 ]
CynosDB写操作将所有 数据页或索引页 的修改(包括插入,删除或提交操作)作为历史数据SLOG写入持久存储,以应对故障,当事务提交/中止时,立即写到存储系统,以下是写数据 A 到TABLE_A 的过程:
- 接收到第一个INSERT语句时,CynosDB 在共享缓冲池创建空白页,然后在页面上写入元组'A',创建XLOG记录 写入LSN_1的 WAL缓冲区,同时创建 修改数据页的SLOG 保存在PLOG缓冲区(双向链表数据结构),然后将缓冲池中TABLE_A的数据页上的LSN从LSN_0更新为LSN_1,同时创建另一条SLOG保存在PLOG缓存区,后期CynosDB 将取消 WAL 缓存。
- 接收到提交事务命令,CynosDB创建提交操作的XLOG记录并写入WAL缓冲区,创建SLOG 写入PLOG缓冲区,然后将PLOG缓冲区中的所有SLOG记录 根据 映射规则 分发到 SLOG 缓冲区中指定的SegmentGroup区域,并锁定等待刷新结果,而刷新线程将 SLOG 批量成组(Batch Group Commit)发送 到 Store Node,等待Store Node 发送ACK响应,接收到响应后,取消锁定等待,完成该事务提交。
说明:关于 全页写,因后台写进程刷脏页时,由于机械盘故障导致数据页损坏,而且根据XLOG记录无法在损坏的页面上重放来恢复(可通过全量XLOG恢复,但代价极大),故PostgreSQL采用全页写方式来解决此问题,在每个检查点后,每个页面的首次更改时将整个页面作为XLOG记录,这种XLOG记录也称为备份块,CynosDB 对此进行优化,移除 全页写 和 Checkpoint.
CynosDB 恢复
传统的数据库都依赖于类似 ARIES 恢复协议来实现故障恢复,而CynosDB 使用状态机复制技术 State machine replication来避免故障恢复, 不需要REDO恢复,这是因为CynosDB 只会将已经提交的更新写入到存储。由于重做日志应用程序与数据库实例分离,交给存储层,在存储层以并行、异步、分布式的方式进行REDO操作,所以数据库可以很快恢复。数据库实例启动后,将与存储服务协同恢复以重建其运行时状态,首先从 资源管理器 获取该数据库实例所拥有的SG(Segment Group),计算出每个Segment的VDL,产生截断范围,消除VDL之后的日志记录(SLog的LSN大于VDL的记录),对该范围内的日志进行存储,产生新的VDL。
相关概念
事务日志: 在计算机科学数据库领域,事务日志(数据库日志,二进制日志或审计跟踪)是数据库管理系统用于保证ACID属性而执行的历史动作,保存在稳定存储中,记录一系列数据库变更的文件。当发生崩溃或硬件故障时,重启系统后发现数据库处于不一致状态或未正确关闭,则数据库管理系统将检查未提交事务日志并回滚这些事务所做的更改,所有已提交但尚未在数据库物化的事务则重新应用日志,两者都是确保事务的原子性和持久性。在PostgreSQL数据库中 XLOG 或 WAL 日志为 事务日志。
日志序列号LSN(Log Sequence Number): 是日志记录的唯一标识, 以单调递增顺序进行分配,这在 ARIES 恢复算法中很有用。
预写日志记录WAL(Write-ahead logging): 在数据库系统中,对于一个对象的任何更改,首先记录在日志中并保证其写入到稳定存储,然后将对象的更改写入磁盘,是提供原子性和持久性的一系列技术。
PostgreSQL事务ID(txid): 每当事务开始时,事务管理器就会分配一个具有唯一标识符的事务id(txid),txid是一个32位无符号整数,在事务启动后可通过执行内置函数txid_current()查看当前txid。由于实际系统txid空间不足,将txid空间当作一个圆,当达到最大值后,从新开始,循环使用。不会为BEGIN命令分配txid,当执行BEGIN命令后执行第一个命令时,事务管理器会分配tixd,然后启动其事务。
PostgreSQL 元组(Tuples): 由 t_xmin t_xmax t_cid t_ctid t_xvac t_infomask2 t_infomask t_hoff t_bitsFLEXIBLE_ARRAY_MEMBER USER DATA 组成, t_xmin 保存插入此元组时的事务txid, t_xmax保存删除或更新此元组的事务的txid。 如果尚未删除或更新此元组,则t_xmax设置为0,表示无效。t_cid保存命令id(cid),表示从0开始的当前事务执行多少个SQL命令。t_ctid保存指向自身或新元组的元组标识符(tid), 更新此元组时,此元组的t_ctid指向新元组; 否则,t_ctid指向自己。
可见性规则(Visibility check rules): 可见性检查规则是一组使用元组Tuples的t_xmin和t_xmax,clog以及获取事务快照来确定每个元组是可见还是不可见的规则。
提交日志CLOG(Commit Log): 在CLOG中保存事务的状态,CLOG被分配给共享内存,并在整个事务处理过程中使用, 事务的状态包括:IN_PROGRESS, COMMITTED, ABORTED, 和 SUB_COMMITTED.
事务快照(Transaction Snapshot) 事务快照是一个数据集,用于在单个事务特定时间点存储有关所有事务是否处于活动状态的信息。这里的活动事务表示正在进行中或尚未开始,PostgreSql内部将事务快照表示格式定义为“100:100:”, 表示'小于99的txids未激活,并且等于或大于100的txids处于活动状态'。
参考资料
- Michael Stonebraker.The Design of the Postgres storage system.EECS Department University of California Berkeley, Ca., 94720
- Abraham Silberschatz, Henry F. Korth, and S. Sudarshan, "Database System Concepts", McGraw-Hill Education, ISBN-13: 978-0073523323
- Dan R. K. Ports, Kevin Grittner. Serializable Snapshot Isolation in PostgreSQL.Proceedings of the VLDB Endowment, Vol. 5, No. 12. 2012
- The Internals of PostgreSQL
-- DONE --
更多前沿数据库技术和案例分享,请关注我们的微信号:腾讯云数据库CDB