前⾔:
在任何数据库的学习中,如果想深⼊的理解⼀种数据库系统,对于体系结构的理解和掌握都是重中
之重,本篇开始为⼤家正式开始HaloDB的体系结构介绍。
⼀、HaloDB的体系结构:
如下图1-1所⽰,Halo数据库的体系结构。数据库实例主要包含共享内存区域、本地内存区域和⼀系
列后台进程。其中共享内存区域主要由共享缓存、事务⽇志缓存构成。后台进程主要由Background Writer(数据写进程)、WALWriter(事务⽇志写进程)、Checkpointer(检查点进程)、Statistic Collector(统计信息收集进程)、Autovacuum(⾃动清理进程)等构成。数据库群集主要由数据⽂
件、事务⽇志⽂件及其它⼀些辅助⽂件组成。

1、HaloDB中的共享内存区域和本地内存区域:
在我们的HaloDB中,内存架构部分主要分为,本地内存区域(LocalMemoryArea)和共享内存区域 (SharedMemoryArea)两个部分,下⾯针对每个部分做详细的说明;
a.本地内存区域(LocalMemoryArea):本地内存区域是每个后台进程(BackendProcess)独⽴ 使⽤的内存空间。每个后台进程都有⾃⼰的本地内存区域,⽤于存储会话相关的数据和临时数据。本
地内存区域包括以下⼏个重要的组件:
• 栈(Stack):⽤于存储函数调⽤和局部变量等信息。
• 上下⽂(Context):⽤于管理内存分配和释放,每个上下⽂都有⼀个⽗上下⽂,形成⼀个层次结
构。
• 缓冲区(Buffer):⽤于存储查询结果集的数据,以及排序和哈希操作的中间结果。
• 连接信息(ConnectionInformation):⽤于存储与客⼾端连接相关的信息,如会话状态、权限等。
b.共享内存区域(SharedMemoryArea):共享内存区域是多个后台进程共享的内存空间,⽤于存储全局数据和缓存数据,以提⾼性能和效率。共享内存区域包括以下⼏个重要的组件:
• 缓冲池(BufferPool):⽤于缓存磁盘上的数据⻚,减少磁盘访问次数。
• 内存上下⽂(MemoryContext):⽤于管理共享内存的分配和释放。
• 后台进程信息(BackendProcessInformation):⽤于存储后台进程的状态和信息。
• 锁表(LockTable):⽤于管理并发访问的锁信息。
• WAL(Write-AheadLogging)缓冲区:⽤于存储事务⽇志的中间结果。
• 共享内存区域的⼤⼩是在启动HaloDB时配置的,可以根据系统的硬件资源和负载情况进⾏调整。
本地内存区域的⼤⼩则由每个后台进程独⽴管理,通常受到系统的内存限制。

HaloDB的后台进程
后台进程:
1、BackgroundWriter(数据写进程):
主要作⽤:后台写进程负责将脏数据⻚(DirtyBuffer)从共享缓冲池(SharedBuffer)异步写⼊ 磁盘,以减少磁盘写⼊的频率。它根据⼀定的策略来选择需要写⼊的数据⻚,并通过延迟写⼊ (DelayedWrite)机制来提⾼性能。
BackgroundWriter(数据写进程)⼯作过程如下:
a.监控脏数据⻚:BackgroundWriter定期扫描数据库缓冲区,监控其中的脏数据⻚。脏数据⻚是指
已经被修改但尚未写⼊磁盘的数据⻚。
b.决定写⼊时机:BackgroundWriter根据⼀定的策略决定何时将脏数据⻚写⼊磁盘。这个策略通常
基于缓冲区的使⽤情况、脏数据⻚的数量和系统负载等因素。
c.异步写⼊数据:⼀旦决定将脏数据⻚写⼊磁盘,BackgroundWriter将这些数据异步地写⼊磁盘,⽽不是等待前台进程需要这些数据时再写⼊。这样可以减轻前台进程的写⼊压⼒,提⾼数据库的性能。
d.更新元数据:BackgroundWriter在写⼊数据的同时,还会更新相关的元数据信息,包括系统表中
的数据库统计信息。这些统计信息⽤于查询优化器,以帮助⽣成更有效的查询计划。 通过执⾏这些操作,BackgroundWriter减轻了前台进程的写⼊压⼒,提⾼了数据库的性能和响应能
⼒。它异步地将脏数据⻚写⼊磁盘,并更新相关的元数据信息,以保证数据的⼀致性和持久性。同 时,BackgroundWriter的⼯作是基于⼀定的策略,以适应不同的系统负载和数据库使⽤情况。
简单画了⼀个BackgroundWriter的⼯作流程图如下,⽅便各位理解

2、WALWriter(事务⽇志写进程):
主要作⽤:
WAL写进程负责将事务⽇志(Write-AheadLog)从共享内存区域异步写⼊磁盘。它将事务的修改操作记录到事务⽇志中,以保证数据库的持久性和恢复能⼒。WAL写进程通过批量写⼊和延迟写⼊等 技术来提⾼性能。
WALWriter⼯作过程如下:
a.监控WAL缓冲区:WALWriter定期扫描WAL缓冲区,监控其中的WAL⽇志条⽬。WAL缓冲区是⼀个 内存区域,⽤于暂存事务⽇志。
b.决定写⼊时机:WALWriter根据⼀定的策略决定何时将WAL⽇志条⽬写⼊磁盘。这个策略通常基于 WAL缓冲区的使⽤情况、WAL⽇志条⽬的数量和系统负载等因素。
c.异步写⼊⽇志:⼀旦决定将WAL⽇志条⽬写⼊磁盘,WALWriter将这些⽇志条⽬异步地写⼊磁盘, ⽽不是等待事务提交时再写⼊。这样可以提⾼数据库的性能,因为事务提交时不需要等待⽇志写⼊完
成。
d.更新WAL位置:WALWriter在写⼊⽇志的同时,还会更新WAL位置(WALLSN)。WAL位置是⼀个 递增的标识符,⽤于记录已经写⼊磁盘的WAL⽇志的位置。这个位置信息对于数据库的恢复和故障恢 复⾮常重要。
通过执⾏上述的操作,WALWriter确保了事务⽇志的持久性和⼀致性。
简单画了⼀个WALWriter的⼯作流程图如下,⽅便各位理解

3、Checkpointer(检查点进程):
主要作⽤:
a.触发检查点:Checkpointer定期触发检查点操作,以将内存中的数据写⼊磁盘。检查点的触发可以
基于时间间隔、⽇志⼤⼩或其他策略来确定。
b.写⼊脏⻚:⼀旦检查点被触发,Checkpointer开始将脏⻚(已被修改但尚未写⼊磁盘)写⼊磁盘。 脏⻚是指在内存中已被修改的数据⻚。
c.停⽌写⼊:在开始写⼊脏⻚之前,Checkpointer会通知其他进程停⽌向磁盘写⼊新的脏⻚。这是为 了确保在检查点期间不会有新的修改被写⼊磁盘,以保持⼀致性。
d.写⼊过程:Checkpointer按照⼀定的顺序将脏⻚写⼊磁盘。在HaloDB中,Checkpointer使⽤了⼀ 种称为"Write-AheadLogging"(WAL)的机制。它将脏⻚的修改操作写⼊到WAL⽇志⽂件中,然后再 将脏⻚本⾝写⼊磁盘。这种⽅式可以提⾼写⼊的效率和可靠性。
e.更新检查点位置:在写⼊完成后,Checkpointer会更新检查点位置,以记录已经完成的检查点。这 样,在系统恢复时,可以根据检查点位置来确定从哪⾥开始进⾏恢复操作。
f.恢复写⼊:⼀旦检查点完成并且检查点位置被更新,Checkpointer会通知其他进程可以继续向磁盘 写⼊新的脏⻚。
此外:
在我们的HaloDB中,Checkpointer还有⼀些额外的⼯作:
a.⾃适应检查点:HaloDB的Checkpointer会根据系统的负载情况和性能指标来⾃动调整检查点的触 发频率,以平衡写⼊性能和系统负载。
b.后台写⼊:HaloDB的Checkpointer可以与其他后台进程(如BackgroundWriter)协同⼯作,以
提⾼写⼊的效率和并发性。
通过Checkpointer的⼯作,HaloDB数据库可以保证数据的持久性和⼀致性。它定期将内存中的数据写
⼊磁盘,并使⽤WAL机制来确保写⼊的可靠性。这样,在系统故障或崩溃时,可以通过检查点位置和 WAL⽇志来进⾏数据恢复和⼀致性保证。

4、StatisticCollector(统计信息收集进程):
主要作⽤:
StatisticCollector(统计信息收集进程)是PostgreSQL数据库中的⼀个重要组件,它负责收集和更新 数据库对象的统计信息,以供查询优化器使⽤。
a.⽬的:StatisticCollector的主要⽬的是收集和维护数据库对象的统计信息,包括表、索引、列等。
这些统计信息对于查询优化器来说⾮常重要,它们帮助优化器选择最佳的查询计划,提⾼查询性能。
b.统计信息类型:StatisticCollector收集的统计信息包括⾏数、唯⼀值数量、空值数量、最⼩值、最
⼤值等。这些信息可以帮助优化器估计查询的选择性和数据分布情况。
c.统计信息更新:StatisticCollector会定期扫描数据库对象,收集最新的统计信息。它会根据配置的
参数(如autovacuum参数)来确定统计信息更新的频率和⽅式。通常情况下,当表或索引的数据发⽣ 变化时,StatisticCollector会⾃动触发统计信息的更新。
d.统计信息存储:StatisticCollector将收集到的统计信息存储在系统表pg_statistic和 pg_statistic_ext中。这些表包含了每个数据库对象的详细统计信息,以及⼀些全局统计信息。
e.查询优化器使⽤:查询优化器在⽣成查询计划时会使⽤StatisticCollector收集的统计信息。通过分 析统计信息,优化器可以选择最佳的索引、连接顺序和操作符等,以提⾼查询性能。
f.配置参数:在PostgreSQL的配置⽂件中,可以通过⼀些参数来控制StatisticCollector的⾏为,如 autovacuum参数⽤于控制统计信息的⾃动更新频率。
总之,StatisticCollector是PostgreSQL数据库中负责收集和维护统计信息的重要组件。它通过定期扫
描数据库对象,收集最新的统计信息,并存储在系统表中。这些统计信息对于查询优化器来说⾮常重 要,它们帮助优化器选择最佳的查询计划,提⾼查询性能。
⼯作过程如下:
a.启动:StatisticCollector在PostgreSQL数据库启动时⾃动启动,并在后台运⾏。
b.扫描对象:StatisticCollector会定期扫描数据库中的表、索引和列等对象,以收集它们的统计信
息。扫描的频率和⽅式可以通过配置参数进⾏调整。
c.统计信息收集:当StatisticCollector扫描到⼀个对象时,它会收集该对象的统计信息。这些统计信
息包括⾏数、唯⼀值数量、空值数量、最⼩值、最⼤值等。
d.统计信息更新:StatisticCollector会将收集到的统计信息与已有的统计信息进⾏⽐较,并根据⼀定
的算法和策略来更新统计信息。通常情况下,当表或索引的数据发⽣变化时,StatisticCollector会⾃ 动触发统计信息的更新。
e.统计信息存储:StatisticCollector将更新后的统计信息存储在系统表pg_statistic和 pg_statistic_ext中。这些表包含了每个数据库对象的详细统计信息,以及⼀些全局统计信息。
f.查询优化器使⽤:查询优化器在⽣成查询计划时会使⽤StatisticCollector收集的统计信息。通过分 析统计信息,优化器可以选择最佳的索引、连接顺序和操作符等,以提⾼查询性能。
g.⾃动更新:StatisticCollector可以根据配置参数(如autovacuum参数)来确定统计信息的⾃动更 新频率和⽅式。这样可以确保统计信息始终与实际数据保持⼀致。
5、Autovacuum(⾃动清理进程):
主要作⽤:
HaloDB中的Autovacuum(⾃动清理进程)是⼀个重要的后台进程,负责⾃动管理表的清理和维护
⼯作。
⼯作内容如下:
a.清理过期⾏:Autovacuum会检查表中的过期⾏(已被标记为删除但尚未被实际清理的⾏),并将
其删除。这有助于回收磁盘空间,并提⾼查询性能。
b.合并碎⽚化的⻚⾯:当表中的⾏被删除时,可能会导致⻚⾯碎⽚化。Autovacuum会合并碎⽚化的 ⻚⾯,以减少磁盘访问和提⾼查询性能。
c.更新统计信息:统计信息对于查询优化器的性能决策⾮常重要。Autovacuum会更新表的统计信 息,以确保查询优化器能够做出准确的执⾏计划。
d.⾃动分析:Autovacuum还负责⾃动分析表的统计信息。它会检查表的更新频率和配置参数,判断
是否需要进⾏⾃动分析。⾃动分析将收集表的统计信息,以便查询优化器能够做出更好的执⾏计划。
e.配置参数:PG数据库提供了⼀些配置参数,⽤于控制Autovacuum的⾏为。这些参数包括⾃动清理 的阈值、⾃动分析的频率、并发清理的并发度等。管理员可以根据实际需求进⾏配置。
f.⾃动触发:Autovacuum会根据表的更新情况和配置参数⾃动触发清理和分析操作。它会根据表的更
新频率、删除操作、空闲空间等因素来判断是否需要执⾏⾃动操作。
g.⼿动触发:除了⾃动触发,管理员也可以⼿动触发Autovacuum操作。这可以通过执⾏VACUUM、 ANALYZE或VACUUMANALYZE语句来实现。
h.进程调度:PG数据库可以同时运⾏多个Autovacuum进程,以处理多个表的清理和维护⼯作。进程 调度器会根据系统负载和配置参数来决定启动和停⽌Autovacuum进程。

⼆、HaloDB的物理结构:
在HaloDB中,软件的安装⽬录是在/u01/app/halo/product/dbms/14⽬录下。⼀般⽤环境变量 HALO_HOME作为数据库软件安装的根⽬录。数据⽂件存放位置则根据环境变量HALOHOME来⾃定义
指定。
⽬录的初始化在pg_ctlinit-D/data/halo动作完成后,数据⽬录会⽣成若⼲配置⽂件,如下所⽰:

下⾯针对这些配置⽂件,做些简单的说明:
postgresql.conf:HaloDB的主配置⽂件,基本所有的参数配置均在此⽂件中。
postgresql.auto.conf:HaloDB的附加配置⽂件,使⽤后altersystem修改的配置存储在该⽂件中。
pg_hba.conf:HaloDB数据库的认证配置⽂件。
pg_ident.conf:ident认证⽅式的⽤⼾映射⽂件。
postmaster.opts:记录服务器上次启动的命令⾏参数,此⽬录下还会⽣成若⼲的⼦⽬录。

1、数据存储结构:
在Halo数据库中,数据库群集是⼀组数据库的集合。每个数据库⼜是⼀组对象的集合。详细的架构
图如下图所⽰:

在Halo数据库体系中,数据库群集(DatabaseCluster)类似⼀个容器,在该容器内可以创建多个独
⽴的数据库,分别对应不同的系统,即多租⼾。数据库群集初始化后,会创建3个默认的数据库: halo0root、template1和template0。
• halo0root:管理库,群集管理使⽤,请不要删除。
• template1:正如其名称所⽰,这是⼀个模板库,后⾯通过CREATE DATABASE命令创建的库默认 都以它为模板进⾏创建。
• template0:也是⼀个模板库。和template1的区别在于,通过template1创建的库部分库属性, 如字符集是⽆法更改的。⽽通过template0就可以。 上图所述表空间所存放的物理位置放置在$HALODATA/base⽬录下,该⽬录下的存放很以OID命名的⽂ 件夹,就是该集簇下数据库存放的物理位置。接下来我们看下具体例⼦: 查询⽬前HaloDB中现有的数据库
halo0root=\l
List of databases Name | Owner | Encoding | Collate | Ctype | Access
privileges
-----------+-------+----------+-------------+-------------+------------------
halo0root | halo | UTF8 | en_US.UTF-8 | en_US.UTF-8 | halozz1 | halo |
UTF8 | en_US.UTF-8 | en_US.UTF-8 | =Tc/halo
template0 | halo | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/halo +| | | | |
halo=CTc/halo
template1 | halo | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/halo +| | | | |
halo=CTc/halo
testzz | halo | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
(5 rows)
查询每个数据库所对应的OID号码
halo0root=# select oid,datname from pg_database; oid | datname
-------+-----------
14509 | halo0root
1 | template1
14508 | template0
16385 | testzz
24987 | halozz1
(5 rows)
进⼊到物理存储位置:

如上图所⽰,14509所对应的halo0root模板库的OID为14509跟物理路径下的⽂件夹名称是⼀⼀对
应关系,该⽂件下下存放该库的表,索引等数据⽂件。图1-2描述了这种关系。其中,表的数据⽂件 内部⼜被划分为固定⻓度的⻚(Page),通常为8KB。每个⽂件中的⻚从0开始按顺序编号,该编号 即为⻚号。⻚通常⼜被划分为⻚头和数据,即PageLayout中的内容。数据按⾏(Row)进⾏存储, 每⼀⾏⼜被划分为⾏头和实际数据,即RowLayout中的内容。索引的布局与表的布局类似。
2、HaloDB数据库内核架构:
HaloDB数据库的核⼼设计思想是插件式内核,这也是Halo数据库的⼀个最显著的特性。

图1-3描述了Halo数据库实例的主要成分。通过插件式内核的设计,可以⾮常灵活的实现多种协
议的兼容、多种语法的兼容、以及分布式存储引擎(HFSX,发展中技术)的⽀持等,这个重要特性也 是Halo能够称之为新⼀代统⼀数据库的重要原因。如果有对内核层⾯感兴趣的朋友,可以考虑加⼊我 们公司进⾏深⼊的学习和体验。
三、HaloDB的逻辑架构:
说到逻辑结构,确实对新⼿来说理解起来有⼀些的难度,来话不多说,直接上图。

1、数据库集簇(Databasecluster):
它是指有单个HaloDB服务器实例管理的数据库集合,组成数据库集群的这些数据库使⽤相同的全
局配置⽂件和监听端⼝、共⽤进程和内存结构。⼀个DataBaseCluster可以包括:多个DataBase、多 个User、以及Database中的所有对象。如上图所⽰。
2、数据库(Database):
在HaloDB中,数据库本⾝也是数据库对象,并且在逻辑上彼此分离,除数据库之外的其他数据库
对象(例如:表、索引等等)都属于他们各⾃的数据库。
3、表空间(tablespace):
在HaloDB中,数据库在逻辑上分成多个存储单元,称作表空间。表空间⽤作把逻辑上相关的结构
放在⼀起。数据库逻辑上是由⼀个或多个表空间组成。初始化的时候,会⾃动创建pg_default和 pg_global两个表空间。
halo0root=# \db
List of tablespaces Name | Owner |
Location
------------+-------+-----------
halozz_tbs | halo | /data/tbs ###⾃⾏创建的表空间,⾮默认
pg_default | halo |
pg_global | halo |
(3 rows)
创建表空间的语句可以参考如下:
halo0root=# create tablespace 表空间名称 location '所创建表空间的物理路径';
4、模式(Schema):
在HaloDB中,当创建⼀个数据库时,会为其创建⼀个名为public的默认Schema。Schema是数
据库中的命名空间,在数据库中创建的所有对象都是在Schema中创建,⼀个⽤⼾可以从同⼀个客⼾端 连接中访问不同的Schema。⽽不同的Schema中可以有多个同名的Table、Index、View、 Sequence、Function等等数据库对象。可以通过下⾯的⽅式来查看当前数据库的Schema:
halo0root=# \dn
List of schemas Name | Owner
--------+-------
public | halo
sys | halo
(2 rows)
5、段(segment):
在HaloDB中,⼀个段是分配给⼀个逻辑结构(⼀个表、⼀个索引或其他对象)的⼀组区,是数据
库对象使⽤的空间的集合;段可以有表段、索引段、回滚段、临时段和⾼速缓存段等。
6、区(extent):
在HaloDB中,区是数据库存储空间分配的⼀个逻辑单位,它由连续数据块所组成。第⼀个段是由
⼀个或多个盘区组成。当⼀段中间所有空间已完全使⽤,为该段分配⼀个新的范围。
7、块-block(Page):
数据块是HaloDB管理数据⽂件中存储空间的单位,为数据库使⽤的I/O的最⼩单位,是最⼩的逻 辑部件。默认值8K。
halo0root=# SHOW block_size;
block_size
------------
8192
(1 row)
8、数据库对象(Databaseobject):
在HaloDB中的所有数据库对象⽐如表、视图、索引、序列、函数都由各⾃的对象标识符(OID) 进⾏内部的管理。例如,数据库的OID存储在pg_database系统表中,可以通过下⾯的语句进⾏查询。
testzz=# select oid,relname,relkind,relfilenode from pg_class where
relname ='emp';
oid | relname | relkind | relfilenode
-------+---------+---------+-------------
16391 | emp | r | 16391
(1 row)
⽽数据库中的表、索引、序列等数据库对象的OID则存在了pg_class系统表中,例如可以通过下⾯ 的语句查询前⾯创建的表的OID。
testzz=select oid,relname,relkind,relfilenode from pg_class where relname
='emp';
oid | relname | relkind | relfilenode
-------+---------+---------+-------------
16391 | emp | r | 16391
(1 row)
⼩tips:pg_database,pg_class这两个视图真的很常⽤,请务必熟悉~
最后:
请大家持续关注Halo数据库,如需试用请直接联系我们。




