《阿里云:PolarDB for PostgreSQL 开源必读手册(125页).pdf》由会员分享,可在线阅读,更多相关《阿里云:PolarDB for PostgreSQL 开源必读手册(125页).pdf(125页珍藏版)》请在三个皮匠报告上搜索。
1、封面页(此页面将由下图全覆盖,此为编辑稿中的示意,将在终稿 PDF 版中做更新)目录 开源 PolarDB for PostgreSQL 架构介绍.4 PolarDB 安装与配置.14 PolarDB 数据库结构.21 Foreign Data Wrappers(FDW)使用介绍.35 用户和权限管理.47 VACUUM 处理.65 缓冲区管理器.75 备份与恢复.82 共享存储原理与实践.95 云原生 HTAP.108 最佳场景实践与压测.120 开源 PolarDB for PostgreSQL 架构介绍 4 开源 PolarDB for PostgreSQL 架构介绍 一、PolarDB
2、 总体架构设计 传统数据库的部署方式,有主库、备库和 Standby,主备库之间通过流复制进行同步。节点扩展时,需要将数据全部进行复制,速度极慢。另外,主备之间复制一般使用异步复制,可能存在数据丢失。主备之间存在延迟,因此可用性较差。此外,随着副本数的增加,存储成本呈线性增加。针对以上问题,PolarDB 实现了计算存储分离架构。开源 PolarDB for PostgreSQL 架构介绍 5 在 PolarDB 架构中,共有三个节点,其中一个读写节点,两个只读节点。存储数据时,通过网络存储到后端存储池。该架构具有四个优势:第一,扩展性较好。计算能力不足时,只需简单操作即可增加计算节点。因为数
3、据存储在共享存储上,无需再做一次复制。且计算节点无状态,扩展快。而当计算资源过多时,可以将三个节点迅速缩为两个节点。第二,成本低。多个计算节点共享一份数据,存储成本显著下降。传统数据库有 N个备库,数据需要复制 N 份。而存储计算分离架构下,数据只需在共享存储上存储一份即可。第三,易用性。存储计算分离架构的存储池技术相对较成熟,保证了数据不会丢失。计算侧每一个节点都能看到完整的数据库状态,使用体验接近于单机数据库。第四,可靠性。由于共享存储具备了三副本以及秒级备份等特性,其可靠性也得到了保障。PolarDB 计算存储分离的模块栈分为四层。开源 PolarDB for PostgreSQL 架构
4、介绍 6 事务层:除了原生事务,还实现了 CSN 快照。日志层:主库将 WAL 日志写到共享存储上,备库无需再做一次流复制,从共享存储上读取日志即可。此外还实现了 lazy 回放、并行回放和 LogIndex 等核心数据结构。缓存层:实现了常驻 BufferPool,节点重启时,buffer 数据无需重新预热。另外,实现了多版本页面,解决了 fullpage 问题。存储层:实现了 Direct IO、数据预读、预扩展以及抽象了 PolarVFS 文件系统接口。PolarDB 除了实现计算存储分离架构,还实现了 HTAP 架构。PolarDB1.0 计算存储分离时,可以通过读写分离将 TP 事务
5、型查询均匀地打散到不同节点上。但该架构在处理 AP 型查询时存在一些问题,因为查询只能在计算节点上处理,无法发挥多个计算节点的能力。开源 PolarDB for PostgreSQL 架构介绍 7 因此,PolarDB 在存储计算分离架构上进一步实现了 HTAP 架构。如图中所示,在计算层实现了分布式并行计算引擎。任何一个计算节点均支持单机查询引擎,也支持分布式并行计算查询引擎。如上图,最左侧节点可用于处理单机 TP 型查询,用户可将业务中所有 TP 查询、点查发送到该节点。同时,分析性查询可利用多个计算节点的特性来完成计算(上图中的只读节点),四个节点基于 MPP 工作原理。最终,我们实现了
6、一套系统,既可以做单机点查、点写,也可以做多机并行计算引擎处理 AP 分析。以上架构实现了一体化存储,TP 和 AP 共享一份数据,用户将 TP 数据写到共享存储,AP 做分析时可以实现毫秒级的数据新鲜度。传统的解决方案下,TP 库到 AP 库之间的复制延迟非常长。另外,使用一份存储也减少了存储成本。其次,该架构将 TP 和 AP 做了物理隔离,可以将部分节点配置为负责处理 TP 查询,单机执行;然后将其他节点部署为分布式 MPP 执行,实现了 TP 和 AP 的物理隔离,甚至可以实现不同业务域运行在不同计算节点上,避免 AP 查询对 TP 查询的影响。另外,该架构也具备了 Serverles
7、s 弹性扩展能力,任何 RO 节点均可以发起 MPP 查询。传统 MPP 查询中存在一个协调节点,而 PolarDB 里每个节点均可看到所有数 开源 PolarDB for PostgreSQL 架构介绍 8 据以及元数据,所有节点本质上是对等的,因此任何节点都可以作为 MPP 查询的协调节点。同时,实现了SQL级别调整单机执行并行度以及SQL级别调整MPP执行节点范围。这意味着计算能力不足时,可以迅速增加计算节点。因此新增节点可以直接访问共享存储,对计算能力做扩展时,无需对数据做重分布。传统 MPP 统在新增节点时需要对数据做重分布,过程相当漫长,而 PolarDB 几乎可以实现秒级生效。另
8、外,如果存储容量不足需要增加机器,也无需再做扩容,因为PolarDB底层为分布式存储,存储池化后容量按需分配,可以认为容量无限大,无需担心存储容量不足的问题。HTAP 架构内置了两个优化器,其一为传统内置优化器,用于处理单机查询。其二为GPORCA 优化器,用于处理分布式查询。执行器层引入了大量算子。除了单机执行引擎所需要的算子之外,还需要对以上算子做并行化改造,比如支持 Shuffle 节点、支持对顺序扫描节点的并行化操作。事务层,PolarDB HTAP 完整兼容事务,执行 MPP 时完备兼容事务的可见性级别。PolarDB HTAP 实现了 SQL 全兼容,做了大量工作实现 SQL 特性
9、。开源 PolarDB for PostgreSQL 架构介绍 9 PolarDB 除了支持存储计算分离和 HTAP 架构,还支持三节点高可用架构。该架构为基于 X-Paxos 做流复制,同时可以将 PolarDB 部署在本地盘,在可用区内部通过X-Paxos 实现了低延迟系统。由于接入了 X-Paxos 协议,在某个节点宕机时可自动选择 leader 节点、自动做恢复、自动做集群节点变更。同时,借助 DataMax 既可以支持日志+存储的部署方式,也支持仅部署日志的方式,实现两地三中心的部署。如图所示,可以将可用区域 1 中的日志通过异步或同步方式复制到 Log Syncer 进程,该进程在
10、本地盘只存储了日志,并不存储数据,同时将WAL 日志向下游做复制,复制到另一可用区。从而既保证了可用性,又从成本上得到了进一步控制。开源 PolarDB for PostgreSQL 架构介绍 10 PolarDB 支持存储计算分离架构、HTAP 架构以及三节点高可用架构,可以通过不同的配置文件部署成不同的方式,三个架构为正交关系。如上图左侧,可以是云原生和 HTAP 混合使用的方式,业务可以根据自己的需求将TP 和 AP 流量分别发送到不同的计算节点,且只需一份存储。如上图右侧,可以借助X-Paxos将PolarDB以本地盘的方式进行部署,有一个leader和两个 follower,可实现高
11、可用。同时,本地盘的方式也可以支持 HTAP 的业务负载,比如可以将 TP 查询发送到 leader 节点上,同时将 AP 查询发送到 follower 上。且多个 follower 节点和 leader 节点可以组织成分布式查询,解决了传统 TP 数据库主备方式做分析时计算能力扩展的问题。二、PolarDB 企业级特性 PolarDB 还实现了企业级特性。架构方面,支持存储计算分离架构、HTAP 架构以及三节点高可用架构。开源 PolarDB for PostgreSQL 架构介绍 11 1.性能方面 实现了 CSN 快照用于解决单核场景下,随着核数增加其性能线性扩展的问题。实现了 WAL
12、Pipeline 功能,加速 WAL 日志的写入,提高写入吞吐量。实现了预读和预扩展功能,一般做分析查询时需要大量扫描,而预读功能可尽可能地发挥共享存储大带宽的特性。实现了 RelSizeCache,查询时首先需要得到文件大小,此功能实现了对文件的元数据做缓存。实现了 CLOG 优化以及 FullPageWrite 优化,主要为通过 LogIndex 以及页面多版本彻底避免 FullPage 的问题。FullPage 在 PolarDB 里有两种解法。?解法一:如果共享存储提供了 8k 或 8k 以上的原子写,可直接将 FullPage关闭。因为共享存储是软件定义的存储,其原子写可以大于硬件的
13、页面单元。?解法二:如果共享存储原子写在 8k 以下,可以使用页面多版本,将 FullPage内容从 WAL 日志剥离,即可大幅减少 WAL 日志容量。2.高可用方面 实现了 DataMax,DataMax 指 logger 节点,可以通过配置文件将 PolarDB 部署成只存储 WAL 日志,不存储数据页面。再配合 X-Paxos 即可实现两地三中心的部署架构。实现了 Online Promote,原生做 HA 切换时需要重启,而我们通过 Online Promote 实现了在线将备库切换为主库,进程无需重启。实现了延迟回放和并行回放,能够降低主备之间的复制延迟,经过测试,在高压力情况下,可
14、实现毫秒延迟。实现了常驻 BufferPool,数据库 BufferPool 做重启时,BufferPool 的内存会丢失,导致数据库重启后需要花费长时间做预热。而常驻 BufferPool 将 BufferPool内容剥离,放至共享存储,不会随着进程重启被销毁,维护了 buffer 的可用性。实现了 Replication Slot 持久化功能,能够避免备库变成主库之后 replication slot 的丢失。开源 PolarDB for PostgreSQL 架构介绍 12 实现了算子级别内存控制。执行分析性查询时,某些算子会占用大量内存,导致内存膨胀,最终导致 OOM。而算子级别内存控
15、制可以精细控制每个算子的内存使用上限。安全方面,实现了 TDE 透明加密功能。支持 AES 128、256 以及国密 SM4 算法。三、PolarDB 开源社区 PolarDB 在 2021 年云栖大会之前对所有内核代码全部做了开源,包括 PolarDB 内核、PolarDB 分布式文件系统以及 PolarDB 云管控,我们始终坚持 100%兼容社区的 PostgreSQL。开源代码与公有云上代码一致,经过了云上客户以及内部测试人员的大量验证,代码质量极高,用户可以直接将其部署在自己的生产环境中使用。开源 PolarDB for PostgreSQL 架构介绍 13 PolarDB 在开源的同
16、时,还提供了丰富的文档和视频资料,比如整体架构文档介绍、核心功能原理解析、快速入门文档以及每周的钉钉群 PolarDB 内核原理解析。PolarDB 安装与配置 14 PolarDB 安装与配置 PolarDB 的安装软件在 GitHub 上,因此安装前需要先注册 GitHub 账号。虚拟机安装 PolarDB 时,为了能够更好地继承 postgres 数据库的使用习惯,建议使用postgres用户进行安装,并为该用户赋予sudo和工作目录权限。安装完以后,执行上图中的代码。PolarDB 安装与配置 15 此时需要注意目录的权限。创建用户时,默认会在 home 目录建 postgres 目录
17、,确认目录的属主为 postgres。执行上图命令,继承环境变量,配置 sudo 使用资源的限制。下载安装 GitHub 软件,并在本地添加登录的用户名和邮箱,最后通过 git config 命令查看是否添加成功。添加成功显示的结果 在 GitHub 上添加 SSH 需要 RSA 密钥。PolarDB 安装与配置 16 密钥在 Linux 主机上产生,执行上图命令,其中 为注册邮箱名。产生RSA 密钥后,需要将密钥粘贴到 GitHub 配置里。如上图所示,选中的即为 RSA 密钥。PolarDB 安装与配置 17 登录 GitHub,点击进入 Settings。点击 SSH and GPG k
18、eys。输入 rsa 以及密钥,点 Add SSH key。PolarDB 安装与配置 18 下 载 软 件。下 载 完 以 后 进 入 到 PoalarDB-for-PostgreSQL 目 录 下 执 行sudo./install_dependencies.sh 脚本,用于下载 PolarDB 在安装过程中需要的系统软件包。下载完后进行编译。需要通过$source/etc/bashrc 更新环境变量,否则编译可能会报错。然后进入目录,执行编译,系统会初始化一个实例。PolarDB 安装与配置 19 部署完以后,通过以上命令进行实例检查和测试,确保部署成功。返回上图结果则说明部署成功。Pol
19、arDB 安装与配置 20 为了方便后面的使用,还需在 postgres 用户下添加环境变量。添加 PG_HOME 以及 PGDATA 的路径,并声明端口。其中 tmp 为阿里开发的文件系统,可以理解为共享存储,可以被多个节点访问。这也意味着节点启动实例时,无需复制拷贝,直接访问节点上的数据即可。然后配置用户名、访问的主机以及数据库。完成以上配置后,通过$psql 即可登录到 PolarDB 数据库。如上图所示,实例一级对本地的用户登录是信任的,因此登录时无需提供密码。PolarDB 数据库结构 21 PolarDB 数据库结构 PolarDB 由众多数据库组成,因此称为数据库集群,每个数据库
20、下存放各自的对象,对象包括表、索引、视图、存储过程、序列等。登录到数据库以后,可以通过l 查看数据库信息。PolarDB 数据库结构 22 PolarDB 提供了两种 SQL 语句,一种是标准的 SQL 语句,另一种是 PSQL。PSQL 使用 l 来代替 select pg_database 语句。可以通过?查看 PSQL 提供的所有命令。PolarDB 管理对象时通过 oid(对象标识符)进行管理,它与数据库物理结构的名字一一对应,数据库 oid 和堆表 oid 分别存储在 pg_database 和 pg_class。进行数据库访问时,PGDATA 变量指定了整个数据库的基本位置。下面的
21、 base 目录是所有数据库的副目录,其下的子目录都以数字命名,与每个数据库的 OID 对应。PolarDB 数据库结构 23 比如查找当前数据库的物理位置,需要先找到 PGDATA 目录所在。查看 base 目录,即可看到它以数字命名的子目录。PolarDB 数据库结构 24 子目录名称与数据库创建目录的 OID(上图)一致。上图为 PGDATA 下不同目录和文件的作用。pg_hba.conf 用于控制实例的访问权限,可以在文件里定义哪一些主机可以访问哪一 些 数 据 库;postgresql.conf 是 PolarDB 数 据 库 的 主 要 参 数 文 件,postgresql.aut
22、o.conf 是二进制的参数文件,两者可以结合使用。PolarDB 代理启动时先读 postgresql.conf 文件,再读 postgresql.auto.conf 文件。PolarDB 数据库结构 25 上图为数据库集群布局的主要文件和子目录。数据库是 base 子目录下的子目录。初始化时,表的 OID 与数据文件名字一致,但 TRUNCATE、REINDEX 等操作会造成不一致。如上图所示,查询 sampletbl 表所在的数据文件。PolarDB 数据库结构 26 首先,登录到 sampledb。sampledb 数据库的 OID 为 32768。到 sampledb 目录下查找文件
23、 32769,可以发现实际的数据并没有存放在该路径下。因为 PolarDB 将数据存放的位置做了分流。真正的数据放在上图路径的 32768 子目录下。PolarDB 数据库结构 27 PolarDB 为了方便查找表的数据文件位置提供了查询函数,如上图。此处的 file-dio为 PolarDB 提供的共享存储。随着表的数据越来越多,文件尺寸超过 1GB 后,命名规则也会有所改变,会在原有名字后加上.1 以示区别。fsm(free space map)是空闲可用空间地图。比如有多个数据块,往表里插入数据时,需要明确哪个数据块里有空间可用于存放新插入的数据,PolarDB 会先查看 fsm文件。v
24、m 是可见性地图,主要用于对表空间进行整理,帮助 vacuum 时提高效率。数据文件的 fork 号为 0,空闲空间映射的 fork 号为 1,可见性地图的 fork 号为 2。PolarDB 数据库结构 28 PolarDB 也支持自建表空间(目前仅在企业版 PolarDB 支持)。创建新的表空间后,PGDATA 下的 pg.tblspc 目录下存在与新建表空间名一致的目录。它 是 一 个 指 针,指 向 创 建 表 空 间 时 的 物 理 位 置,数 据 存 放 于/home/postgres/tblspc 上。PolarDB 默认有两个可用的表空间,pg_default 和 pg_glo
25、bal。默认情况下使用pg_default。PolarDB 数据库结构 29 表数据文件内部被分为多个块,默认 8k,每个块都有唯一 ID。上图右侧为数据块的结构。块头里面存放了控制性的信息,比如常见的行指针。存放数据时,顺序为从下往上存放,而块头的信息为从上往下存放,中间区域即可用空间。一行数据插入到数据块中,首先,数据会进入块的底部,块头产生指针,指向行开始的位置。插入第二行数据,则会由下往上叠加,并在块头产生新的指针。PolarDB 数据库结构 30 访问表的方式有两种:顺序扫描:即全表扫描。比如有两个数据块,PolarDB 在访问时会先找到第一个块的位置,并从第一行开始进行完整扫描;如
26、果没有,则进入第二个块的第一行继续扫描,以此类推。索引扫描:根据索引行里记录的 rowID 进行扫描,无需全表扫描。启动 PolarDB 数据库时,会先启动实例,实例由进程和内存组成。postgres server process是PolarDB的父进程,所有后台进程、后端进程等都由父进程派生。backend process 进程负责处理用户请求,background process 用于管理整个数据库。PolarDB 数据库结构 31 Backend process 负责客户端请求。客户端访问数据库时,并不会直接与数据库交互,而是将所有请求发给 Backend 进程(相当于代理进程),由它再
27、向 server 进程或后台进程发出请求。执行结果也由 Backend 进程返回给用户进程。Prostgers进程通过pg_ctl进行启动。启动以后,默认会有一个网络监听端口5432,也可以在配置文件中修改监听端口。如上图,当前进程名为 postgres,父进程下派生了很多子进程。PolarDB 数据库结构 32 Backend 进程与用户进程一一对应,他们之间的连接方式被称为专用连接。传统的PG 数据库只支持专用连接。而 PolarDB 在此基础之上又开发出与 Oracle 类似的共享连接,后续该能力也将开源,由 max_connections 参数控制数据库允许的最大连接数。上图为 Pol
28、arDB 后台进程的功能。写进程负责将PolarDB数据缓冲区的脏块写到数据文件。检查点进程用于做检查点,同时也负责写。PolarDB 数据库结构 33 上图即父进程派生出的子进程。PolarDB 的内存结构分为两种,分别是本地内存和共享内存。本地内存为每个backend 进程启动时自动分配,共享内存为整个实例提供服务。PolarDB 数据库结构 34 本地内存分为以下三种类型:work_mem:执行 SQL 语句时,如果涉及到排序操作、表连接等,则会使用工作内存。maintenance_work_mem:主要用于 vacuum 或者 reindex。temp_buffers:用于存放临时表。
29、本地内存类似于 Oracle 的 PGA,server 进程相当于 backend,PGA 相当于本地内存。共享内存分为以下几种类型:shared buffer pool:修改数据文件的数据块时,会读到数据缓冲区里进行修改。WAL buffer:进行事务操作时,会分配一个日志缓冲区。commit log:PolarDB 有专门的进程记录提交的事务信息,因此会分配 commit log 缓冲区,为整个实例提供服务。Foreign Data Wrappers(FDW)使用介绍 35 Foreign Data Wrappers(FDW)使用介绍 FDW(Foreign Data Wrapper)是
30、PolarDB 数据库提供的与异构数据库进行数据交流的方式,可以通过 FDW 对异构数据库进行数据访问,类似于 Oracle 的 DBLink,但 FDW 的功能更强大。当前,数据库国产化是一个大趋势。将数据从 Oracle 迁移到国产数据库,通过 FDW可轻松实现。但很多开发者在原来的 Oracle 数据库上使用了包存储,迁移时涉及到应用、开发上的修改,难度极大,甚至可能还需要开发新的适合国产数据库的业务。FDW 的访问与本地访问一致,不管远程数据库是 PG、MySQL 还是 Oracle,通过select count(*)From 即可实现访问。Foreign Data Wrappers(
31、FDW)使用介绍 36 上图为 FDW 的执行流程。SQL 语句执行前需要进行解析,访问 FDW 表也同理,PolarDB 会对 SQL 进行解析。数据表放在另外的数据库,因此 PolarDB 需要查询数据字典,确认当前表的创建方式,判断其是否为外部表。如果是,还需查看外部表所在的服务器以及用户 mapping。随后,产生执行计划,再将要执行的 SQL 语句传到远程服务器,最后将结果返回到本地。总结来看,FDW 的执行流程分为以下几步:a)创建查询树。b)连到远程服务器查看是否存在表及其权限。c)本地优化器创建执行计划。d)根据优化器的执行计划再还原成文本的 SQL 语句,将 SQL 语句传到
32、远程。不同的异构数据库语法存在差异,此处会转成异构数据库能够兼容的语法。e)远程服务器执行 SQL 语句,将结果返回给本地。Foreign Data Wrappers(FDW)使用介绍 37 Deparesing 是将执行计划做反解析,变为 SQL 语句。FDW 从本地将语句传到远程,远程执行完以后再将数据传回到本地。Foreign Data Wrappers(FDW)使用介绍 38 上图为远程服务器上的日志。执行外部的 SQL 语句时,会将事务变为 repeatable read 隔离模式,然后声明一个游标,绑定游标执行,执行后将数据返回,关闭游标,结束事务。发请求时,远程服务器有可能被修改
33、,因此我们将事务变为可重复读的隔离级别。随着版本的更新,FDW 的功能越来越强大,性能越来越好。FDW 最早在 PG9.3 版本推出。PG9.6 新增了以下特性:如果 SQL 语句带有排序,则会在远程排序以后将数据返回到本地,省去本地排序的操作,提高了性能;如果两张表的连接都为远程,则会在远程将两张表的数据进行连接,连接后将处理结果传到本地;如果数据库是 PolarDB对 PolarDB,则可支持 DML 操作;可以根据网络带宽的情况设置返回的行数,减少对网络带宽的占用。PG10 版本中,新增的特性为:如果操作里带有聚簇函数的操作,则聚簇的计算在远程完成,将结果返回到本地,省去了将元数据传到本
34、地再排序所占据的网络带宽。Foreign Data Wrappers(FDW)使用介绍 39 如果两个数据库支持 update,比如 PolarDB 对 PolarDB,用 FDW 对远程的表进行更新时,可以实现不同事务修改同一张表的同一行。而按照以往的情况,该种场景下会造成死锁。FDW9.5 之前的版本中,多表扫描需要从远程服务器扫描数据,然后传到本地,在本地做连接操作。Foreign Data Wrappers(FDW)使用介绍 40 而 FDW9.5 以后,打开 use_remote_estimate,计划器将通过执行 EXPLAIN 命令向远程服务器查询计划的成本。做多表连接时,连接操
35、作在远程服务器执行,执行完以后将数据传到本地,极大减少了数据在网络之间的传输。上图为 FDW9.5 之前做外部连接的流程,代价较大。上图为 FDW9.5 后的外部链接流程,打开 use_remote_estimate 后,连接在远程完成,极大提高了查询性能。Foreign Data Wrappers(FDW)使用介绍 41 在 9.5 版本之前,排序也需要从远程将数据传到本地,然后在本地做 sort 操作。而 9.5 之后的版本中,排序操作被下推到远程服务器,排序完成后将结果返回至本地即可。Foreign Data Wrappers(FDW)使用介绍 42 聚合操作同理。在 10 版本之后,本
36、地不再做聚合,而是由远程服务器来完成。综上,新版本的 FDW 性能得到了较大的提高。部署演示 Postgres_FDW 部署 首先,添加扩展,安装外界PolarDB扩展。创建server并为其命名,传入远程PolarDB名、服务端口以及要访问的数据库名,用于连接远程服务器。远程服务器上应配置好权限,即允许哪台客户端访问服务器。通过des 命令可查看当前有哪些外部服务器以及对应的主机信息,如上图所示。Foreign Data Wrappers(FDW)使用介绍 43 注意,因为 PolarDB 由很多数据库组成,在 create FDW 时,在哪个数据库上部署FDW,则 FDW 只能在该数据库上
37、使用。创建用户并为其授权,使其有权创建外部表,然后创建用户映射。有了用户映射后,比如远程服务器是 postgres 用户,本地的 scott 用户也可对其进行访问,即能够以不同的用户来访问不同表的数据。创建外部表,创建时后面需要跟上外部的服务器、schema 以及远程表的名字。Foreign Data Wrappers(FDW)使用介绍 44 通过d 查询,当前 scott_pg 用户下有 dept_fdw 和 emp_fdw 两个外部表,访问外部表与访问本地表没有任何区别。可以通过ds+emp_fdw 查看该表,结果会显示它为外部表以及它的访问模式、远程服务器等信息。File_FDW 部署
38、Foreign Data Wrappers(FDW)使用介绍 45 首先,添加扩展,创建本地插件,创建基于本地 FDW 的 server,将权限赋予相应的用户。创建外部表。因为需要引用本地某文本文件里的数据,因此后面需指定 file name 即数据源路径,并指定格式为 CSV,代表用逗号隔开列与列之间的数据。创建成功后,查看该表。/home/postgres/emp2.csv 文件的产生有两种方式:其一,如果是外部的文本文件,采集时将列与列之间用逗号隔开即可。其二,如果是自己创建的外部表,通过上图语句将 emp 的内容进行复制即可。Foreign Data Wrappers(FDW)使用介绍
39、 46 最终,用户可以像访问内部表一样访问外部表,非常方便。此外,基于文件的外部表为只读,不能做 DML 操作。用户和权限管理 47 用户和权限管理 PG 数据库下的用户指用来访问、管理数据库中的对象,角色用于管理数据库访问权限,简化权限的管理,同时也兼任用户功能。用户和角色的概念非常接近,如果给角色分配了可登录的权限,角色也可以与用户一样登录并创建对象。因此,在 PG 数据库下,用户和角色可以等同看待。用户和角色在整个数据库中是全局的,登录到某数据库时,创建的用户不只针对数据库里的用户,而是针对整个集群的数据库。数据库中的用户可以分为两类:超级用户:安装完 PolarDB 以后,默认的用户即
40、超级用户 postgres,拥有所有权限。普通用户:根据需要创建。另外,group 指不拥有 replication/noreplication、connection limit 属性的角色。用户和权限管理 48 创建用户有两种模式:方式一为在操作系统下创建数据库的用户,不需要登录到数据库。方式二为登录到数据库进行创建。方式一的后台处理时也需要登录到数据库进行操作,只是登录操作由后台负责,为使用者简化了流程。如果在数据库里创建用户,创建时即可为其分配不同的权限,比如特指定是否为超级管理员、是否有创建数据库的权限、是否有创建角色的权限、是否有创建用户的权限、用户能否继承所拥有的权限等。此类权限为
41、特殊权限,无法使用 grant 进行授权。用户和权限管理 49 上图为创建用户示例。需要注意,Login、Superuser 和 Createrole 三个权限无法被继承。创建角色的语法与创建用户的语法基本一致。角色和用户最大的区别在于,角色默认没有 login 的权限,如果有了 login 的权限,则角色等同于用户。用户和权限管理 50 上图为创建角色示例。我们通常用角色对权限进行间接的授权。创建完用户和角色以后,可以用du 指令显示用户和角色的属性。“角色名称”一列下包含了用户和角色,此处对用户和角色进行了统一对待,同时展示了属性以及属于哪个角色或权限。用户和权限管理 51 除了du 以外
42、,也可以通过数据字典访问用户/角色信息,比如通过 pg_user 或pg_rules。查询结果显示为上图形式。如果要修改用户的属性,比如权限、名字或密码,可以用h alter user 命令。用户和权限管理 52 上图为修改用户属性示例。修改角色的属性可以用h alter role 命令。用户和权限管理 53 上图为修改角色属性示例。用户与角色的删除有两种方式。方式一:通过 dropuser 命令删除用户。删除时如果没有设置信任关系,则需要指定登录的用户名、密码,且此用户名必须拥有创建用户的权限,方能登录到数据库执行 dropuser 命令。方式二:在 psql 命令行使用 drop 删除。删
43、除时需要附上判断用户/角色是否存在的语句,避免用户/角色不存在而产生报错。尤其是脚本里,如果产生报错,则会影响后续的执行。注意,只有超级用户能够删除超级用户,只有具有 createrole 权限的用户才能删除非超级用户。且删除用户前,需要删除依赖该用户的对象、权限等信息,或将权限授权给其他用户,以保证对象的安全性。PG 不支持 cascade 此类级联的删除方式。此外,当前登录的对象也无法删除,需退出登陆后再做删除操作。用户和权限管理 54 比如,删除 u1 时需要先通过d 命令查看其关联的对象。而除了对象以外,还需要通过dn 查看其是否存在相关 schema。如上图,显示 u1 下还存在一个
44、 sport schema,因此,需要通过 drop schema sport 将该 schema 删除。再次执行删除 u1 操作,提示在 testdb 上还存在相关权限。需要通过 revoke all 命令将授权给用户在 testdb 数据库上的所有权限撤回,随后即可删除用户。为角色授权以后,如果要使用角色里的权限,需要通过 set role 命令启用该角色。如上图所示,启用角色前,不允许创建 schema。而执行 set role 命令后,schema 创建成功。用户和权限管理 55 每个数据库对象都有一个 owner,owner 默认情况下拥有该对象的所有权限。数据库中所有权限都与角色挂
45、钩。对超级用户的权限不做检查,其他用户需要通过 ACL。对于数据库对象,所有者和超级用户可以做任何操作,其他用户需要通过 ACL。PG 数据库下的权限管理可以分为几个层级,分别为实例权限、数据库权限、表空间、schema 权限以及 object 对象(表、视图、索引)。用户和权限管理 56 实例级别的权限主要通过 pg_hba.conf 控制实例访问的隔离级别来实现。该文件存在于 PGDATA 目录下,每一列分别为类型、访问的数据库、访问的用户、客户端地址以及访问方式。如上图,第一条信息中的 host 代表主机,all 代表所有数据库和用户,127.0.0.1 指本地地址,trust 指信任,
46、意味本地用户登陆无需用户密码。第二条信息中的 0.0.0.0 指所有外部主机,意为拒绝外部任意主机以 postgres 用户登录。第三条信息中的 md5 指通过用户和密码登录。上图第一行代表任何用户访问任何数据库时都需要密码访问。设置完以后通过reload 重新加载方可生效。访问方式如上图所示。PolarDB 数据库上,只要用户有 login 权限,即允许所有用户连接到数据库。另外,不允许除了超级用户和 owner 以外的任何人在数据库中创建 schema。系统会自动创建名为 public 的 schema,允许任何人在里面创建对象。用户和权限管理 57 比如创建一个新的数据库名为 newdb
47、1,则所有用户都可以登录,上图为以 u2 用户登录,而且可以在数据库上创建表。如上图,创建了 t1 表,其 schema 属于 public。因此创建了数据库以后,默认数据库下存在 public 模式的 schema,u2 用户即可以 public 模式创建 t1 表。上图语句实现了允许用户在指定的数据库下做任何操作。用户和权限管理 58 如上图,u2用户想要创建一个schema被拒绝。因为数据库上已经存在一个schema。而执行 grant create on database newdb1 to u2 语句后,u2 即可在数据库上创建schema。创建完后,可以看到该数据库下存在两个 sc
48、hema 模式,分别是 public和 s1。模式是指某个用户下所有对象的集合。在 Oracle 下,用户与模式一一对应。比如“scott 用户下的 emp 表”严谨的表述方式应为“scott 模式下的 emp 对象”。而 PolarDB 下,一个用户可以有多个模式。比如 scott 用户下有 sport 和 art 两个模式,其中 sport 模式下有篮球、羽毛球、乒乓球三张表,art 模式下有钢琴、小提琴、手风琴三张表。如果不指定模式,则 scott 下的所有表都属于 public 模式。如果想要沿用与 Oracle 一样的模式,在用户下创建一个与用户名一致的模式即可。用户和权限管理 59
49、 实现方式如上图所示,先创建两个模式。创建表的时候,如果希望将篮球表分至体育模式下,则创建语句写为 create table sport.lanqiu()。分别在两个模式下建了不同的表以后,用d 查看会发现无法查询。因为默认情况下PolarDB 的搜索路径是与用户名一致的模式以及 public 模式。因此,需要将 search_path 加上 sport 和 art,方可成功查询。用户和权限管理 60 如果要将某个模式下表的数据给某个用户访问,首先要先将模式的访问权限赋予用户,再将表的访问权限赋予用户。如上图,对 u3 赋予 select 权限后,访问 art 模式下的 gangqin 表,显
50、示访问被拒绝。因为该用户没有访问 art 模式的权限。对 u3 赋予 usage 权限后方可访问。综上,如果访问特殊模式,不是 public 模式,授权时需要分两步:首先,将模式的权限赋予用户,其次,将模式下对象的权限赋予用户。用户和权限管理 61 数据字典 namespace 里包含了所有模式的名字以及模式下分配给用户的权限。上图为为对象级别赋予权限的语法。用户和权限管理 62 比如要使用 u3 访问 u2 下的 t1 表,需要执行 grant select on t1 to t3。如果该表不属于 public 模式,则需要先将模式的访问权限赋予用户,再将模式下的对象权限赋予用户。上图为对象
51、级别的权限示例。上图为表的权限含义。用户和权限管理 63 将权限赋予用户以后,可以通过 psql 的z 或dp 查看查看当前有哪些可访问的权限。查询结果显示如上图。比如 u3=r/u2 代表 u3 拥有 r 的权限且属于 u2。执行 grant all on t1 to u3 将 t1 的所有权限赋予 u3,然后通过z 查询,结果如上图,u3=arwdDxt/u2,代表 u3 拥有了所有权限。撤销权限可通过 REVOKE 语法,示例如上图。用户和权限管理 64 比如,执行 revoke all on t1 from u3 后,结果如上图,原先 t1 赋予 u3 的权限全部被撤销。VACUUM
52、处理 65 VACUUM 处理 vacuum 的工作内容为空间清理,最主要的工作内容为将数据块中被删除的行的空间进行释放。比如当前的数据块存在两行数据,用户对最下面一行数据做了修改,PolarDB 的操作方式不是在当该行进行修改,而是将该行标识为删除,然后在数据块里重新插入修改后的新行。被标识为删除的行不会立刻释放空间,而是需要由 vacuum 来做释放操作。另外,vacuum 还负责冷冻老的 Txid 以及收集表的统计信息,为优化器提供可用信息做参考,并且更新表的文件。VACUUM 处理 66 vacuum 的操作过程分为三部分。第一部分,准备创建死元组列表。需要从各个数据库里搜索哪些表达到
53、了整理的条件,然后将这些表组成列表。第二部分,清理阶段。将列表里包含有被删除的行的空间进行释,同时更新 FSM 和VM 两个文件。第三部分,善后工作。如果最后一页没有元组,则将其删除,以减少数据文件的尺寸。如果需要,同时会将不必要的 Clog 删除。比如山图中第一个数据块内有三行,其中有一行已被删除,PolarDB 将其标识为dead tuple。做完 vacuum 以后,Tuple_2 的空间被释放,剩余两行被整理后更加紧凑。同时,被删除行的指针没有删除,插入新行后,该指针会指向新行,减少了维护操作,提高了效率。VACUUM 处理 67 做 vacuum 时,正常情况下需要将表的所有数据扫描
54、一遍。为了提高效率,PolarDB使用了 VM(可见性地图)数据文件。如上图,比如当前有三个数据块,第二块里不包含被删除的行,则 VM 会将其标识为 1。后续做 vacuum 时,会跳过 VM=1 的页,提高 vacuum 的效率。9.6 版本以后,可见性地图除了提高 vacuum 的效率以外,同时也提高了冻结的效率。数据库为了描述事务操作的先后顺序,会为事务分配 ID 号,即 TxID。VACUUM 处理 68 TxID 不会无限增大,而是循环使用。最大的可用事务 ID 为 42 亿,PolarDB 将其分为两半,前 21 亿代表“过去的或当前正在用的”,此类事务 ID 修改的行为对用户可见
55、;而后 21 亿事务 ID 代表“未来的”,修改的函数不可见。两个部分的 21 亿事务 ID 可以循环使用。比如前面的 21 亿使用完以后,再用后面的 21 亿,使用完以后再重新使用前面的 21 亿。冻结主要针对可见性规则。比如当前正在使用后 21 亿的 TxID,而前 21 亿中某个数据块里仍有数据,需要对用户可见。因此,将其标记为冻结,使得其可见。冻结处理分为懒惰模式和急切模式。懒惰模式指每次小部分、分批次地进行冻结,类似于日常做卫生;急切模式指大批量地冻结,类似于年终大扫除。惰性冻结的公式中,OldestXmin 指当前最小的事务 id,vacuum_freeze_min_age是一个固
56、定参数,默认为 5000 万。VACUUM 处理 69 以上图为例,假设当前的最小事务 id 为 50002500,意味着要将小于或等于 2500的 ID 都冻结。冻结时,首先会先判断 VM 值,如果 VM 为 1(当前数据块内不存在被删除的行),则跳过,不对其进行冻结。然后判断每一个块内每一行的事务 id,如果 id2500,则跳过。比如上图中 Turple9 内最后一行 id 为 3000,因此不冻结。急性冻结的触发条件为:自上一次急性冻结后,TxID 使用了 1 亿 5000 万后会再次触发。VACUUM 处理 70 如上图,如果从未做过急性冻结,则 datfrozenxid 默认为 5
57、60。发生急性冻结后,datfrozenxid 会变为该次急性冻结的 TxID。如上图所示,当前最小的事务 id 为 150002000,150002000-5000000=100002000,因此事务 id 小于 100002000 的行全部进行冻结。比如当前有三个块,PolarDB 会对三个块全部进行扫描,事务 ID 小于 100002000的,在该行某一位做 frozen 标记。可以理解为,只有很早以前的事务修改的行会被冻结,最新修改的行不冻结。做完冻结后,所有数据库的列均会写上被冻结时的 ID,等下一次发生急性冻结时,ID 会被更新。VACUUM 处理 71 查询结果如上图,比如 id
58、=651 意味着该表没有被冻结,此 id 为事务创建时的 id。而 id=0 意味着数据库是初始化时创建的。为了提高急性冻结的效率,PolarDB 在 VM 上又做了改进。上次冻结后(包括惰性冻结与急性冻结)至今没有发生变化的,状态为 1;上次惰性冻结没有发生的,状态为 0;冻结后发生变化的,状态为 0。做急性冻结时,状态为 1 则跳过,状态为 0 则进行急性冻结,以减少冻结工作量。VACUUM 处理 72 PolarDB9.6 以后,提供了专门的后台进程做 autovacuum。默认每 10 分钟执行一次,由 autovacuum 参数定义间隔时间。每次做 vacuum 时,默认用三个 wo
59、rker 进程,每个进程同一时间只能负责一个数据 库 里 的 表。如 果 想 增 加autovacuum的 进 程 数,可 以 通 过autovacuum_max_workers 参数进行修改。vacuum 分为普通 vacuum 和 Full Vacuum。上图为普通 vacuum 操作。比如当前有三个块,紫色标示都是被删除的行。做了普通 vacuum 以后,三个块均保留,但里面只有少量数据。VACUUM 处理 73 为了减少数据文件的尺寸,可以做 Full Vacuum。将块中被删除的行的空间清理后,会再新建一个数据文件,为其分配一个块。然后将剩下的行放入新的数据文件的块中,将原来的数据文
60、件删除。如果检测到数据块很多,且每个块内的可用空间也很多时,建议做 Full Vacuum。比如上图,表中一共有 1640 个数据块,每个数据块内的可用空间为 99 个字节。对该表做 delete 操作并进行普通 vacuum 后,每个块的可用空间为 7124 字节,空闲率达 86.97%。VACUUM 处理 74 而对该表做 Full Vacuum 后,表内只剩 164 个数据块,且每个块内的可用空间为 0,数据文件的尺寸大幅降低。如上图所示,表内原先有 94 个块,做完普通 vacuum 后依然为 94 个块,但是做了Full Vacuum 后,只剩 10 个块。将来做全表扫描时,扫描量从
61、 94 个块降低至 10 个块,扫描效率得到了极大提升。缓冲区管理器 75 缓冲区管理器 缓冲区管理器位于用户和数据库存储之间,用户进程请求数据块时,由缓冲区管理器从数据库存储层将数据块读取到数据缓冲区提供服务。数据缓冲区内存放的是数据块,包含表和索引的块、可用性地图的块、可见性地图的块以及缓冲区索引块。缓冲区管理器分为三层,第一层为缓冲区表层,第二层为缓冲区描述层,第三层为缓冲区池层(负责将数据块从数据文件读到内存)。缓冲区描述层包含大量信息,也是对于管理最重要的一层。缓冲区管理器 76 缓冲区表层存在很多插槽,每个插槽里存放了数据块的标记,标记里包含了对于要访问的数据块的描述,比如哪个数据
62、文件的第几个块。每个槽里记录了一个或多个标记。比如缓冲区标记为(16821、16384、37721)、0、7,其中 16821、16384、37721分别代表对象 oid、数据库 oid 以及表空间 oid,0 代表页面的 fork number,7 代表页面 number。缓冲区管理器 77 将数据块读到数据缓冲区需要记录信息,此类信息存放在描述层。描述层里包含了缓冲区的 tag 信息以及 buffer_id。PolarDB 将缓冲区分为多个大小相同的块,每个块都有自己的 buffer_id。refcount 和 usage_count 用于描述缓冲区被访问的热度。缓冲区被某个进程访问过一次
63、,refcount 和 usage_count 均会+1。与此同时,如果缓冲区被时钟扫描过后refcount-1,refcount=0 代表该缓冲区可用。Flag 有三个状态,其中 dirty bit 代表缓冲区已经被修改过;valid bit 代表已经被写到数据文件,当前可用;io_in_progress bit 代表正在被进程处理。缓冲池层是连接描述层与表层非常重要的一层,它将内存分割为若干个内存块,每个内存块都有一个 buffer_id。缓冲区管理器 78 将数据块读取到数据缓冲区的流程如下:首先,发送一个请求,请求到达描述层后分配一个插槽,管理器将数据块的标记记录在描述里,同时从数据缓
64、冲区层申请内存块,将缓冲数据块的标记从数据库读到缓冲区,并发送 Buffer_id,使得块的标记与 Buffer_id 的标记能够进行匹配。假如数据块是 8k,则缓冲池会被分割为若干个 8k 的池槽,正好等于数据块的大小。Backend 访问数据块时,读取数据的流程如下:首先,将进程要访问的数据块标记发送给管理器,并由管理器负责寻找当前哪个 ID存在可用空间。然后管理器将找到的 Buffer_id 发送给用户进程并记录到描述层,缓冲区管理器 79 管理器的后台将数据块读到数据缓冲区。后台进程得到 Buffer_id 以后,根据Buffer_id 找到数据块。如果下一次要读同样的块,backen
65、d 进程会将需要访问的 buffer tag 发送给管理器,管理器扫描该数据块是否曾被访问过。如果有,则查询该数据块当前放在哪个Buffer_id 并将 Buffer_id 发给 backend 进程,然后进行访问。由于数据块已经存在缓冲区,因此不再需要从磁盘里读数据块。数据缓冲区的大小固定,无法将整个数据库的数据都存放在内存中,因此数据缓冲区的空间应轮流重复使用,需要做替换。通常,页面替换的算法有两种,分别为 LRU 即最近最少使用规则(Oracle 使用的算法)以及时钟扫描。时钟扫描:描述层里通过 refcount 参数记录了数据块曾经被访问过的次数,进程访问一次则+1,被时钟扫描过一次则
66、-1,以此判断数据块当前的受欢迎程度。如果refcount 为 0 则代表该数据块可用。如上图,图里时钟指向的数据块 refcount=3,则跳过,继续指向下一个数据块。图里指向的 refcount=2,对其做-1 操作,继续指向下一个数据块。图里指向的 refcount=0,代表该块可用,因此可分配给进程使用。缓冲区管理器 80 LRU 算法和时钟扫描算法的本质都是根据数据块当前被关注的程度来判断其是否可被替换。数据缓冲区里的数据块被修改以后,会被标识为脏块。PolarDB 提供了 checkpointer和 background writer 两个进程用于写脏块。Oracle 也提供了两个
67、进程,但是只由 DBWriter 负责写脏块,检查点进程只负责向数据缓冲区发信号。检查点进程会将检查点的记录写到 WAL 日志文件,再将相应的脏块写到数据文件。写操作属于密集型操作,会影响数据库的性能,因此,此处写的机制为一点一点地刷新脏页,以求对数据库活动的影响最低。默认情况下,每次写 100 个数据块,200 毫秒写入一次。可理解为缓冲区不断地被修改,又不断地保存。过了一段时间再发检查点时,会将上一次发生检查点到目前为止的所有脏块都写入。缓冲区管理器 81 可以通过 shared_buffers 参数来控制共享缓冲区的尺寸,共享缓冲区内包含数据缓冲区里的内容。可以通过 wal_buffer
68、s 控制日志缓冲区的尺寸。effectiv_cache_size默认为 4G,用于告知优化器内核中可用的缓存量,为扫描方式的选择提供参考性意见。备份与恢复 82 备份与恢复 PolarDB 是基于共享存储的存算分离架构,因此 PolarDB 的备份恢复和 PostgreSQL存在部分差异。本文将指导您如何对 PolarDB 做备份恢复,搭建只读节点,搭建Standby 实例等:PolarDB 备份恢复原理 PolarDB 的目录结构 polar_basebackup 备份工具 PolarDB 搭建 RO PolarDB 搭建 Standby PolarDB 按时间点恢复 备份恢复原理 Pola
69、rDB 的备份恢复原理整体上和 PostgreSQL 几乎一致,总结为以下几步:执行 pg_start_backup 命令 使用各种方式对数据库进行复制 执行 pg_stop_backup 命令 进行备份的更简单方法是使用 polar_basebackup,但它其实是在内部发出这些低级命令,并且支持使用网络将文件发送到远端。备份与恢复 83 pg_start_backup:准备进行基本备份。恢复过程从 REDO 点开始,因此pg_start_backup必须执行检查点以在开始进行基本备份时显式创建REDO点。此外,其检查点的检查点位置必须保存在 pg_control 以外的文件中,因为在备份期
70、间可能会多次执行常规检查点。因此 pg_start_backup 执行以下四个操作:?强制进入整页写模式。?切换到当前的 WAL 段文件。?做检查点。?创建一个 backup_label 文件该文件在基础目录的顶层创建,包含关于基础备份本身的基本信息,例如该检查点的检查点位置。第三和第四个操作是这个命令的核心;执行第一和第二操作以更可靠地恢复数据库集群。pg_stop_backup:执行以下五个操作来完成备份。?如果已被pg_start_backup强制更改,则重置为非整页写入模式。?写一条备份端的 XLOG 记录。?切换 WAL 段文件。?创 建 备 份 历 史 文 件 该 文 件 包 含b
71、ackup_label文 件 的 内 容 和pg_stop_backup已执行的时间戳。?删除backup_label文件从基本备份恢复需要backup_label文件,一旦复制,在原始数据库集群中就不需要了。目录结构 如上所述,PolarDB 备份过程总体可以概括为三步,其中第二步是使用各种方式对数据库进行复制:手动 copy 使用网络工具传输 基于存储进行打快照。因此,这里介绍一下 PolarDB 数据目录结构,以便于进一步理解备份恢复。备份与恢复 84 如上图,PolarDB 是基于共享存储的,所以 PolarDB 在物理上有两个重要的数据目录,分别是本地存储目录和共享存储目录。本地存储
72、目录 1.postgres=#show data_directory;2.data_directory 3.-4./home/postgres/primary 5.(1 row)可以通过上述命令在数据库中获取本地存储目录的位置,可以看到它是类似于PostgreSQL 的数据目录。1.2.base 3.1 4.13938 5.13939 6.13940 7.global 8.pg_commit_ts 9.pg_csnlog 10.pg_dynshmem 11.pg_log 12.pg_logical 13.mappings 14.snapshots 15.pg_logindex 备份与恢复 85
73、 16.pg_multixact 17.members 18.offsets 19.pg_notify 20.pg_replslot 21.pg_serial 22.pg_snapshots 23.pg_stat 24.pg_stat_tmp 25.pg_subtrans 26.pg_tblspc 27.pg_xact 28.polar_cache_trash 29.polar_fullpage 30.polar_rel_size_cache 本地存储目录中,大多都是通过 initdb 命令生成的文件或目录。随着数据库服务运行,这里会生成更多的本地文件,如临时文件、缓存文件、配置文件、日志文件
74、。由于本地存储目录中的文件不涉及核心数据,因此在做备份时本地存储目录是可选的。您可以仅备份共享存储上的数据目录,然后用 initdb 重新生成一份新的本地存储目录。但是需要记住之前的本地配置信息,如 postgresql.conf,pg_hba.conf 等。提示 如果您不能记住历史配置,或者您需要保留历史日志,建议您将本地存储目录也进行备份。可以将这个目录完全复制后修改配置文件来搭建 RO 或者 Standby。共享存储目录 1.postgres=#show polar_datadir;2.polar_datadir 3.-4./nvme0n1/shared_data/5.(1 row)备份
75、与恢复 86 1.2.3.base 4.1 5.16555 6.16556 7.16557 8.16558 9.global 10.pg_commit_ts 11.pg_csnlog 12.pg_logindex 13.pg_multixact 14.members 15.offsets 16.pg_replslot 17.pg_tblspc 18.pg_twophase 19.pg_wal 20.archive_status 21.pg_xact 22.polar_dma 23.consensus_cc_log 24.consensus_log 25.polar_flog 26.polar_
76、flog_index 27.polar_fraindex 28.fbpoint 29.pg_xact 30.polar_fullpage 共享存储目录中存放 PolarDB 的核心数据文件,如表文件、索引文件、WAL 日志、DMA、LogIndex、Flashback 等。这些文件被一个 RW 节点和多个 RO 节点共享,因此是必须备份的。您可以使用 copy 命令、存储快照、网络传输等方式进行备份。如果您没有更好的选择,推荐使用 polar_basebackup 命令。polar_basebackup 备份工具 下面介绍一下 PolarDB 的备份工具 polar_basebackup,它由
77、 pg_basebackup 备份与恢复 87 改造而来,且完全兼容 pg_baseabckup,也就是说它同样可以用于对 PostgreSQL做备份恢复。polar_basebackup 在 PolarDB 二进制安装目录下的 bin/目录中,您可以配置 export 环境变量来直接使用它。1.2.polar_basebackup takes a base backup of a running PostgreSQL server.3.4.Usage:5.polar_basebackup OPTION.6.7.Options controlling the output:8.-D,-pgda
78、ta=DIRECTORY receive base backup into directory 9.-F,-format=p|t output format(plain(default),tar)10.-r,-max-rate=RATE maximum transfer rate to transfer data directory 11.(in kB/s,or use suffix k or M)12.-R,-write-recovery-conf 13.write recovery.conf for replication 14.-T,-tablespace-mapping=OLDDIR=
79、NEWDIR 15.relocate tablespace in OLDDIR to NEWDIR 16.-waldir=WALDIR location for the write-ahead log directory 17.-X,-wal-method=none|fetch|stream 18.include required WAL files with specified method 19.-z,-gzip compress tar output 20.-Z,-compress=0-9 compress tar output with given compression level
80、21.22.General options:23.-c,-checkpoint=fast|spread 24.set fast or spread checkpointing 25.-C,-create-slot create replication slot 26.-l,-label=LABEL set backup label 27.-n,-no-clean do not clean up after errors 28.-N,-no-sync do not wait for changes to be written safely to disk 29.-P,-progress show
81、 progress information 30.-S,-slot=SLOTNAME replication slot to use 31.-v,-verbose output verbose messages 32.-V,-version output version information,then exit 33.-no-slot prevent creation of temporary replication slot 34.-no-verify-checksums 35.do not verify checksums 备份与恢复 88 36.-?,-help show this h
82、elp,then exit 37.38.Connection options:39.-d,-dbname=CONNSTR connection string 40.-h,-host=HOSTNAME database server host or socket directory 41.-p,-port=PORT database server port number 42.-s,-status-interval=INTERVAL 43.time between status packets sent to server(in seconds)44.-U,-username=NAME conn
83、ect as specified database user 45.-w,-no-password never prompt for password 46.-W,-password force password prompt(should happen automatically)47.-polardata=datadir receive polar data backup into directory 48.-polar_disk_home=disk_home polar_disk_home for polar data backup 49.-polar_host_id=host_id p
84、olar_host_id for polar data backup 50.-polar_storage_cluster_name=cluster_name polar_storage_cluster_name for polar data backup 可以看到 polar_basebackup 的大部分参数及用法都和 pg_basebackup 一致,只是多了以下几个参数,下面重点来介绍一下:polardata:如果您备份的实例是 PolarDB 共享存储架构,这个参数用于指定PolarDB 共 享 存 储 目 录 的 位 置。如 果 您 不 指 定,将 会 使 用 默 认 目 录pola
85、r_shared_data,并且放在本地存储目录(即-D/-pgdata 所指定的参数)下面。如果您对 PostgreSQL 备份则无需关心他。polar_disk_home:如果您备份的实例是 PolarDB 共享存储架构,且您希望将共享存储目录通过 PFS 写入共享存储设备,则需要指定这个参数,它是 PFS 的使用参数。polar_host_id:如果您备份的实例是 PolarDB 共享存储架构,且您希望将共享存储目录通过 PFS 写入共享存储设备,则需要指定这个参数,它是 PFS 的使用参数。备份与恢复 89 polar_storage_cluster_name:如果您备份的实例是 Po
86、larDB 共享存储架构,且您希望将共享存储目录通过 PFS 写入共享存储设备,则需要指定这个参数,它是 PFS 的使用参数。搭建 RO 您可以通过以下两种方式来搭建 RO node。使用 initdb 来搭建 RO 主要步骤是使用 initdb 初始化 RO 的本地存储目录,然后修改配置文件,启动实例。具体请参考只读节点部署。备份 RW 的本地存储目录来搭建 RO 这里使用备份 RW 的本地存储目录。下面通过 polar_basebakcup 来演示:1.polar_basebackup-host=主节点所在 IP-port=5432-D/home/postgres/replica1-X s
87、tream-progress-write-recovery-conf-v 完成 polar_basebackup 命令后,我们可以看到/home/postgres/replica1 中存在一个 polar_shared_data,搭建 RO 时不需要它,将它删除:1.rm-rf/home/postgres/replica1/polar_shared_data 打开/home/postgres/replica1/postgresql.conf,修改如下配置项:备份与恢复 90 1.2.port=5433 3.polar_hostid=2 4.polar_enable_shared_storage
88、_mode=on 5.polar_disk_name=nvme0n1 6.polar_datadir=/nvme0n1/shared_data/7.polar_vfs.localfs_mode=off 8.shared_preload_libraries=$libdir/polar_vfs,$libdir/polar_worker 9.polar_storage_cluster_name=disk 10.logging_collector=on 11.log_line_prefix=%pt%rt%ut%mt 12.log_directory=pg_log 13.listen_addresses
89、=*14.max_connections=1000 15.synchronous_standby_names=打开/home/postgres/replica1/recovery.conf,使用以下配置项替换文件中的所有内容:1.polar_replica=on 2.recovery_target_timeline=latest 3.primary_slot_name=replica1 4.primary_conninfo=host=主节点所在IP port=5432 user=postgres dbname=postgres application_name=replica1 最后,启动只读
90、节点:1.$HOME/tmp_basedir_polardb_pg_1100_bld/bin/pg_ctl start-D$HOME/replica1 检查只读节点能否正常运行:1.$HOME/tmp_basedir_polardb_pg_1100_bld/bin/psql 2.-p 5433 备份与恢复 91 3.-d postgres 4.-c select version();5.version 6.-7.PostgreSQL 11.9(POLARDB 11.9)8.(1 row)搭建 Standby 您可以使用全量备份集搭建 Standby,这里推荐使用 polar_basebacku
91、p 进行搭建,下面介绍搭建流程。使用 polar_basebakcup 对实例作全量备份 1.polar_basebackup-host=主节点所在 IP-port=5432-D/home/postgres/standby-polardata=/nvme0n2/shared_data/-polar_storage_cluster_name=disk-polar_disk_name=nvme0n2 -polar_host_id=3-X stream-progress-write-recovery-conf-v 提示 注意:这里是构建共享存储的 Standby,首先您需要找一台机器部署好 Pola
92、rDB 及其文件系统 PolarFS,且已经搭建好了共享存储 nvme0n2,具体操作请参考准备块设备与搭建文件系统。备份完成后如下图所示:备份与恢复 92 提示 如果您没有共享存储设备,则不需要指定-polar_storage_cluster_name,-polar_disk_name,-polar_host_id 参数。下面我们简单介绍下其他形态的 PolarDB 备份:1.-单节点本地备份 2.polar_basebackup-D/polardb/data-standby-X stream -progress-write-recovery-conf-v 3.-共享存储本地备份 4.pol
93、ar_basebackup-D/polardb/data-standby-polardata=/polardb/data-local -X stream-progress-write-recovery-conf-v 5.-共享存储写入 pfs 6.polar_basebackup-D/polardb/data-standby-polardata=/nvme7n1/data -polar_storage_cluster_name=disk-polar_disk_name=nvme7n1 -polar_host_id=3 检查备份是否正常 查看本地目录:查看共享存储目录:备份与恢复 93 修改 p
94、ostgresql.conf 将参数修改为如下所示:1.polar_hostid=3 2.polar_disk_name=nvme0n2 3.polar_datadir=/nvme0n2/shared_data 4.polar_storage_cluster_name=disk 5.synchronous_standby_names=在主库中创建复制槽 1.psql-host=主节点所在 IP -port=5432-d postgres-c SELECT*FROM pg_create_physical_replication_slot(standby1);2.slot_name|lsn 3.-
95、+-4.standby1|5.(1 row)修改 Standby 本地目录配置 在 Standby 的本地存储目录中 recovery.conf 文件中增加如下参数:1.recovery_target_timeline=latest 2.primary_slot_name=standby1 启动 Standby 1.$HOME/tmp_basedir_polardb_pg_1100_bld/bin/pg_ctl start-D$HOME/standby 备份与恢复 94 验证 Standby 1.psql-host=master 所在 IP-port=5432-d postgres-c cre
96、ate table t(t1 int primary key,t2 int);insert into t values(1,1),(2,3),(3,3);2.CREATE TABLE 3.INSERT 0 3 1.psql-host=standby 所在 IP-port=5432-d postgres-c select*from t;2.t1|t2 3.-+-4.1|1 5.2|3 6.3|3 7.(3 rows)按时间点恢复 可以参考 PostgreSQL 按时间点恢复 PITR。其原理如图所示,使用备份集加上归档日志,可以恢复出任意历史时刻的 PolarDB 实例:共享存储原理与实践 95
97、 共享存储原理与实践 一、整体介绍 传统 RDS 架构在高可用的实现上采用了主备复制的模式,通过 Binlog 逻辑复制保证高可用。该架构存在以下几个问题:增加计算节点需要同步扩容存储,导致存储成本随着节点增加而上升。扩展只读节点较麻烦,需要进行数据重建。在数据量较大的情况下,数据重建耗时较久,可能会影响 HA 的耗时。不同于传统的主备复制,PolarDB 采用共享存储架构,存储和计算分离,中间通过PolarFS 分布式的文件系统和存储进行共享数据。共享存储原理与实践 96 RW 负责数据写入,主要流程为:RW 调用文件系统的 API,文件系统经过内部处理将数据写入到共享存储,数据的可靠性依赖
98、于共享存储的多副本机制。因此在 DB 层面,无需关心多份数据的写入。因为天然存在多副本的机制,对于 DB 而言,只需写一份数据在共享存储。共享存储中,所有节点可共享同一份数据,PolarFS 分布式文件系统天然解决了一写多读场景下的数据一致性问题。该架构下存储和计算节点分离,计算节点的扩容和存储节点的扩容互相不耦合,均可单独进行扩容。并且因为共享一份数据,新增RO 节点时,无需再进行数据重建,可以秒级拉起 RO 节点,存储层面也可独立扩容,无需扩容计算节点。PolarFS 文件系统是专门为 DB 设计的用户态文件系统,并且基于共享存储支持了一写多读的实现,向上提供兼容 posix 的接口。写节
99、点的数据写入流程如下:调用 PolarFS 创建目录,PolarFS 将数据写入到共享存储。由于采用了共享存储的机制,数据已经存在于存储内,但是因为 PolarFS 层面本身也有文件系统的元数据,需要在 RO 节点上通过日志同步的方式进行感知。以上设计的好处在于 PolarFS 提供 posix 兼容,DB 无需再大刀阔斧地改造代码,可以以较小的代价实现分布式架构,并且天然地在共享存储架构下实现了高可用。比如新增 RO 或 HA 的场景下,相较主备复制,它无需进行数据拷贝,RTO 可达到分钟级甚至秒级。共享存储原理与实践 97 PolarFS 和常见的文件系统类似,主要有以下几个元数据的概念。
100、Direntry:记录文件或目录的名字。Inode:描述文件或目录的基本属性,比如 inode 类型、文件长度、atime/mtime/ctime 等。Blktag:描述该 block 所属的文件以及在文件中的位置,每个 block 都拥有一个对应的 blktag。比如上图,创建 gcc 文件并进行写入时,需要为其分配数据块空间,每个数据块默认大小为 4M,需要分配大于 4M 的空间时,将多个 block 进行连接,达到索引链接的目的。文件系统的目录输入采用了典型的树状结构。共享存储原理与实践 98 文件系统中需要进行 MakeFS 的操作,目的为对文件系统的空间进行划分,将底层的共享存储块设
101、备进行管理。假设是 40G 的存储设备,PolarFS 将按照实际的 chunk 对空间进行划分,划分出 4个 chunk 的空间。每个 chunk 内部又划分了大小为 4M 的多个 block。每个 chunk的第0个block将作为superblock,用于记录文件系统的元数据信息,多为blktag、direntry 以及 inode。每一个 4 兆的 superblock 最多可以存储 2048 个 direntry 和inode,意味着每个 chunk 下可以创建 2048 个文件或目录。除了 superblock 之外的所有的 datablock 用于存放文件的数据,共有 2560
102、个。blktag 元数据会负责管理 datablock,映射到 datablock。使用 PolarFS 文件系统时,首先会执行格式化,主要目的是将元数据在 superblock内进行格式。日志是文件系统中很重要的组成。文件系统对文件的修改往往涉及多个不同类型的元数据修改,如果修改不能原子性完成,则会造成数据的不一致或损坏。PolarFS 的日志除了保证事务提交的完整性,还有一个关键用途为通过一写多读同步数据。共享存储原理与实践 99 文件系统内部有很多元数据需要进行管理,当 RW 节点对内部的元数据进行修改之后,如果要保证一致性,RO 节点上也需要对元数据进行同样的修改,才能保证一致性。该能
103、力通过 Journal 的设计实现。Journal 文件是固定大小的空间,其中 head lsn 为当前事务提交到的最新位点。因为日志是循环覆盖写的过程,如果日志提交后空间不够,需要做 checkpoint 操作,将日志 trim 到磁盘上,变更也将反映到磁盘的共享存储上。checkpoint 操作可将位点不停地推进,将块空间进行释放,用于提交新的 log。RW 提交元数据之后,RO 要访问文件时,首先会读取最新位点。同时,RO 内部本身会维护已有的事务位点,然后判断最新位点与已有事务位点的差距,比如最新位点为 10,已有位点为 5,则意味着中间有 5 个事务尚未同步。RO 会将这 5 个数据
104、进行同步,并进行内存回放。同步完以后,将 RO 本身的已有事务位点更新至 10。二、PolarFS 共享存储系统的搭建 首先,从 github 下载源代码,进行编译、安装代码。源码地址 https:/ 共享存储原理与实践 100 文档地址 https:/ 安装后,系统即存在 PFS 对应的工具。执行 PFS 命令可查看 PFS 支持的命令行。使用文件系统的第一步是对文件系统进行格式化,需要 sudo 的权限,因为需要进行磁盘的格式化。命令中的-C disk 指的是针对块设备,因为 PolarFS 支持不同的存储形态,除了块设备之外,也支持阿里内部自研的分布式共享存储 PolarStore。mk
105、fs 命令下的提供了多种选项,比如指定的 logsize 大小,number users 用于控制并发的实例编号。如果对以上选项不了解,采用默认值即可。共享存储原理与实践 101 通过上述语句进行格式化。如果文件系统之前曾被格式化,本次格式化则需要加上-f,意为强制格式化。对于单节点,块设备直接只用本地磁盘即可。PFS 除了支持共享存储,也支持单机硬盘,但是单机硬盘无法实现跨节点的数据访问。日常开发可以使用单机模式进行格式。格式化成功之后,通过 ls 命令查看文件系统下的文件。可以通过 mkdir 命令创建目录,执行后没有报错则意味着创建成功。通过 ls 命令查看,结果显示 nvme6n1 文
106、件下已经存在 testDir 目录。PolarDB 使用多个进程进行数据通信。主进程进行新的请求时,会 fork 一个 work子进程,每个进程下面都会引入 PolarFS 的客户端,包含了 read、write 等常见操作,并负责调用客户端的接口,与后台的 pfsdaemon 进程进行通讯。共享存储原理与实践 102 用户态共享内存作为 PolarFS 的通信信道,最大的优点在于可以减少数据的拷贝。比如客户端要写 buffer,将数据存入共享内存,PID 文件系统可以直接将数据取出,间接实现了零拷贝的操作。上图为此前默认安装的 pfsdaemon。通过 start_pfsd.sh-p 命令即
107、可调用。-p 指定文件系统的盘符。Fuse 类似于文件系统访问的中间层,对上兼容标准的文件系统的语义操作,对下可对接不同的文件系统。通过标准的命令,可直接访问到底层的 PolarFS。比如此前查看 PolarFS 的目录需要通过 PFS 工具,但是接入 fuse 之后,直接使用fuse Linux 的原生命令 makeDir 即可创建目录到 PFS。共享存储原理与实践 103 首先安装 Fuse,具体安装步骤可参考 github 上的安装文档。安装完成后,将 fuse挂载到 PFS 上,系统默认提供了现成的脚本进行挂在操作(如上图所示),脚本在安装时已经部署在机器上。脚本下需要提供三个参数,d
108、iskname 指定块设备的名称;RW 和 RO 参数代表启动读写实例和只读实例的意思,因为需要创建文件和创建目录等写操作,此处启动读写实例;mount_dir 指 fuse 的挂载目录。挂载成功后,可进入 fuse 目录访问。执行 ll 操作,结果可直接显示 PFS 的 testDir。可直接通过原生的 Linux 的命令创建 dir 和文件。共享存储原理与实践 104 可以看到创建的目录和文件都已经同步到 PFS 的文件系统,说明通过 fuse 成功访问了 PFS 文件系统。Fuse 最大的作用为简化文件系统的操作,用原生命令即可达到访问 PFS 文件系统的目的。用完之后,可通过 moun
109、t_dir 接触 Fuse 挂载。PolarFS 文件系统支持不同的挂载形态和存储介质,其中 PolarStore 是阿里云数据库内部自研的分布式共享文件系统,通过 RDMA 在存储层进行数据的多副本机制的通信以及复制。当前,阿里云官网在售的 PolarDB 产品均基于 PolarStore 形态实现,支持 PolarDB-MySQL 的 5.6、5.7、8.0 版本,PolarDB-PostgreSQL11 以及 PolarDB-Oracle 兼容版本。共享存储原理与实践 105 阿里云的 ESSD 是标准的分布式块设备共享存储,可在阿里云上直接购买。部署方式与单机较类似,区别为 ESSD
110、在底层是分布式的共享存储,通过 ESSD 也可以达到一写多读进行数据共享以及数据同步一致性的效果。Ceph 是业界流行的分布式存储系统,对上层暴露了多种存储接口,比如 RDB 为块存储的接口,可以模拟出一个块设备供用户使用,RadosGW 是对象存储的接口,下面可以对接 S3 等,CephFS 是 Ceph 对外暴露的文件系统接口。PolarFS 本身已经是一个文件系统,因此此处主要使用的为 RBD 的形态。共享存储原理与实践 106 首先,安装 Ceph 端。模拟单机节点搭建单机的 Ceph 集群,在一台机器上指定的三个存储盘,分别是 7n1、8n1、9n1。Ceph 环境已提前搭建好,可以
111、看到 7n1、8n1、9n1 已经映射到 Ceph 的集群中,由 OSD 进程管理。通过 ceph osd pool create rbd_pool 8 8 创建 rbd 的存储池,通过 rdb create rbd_pool/volume01 将存储池映射到卷,然后通过 rbd map rbd_pool/volume01 将卷映射为块设备。上图中 rbd 0 即通过 Ceph 映射出的块设备,可以对块设备进行操作,操作方式与单机的硬盘操作一致。共享存储原理与实践 107 上图为对块设备执行 mkfs 操作后的结果展示。上图为创建目录操作。另外,还可通过 Ceph 的块设备进行 PolarFS
112、 文件的读写。云原生 HTAP 108 云原生 HTAP PolarDB-PG 是云原生数据库,具有存储计算分离的架构。可以根据用户需要弹性扩充存储节点,也可以根据用户的计算需求弹性扩充用户的计算节点。但是如果使用原生的 PolarDB-PG 处理 HTAP 场景,在处理 AP 场景时会遇到两个挑战。第一,单机的 PG 只支持单机的串行与单机的并行,不支持多机查询和跨机查询,无法发挥多个计算节点的特性,CPU 和 memory 无法横向的 scale out,只能单机scale up,即必须增加 CPU 和 memory 的实例规格。第二,原生的 PG 直接套用到 PolarDB 上,无法充分
113、发挥共享存储池的大吞吐能力,因为只能利用单机计算节点上的 RO 能力。而根据 TP 和 AP 在存储和计算上是否共享与分离的维度,可以分为三种:第一,TP 和 AP 在存储计算上都分离,即分为 TP 与 AP 两套独立的系统。TP 的数据需要导入到 AP 系统中,存在延迟、时效性不高的问题。同时两份存储也增加了冗余、存储成本以及运维难度。第二,TP 和 AP 在存储和计算上都共享。该模式对 TP 和 AP 查询时或多或少都会造成一些影响。同时,受限于 TP 查询,AP 比重增大时,无法弹性 scale out,同样也只能在单机上调整自己的 CPU 与 memory。云原生 HTAP 109 第
114、三,TP 和 AP 在存储上共享,在计算上分离,即 PolarDB 云原生 HTAP 的方案。PolarDB 云原生 HTAP 的整体架构如上图所示。底层为共享存储池,上层为多个计算节点,每个计算节点内包含了一个读写节点和多个 RO 节点。由于 TP 和 AP 共享一套存储,减少了存储成本,可以提高查询的时效性,能提供秒毫秒级的数据新鲜度。其次,TP 查询受限于 RO 节点与 RW 节点,而 AP 查询仅受限于部分 RO 节点,因此可以实现 TP 与 AP 的物理隔离,并杜绝了 CPU 与 memory 的相互影响。另外,该架构具备 Serverless 的弹性扩展能力,可以在任何 RO 级联
115、上发起分布式MPP 查询,可以弹性调整 MPP 执行节点的范围,可以弹性调整单机 MPP 的单机并行度。最后,该架构消除了数据的存储倾斜和计算倾斜,在执行过程中也可充分考虑到 PG Buffer Pool 的亲和性。由于 PolarDB 底层存储在不同节点上是共享的,因此不能像传统 MPP 一样扫表。我们在原先的单机执行引擎上支持了 MPP 分布式执行引擎(跨机执行引擎)。同时对 Shared-Storage 做了优化,基于 Shared-Storage 的 MPP 是业界首创。其基本原理为借助 Shuffle 算子屏蔽数据的分布,借助 ParallelScan 算子屏蔽共享存储。云原生 HT
116、AP 110 上图是典型的火山模型,扫描 A 表,再扫描 B 表进行 HashJoin,最后做聚合输出。而 PolarDB 的 MPP 场景下,对 A 表和 B 表进行了虚拟分区。两个只读节点上默配置了一个 worker。左侧只读节点上的 worker 会扫描 A 表的 Virtual Partition-1,右侧节点上的 worker 会扫描 A 表的 Virtual Partition-2。B 表同理。通过对 Virtual Partition-1 和 Virtual Partition-2 虚拟表的共同扫描,屏蔽了共享存储。通过 Shuffle 算子的分发之后,分发到上层进行 HashJ
117、oin 的时,已经屏蔽了数据的分布。而上述执行计划必然需要有优化器的支持。我们基于社区的 ORCA 优化器扩展了能感知共享存储特性的 Transformation Rules。使得能够探索共享存储下特有的 Plan空间,比如:一张表在 PolarDB 中既可以全量扫描,也可以分区域扫描,这是与传统 MPP 的本质区别。上图中的 A 表按照分片扫描,但是如果 B 是小表,则可以做全表扫描。每个子节点都会扫描 B 的全量数据,构建一张哈希表。扫描 B 表的数据不需要下发到各个节点上。云原生 HTAP 111 上图灰色部分是 PolarDB 内核 PORCA 优化器的适配部分。下半部分是 ORCA
118、内核,灰色模块是我们在 ORCA 内核中对共享存储特性所做的扩展。PolarDB 中有 4 类算子需要并行化,其中 Seqscan 的算子的并行化极具代表性。为了最大限度地利用存储的大 IO 带宽,在顺序扫描时,按照 4MB 为单位做逻辑切分,尽量将 IO 打散到不同的盘上,达到所有盘同时提供读服务的效果。该方案还有一个优势在于每个只读节点只扫描部分表文件,最终能缓存的表大小是所有只读节点的 BufferPool 总和。云原生 HTAP 112 上图可见,通过增加只读节点,扫描性能线性提升 30 倍。打开 buffer 后,扫描时间从 37min 降至 3.75s,提升了 600 倍。这也是数
119、据亲和性的优势所在。倾斜是传统 MPP 固有的问题,主要包含两方面:一方面是存储的倾斜,大对象通过heap 内部表关联 toast 表时,因为无法确切地知道实际存储的数据量有多大,无论怎么切分,数据存储都有可能不均衡;另一方面是执行时的倾斜。不同只读节点上的事务、buffer、网络等会抖动,因此也会存在执行计算倾斜。云原生 HTAP 113 为了解决倾斜问题,我们支持了动态扫描。将协调节点内部分成 DataThread 和ControlThread,其中 DataThread 负责收集汇总元组,ControlThread 负责控制每个扫描算子的扫描进度。每个算子控制每个节点上 scan 算子的
120、扫描进度,每个节点上 scan 算子再扫描下一个块的数据时会向 QC 节点进行请求查询,从而获得下一个扫描的目标块,使得扫描快的工作进程能多扫描逻辑的数据切片。此外,尽管是冬天分配,过程中我们也尽量考虑了 buffer 数据亲和性。另外,每个算子的上下文均存储在各个 worker 的私有内存中,协调节点不存储表的相关信息。上图可见,出现大对象时,静态扫描会出现数据倾斜,而使用动态扫描并没有因为 RO 节点增多导致数据倾斜严重。我们利用数据共享的特点,还可支持云原生下极致弹性的要求:将 Coordinator 全链路上各个模块所需要的外部依赖存在共享存储上,每个节点都可以看到相同的数据。同时 w
121、orker 全链路需要的运行时参数通过控制链路从 Coordinator 同步,使Coordinator 和 worker 无状态化。任何节点都可以作为协调节点,确定了协调节点之后,控制节点再从协调节点获取相关的控制信息。以上方式带来的好处在于:SQL 的任何只读节点都可以称为协调节点,解决了协调节点单点的问题。其次,SQL 可以在任何节点上起任意数量的 worker,使算力达到SQL 级别的弹性扩展,使得业务有更多的调度策略。云原生 HTAP 114 比如四个只读节点,可以让业务域 1 的 SQL 只利用只读节点 1 和只读节点 2,业务域 2 的 SQL 利用节点 3 和节点 4,为用户提
122、供更多选择。多个计算节点通过等待回放和 globalsnapshot 机制完成。等待回放能够保证所有需要的数据版本已经同步完成,globalsnapshot 能够保证选取统一的可读版本。主要流程如下:用户 SQL 发送后,生成计划并确定协调节点,协调节点会广播ReadLSN,每个 worker 节点等待回放到 ReadLSN。结束之后获取各自的 snapshot,通过序列化发送给协调节点。协调节点汇总所有 worker,选出最小的 snapshot 并通过广播发给各个节点,再由广播执行计划树,从而可以保证每个 worker 能看到相同的数据、相同的快照和相同的 plan,最终开始执行。上图为使
123、用 1TB 的 TPCH 进行的测试。云原生 HTAP 115 Demo 演示:利用 PolarDB HTAP 加速 TPC-H 首先,更新镜像,启动 Docker。确认实例已经拉起,进行连接。生成 TPC-H 测试数据集,使用 tpch-dbgen 工具生成任意大小的数据集。云原生 HTAP 116 导入建表语句。导入数据。对表的最大并行度进行限制。默认情况下,不支持对 PX 查询的表设置最大并行度,PX 即分布式执行引擎。开启每张表的最大 worker 数,大于 1 则表示可以支持 MPP 查询。云原生 HTAP 117 执行单机的单机并行的执行引擎。并行度设置为 2,上图为使用单机并行执
124、行 tpch查询 q18 的计划。执行 q18.sql,结果显示花费 12 秒。接下来执行 PolarDB 的 MPP 执行引擎。云原生 HTAP 118 打开 px 开关,将单机并行设置为 1,查看执行计划。查看执行效果,耗时 5s。云原生 HTAP 119 将并度设置为 2,查看执行计划,如上图。计划中显示,有 4 个 worker 在工作,数据汇总到一个节点上。查看执行效果,执行时间为 3s,与并行度为 1 时相比,有了将近一倍的提升。最佳场景实践与压测 120 最佳场景实践与压测 TPC 是一个专门负责制定计算机事务处理能力测试标准并监督其执行的第三方组织,其中被业界广泛使用的测试方式
125、有四种,分别是有 TPC-C、TPC-E、TPC-H 和 TPC-DS。前两者针对 OLTP 业务,后两者针对 OLAP 业务。OLTP 主要执行日常的事务处理,包括大量增删改查,其特点为高并发、高性能,且满足 ACID 特性。而 OLAP 主要用于数据仓库,支持非常复杂的查询,查询结果侧重于决策支持,其特点为对实时性要求不高,数据量大。最佳场景实践与压测 121 TPC-E 与 TPC-C 的主要差别在于难度,TPC-E 可以理解为是 TPC-C 的升级版,测试内容更复杂,要求更高。TPC-C 的业务模型为批发零售业务,主要业务为订单的处理能力,涉及 5 种事务、9 张表以及 4 大常见的数
126、据类型。TPC-E 模拟的是证券交易系统,实时性要求更高。它模拟了 10 种实时事务、2 种批处理事务。TPC-C 模拟的是一个在线零售公司的业务场景,假设仓库里包含 10 万个产品,负责十个区域的供货,每个区域都有单独的订单系统,一个区域管理 300 个客户,因此仓库总共涉及到 3 万个客户。树状图如上。最佳场景实践与压测 122 上图为 TPC-C 涉及到的 9 张表。其中 New-Order 表示未发货的订单,发货后即删除,初始值为每个仓库 9000 条;Order 为总订单表,每新增一个客户订单会增加一条记录,不会删除,初始化为每个客户一条订单,初始值为仓库数*每个仓库的商品数量。TP
127、C-C 业务数据模型 TPC-C 测试的能力为每分钟处理完成的事务数,主要针对 neworders 表。事务数越大,说明处理能力越高。最佳场景实践与压测 123 TPC-C 在部署时,数据仓库的数量也会影响业务的复杂程度。另外,TPC-C 也可用于测试同样的硬件配置、同样的业务量下,Oracle 和 PolarDB 的处理性能。TPC-C 测试完后,可以将字符的报告格式化成 HTML 形式,使其更具可读性。TPC-H 主要针对数仓,提供了两种测试方式,其中 TPC-H 较为简单,TPC-DS 较为复杂,支持子表达式、关联子查询、聚簇、排序等数据分析时的常用语法。最佳场景实践与压测 124 OL
128、AP 是 TPC-H 的测试标准,主要针统计分析、数据挖掘、分析处理等。TPC-H 模拟了一个数据库模型,容量可以在 1-10000 GB 几个级别种进行选择,其中包含了八张表,提供了 22 个决策系统里常用的查询语句,比如分组、排序、聚集、子查询、关联等。比如一条查询语句用于查询表的定价报告,该语句的特点是带有分组、排序、聚集操作并存的单表查询。查询会访问表中 95%-97%的行。因此可见,SQL 语句会根据业务量、复杂度以及涉及到的数据量来判断处理能力。最佳场景实践与压测 125 比如某语句用于查询最小代价供货商,则该语句为设计排序、聚集、子查询的多表查询。可以结合 JeMeter 工具将
129、 TPC-H 测试出的结果以图形的方式予以展示,方便后续在报表内使用。另外,PolarDB 也支持 pgbench。TPC-C、TPC-H 的部署需要专门的软件,相对较为复杂。而 pgbench 是系统自带的测试系统,可以测试并发的量。其优点为系统自带,短小精悍,容易扩展。如果期望功能更强大,可以在网站下载免费插件。缺点为测试结果浮动较大,需要多做几次测试然后取平均值。此外,执行过程中无法终端测试操作,只能等测试结束。最佳场景实践与压测 126 pgbench 测试的指标为 TBS 每秒钟处理的事务量。PolarDB-PG基于PostgreSQL开发,因此能够支持所有PostgreSQL原生态的插件。