HyperSQL调研学习文档(二)

2022-04-08 00:00:00 数据 语句 数据库 对象 子类

近调研HyperSQL,把整理的资料记录一下,并分享给大家,由于时间略紧,内容肯定有遗漏和谬误的地方,欢迎大家指正。本人也会持续的修改更新。

2. HyperSQL架构介绍
2.1 通信层次架构

2.1.1 socket层

涉及到的类:ServerSocket, Socket, HttpURLConnection
在非In-Processing模式下,HyperSQL服务端与客户端底层通过socket或http通信:由Server端启动一个ServerSocket监听某一端口,然后Client端通过Socket或者HttpURLConnection来与之通信。对于每个连接,服务端通过创建一个ServerConnection或WebServerConnection线程来与之通信及提供服务。

2.1.2 Result层

涉及到的类:Result
提供了协议层的Session层通信的基础语义单元Result以及对应的常量定义、属性及元数据描述。在连接,服务端及Session实例之间通信的基本单元。
HSQLDB的Result对象包括了所有的请求(例如更改或查询session设置,分配和执行语句等等)和所有的响应(例如异常指示,更新条数,结果集和结果集元数据)。同时实现了HSQL的线协议,用于将这些请求和响应在网络上传递。
内部使用一个导航器(navigator)来服务查询结果数据。

2.1.3 Session层

涉及到的类:SessionInterface, Session, ClientConnection, ClientConnectionHttp
• SessionInterface:Session和它的远程代理类的公共接口。用于JDBC接口的实现类与数据库引擎在session级别的通信。

• ClientConnection:远程session代理实现基类。使用Result实例发送和接收数据。该实现使用了新的HSQL协议。

• ClientConnectionHttp:基于HTTP协议的远程session代理实现。使用了新的的HSQLDB HTTP子协议。

• Session:会话(Session)代表了某用户在一段未中断时间内连接到数据库并执行了一系列操作这样的概念。所有的SQL语句都在session中执行。当与一个数据库引擎的连接建立的时候,一个session就开始了。该session的授权凭证就是启动这个session的用户名。一个session有若干个属性。这些属性默认在启动时从数据库参数设置中获得。
SQL语句一般都是事务型语句。当一个事务语句执行的时候,如果当前执行进程没有事务,它会启动一个。如果SQL数据(保存在表中的数据)在事务中修改了,这个修改可以通过ROLLBACK语句撤销。
当COMMIT或ROLLBACK语句执行以后,事务就结束了。每一个SQL语句都以原子性的方式工作:它要么成功要么不改动任何数据的失败。
如果一个单个语句失败了,一个错误被抛出但是事务一般不会结束(可以在应用代码中捕捉并自主执行rollback或commit)。然而,一些错误是由执行了与其他同时存在的会话中正在执行的语句相冲突的语句所导致的。这种错误除了抛出异常之外,还会导致一个明确的ROLLBACK。
Schema定义和操作语句也是事务性的,基于SQL标准。HyperSQL2.3对于这种事务会在执行之前和之后自动执行commit。如此一来,schema相关的语句不能被回滚。这种情况有可能在未来的版本里改变。
还有一些语句不是事务性的。大部分这样的语句都是用来改变session属性的。这些语句由SET关键字开头。

2.1.4 Connection层

涉及到的类:ServerConnection, WebServerConnection, JDBCConnection
在客户端每次调用:

DriverManager.getConnection(url, "SA", "")
时就会创建一个JDBCConnection,其内部封装了SessionInterface的实现类,在Result通信单元的语义之上负责与Server端的Session进行通信,在InProcess模式下就是Session本身。实现了JDBC的Connection接口。
每创建一个JDBCConnection,在Server端就会对应的创建一个ServerConnection或WebServerConnection的线程与之对应,其中维护了数据库会话Session。

2.1.5 Client基于JDBCConnection之上对JDBC API的实现

HyperSQL Client端终将对于数据库的操作封装为JDBC API,对于JDBC API的各种接口提供了具体的实现类。
这些实现类基于上面提到的JDBCConnection,JDBCConnection本身就是JDBC接口Connection的实现类。
这些类包括:JDBCDriver, JDBCResultSet, JDBCStatement, JDBCPreparedStatement等。
如此一来,客户端就可以使用标准的JDBC接口和标准SQL语句来操作HyperSQL数据库了。

2.2 数据库引擎架构

2.2.1 DatabaseManager

管理Database引擎核心,对数据库的初始化进行处理,尝试连接JVM中的HSQLDB数据库(或JVM内的某一个classloader中的)。如果database尚未打开,就打开它;如果已经打开了就连接它。
DatabaseManager使用一个HashMap来缓存数据库。如此一来就允许不同的服务端实例和直接连接(In-Process)使用相同的数据库。
DatabaseManager也管理着以Server实例为key,以Server打开的databases集合为value的map,并在database关闭时通知其关联的每一个server。

2.2.2 SessionManager

管理Database Session。维护了一个从sessionId到session对象的map,负责管理打开和关闭session对象。
在数据库初始化时创建,创建时就委托database的userManager返回一个system权限的用户,并使用该用户创建两个与数据库连接的会话(session):sysSession,sysLobSession。

2.2.3 SchemaManager

管理所有Schema相关的数据库对象(SchemaObject)。文档中对SchemaObject的定义是指包含数据或者管理数据或者对数据执行操作的database对象,例如:表、视图、字段、约束、等等。直观的说,schema对象就是用于描述数据库的各个概念及这些概念之间逻辑关系的元数据。
另外,Schema也是一个SchemaObject,schema代表了一组schema对象及相应权限的概念,是在catalog之下又进行的一层逻辑划分。每一个schema对象属于一个特定的schema。它们之间的逻辑关系是:一个数据库Database对象与一个SchemaManager对象是一一对应的,一个Database对象中只含有一个catalog。SchemaManager中维护着一个Schema的列表,表示一个database或叫做catalog中包含一个或多个schema,而schema对象内部维护着属于本schema的那些schemaObject,例如表、视图、约束、索引、Trigger、Routine等等。另外,SchemaManager还维护了各个SchemaObject之间的依赖关联关系,referenceMap是一个多值的map,其中key表示被关联对象,value表示关联key的对象,可以为多值。

直观的说,SchemaManager是用来统一管理数据库中各元素(表、视图等等)结构定义元数据的。任何DDL都需要通过SchemaManager来修改SchemaObject来达到更改数据库结构定义的目的。

2.2.4 TransactionManager

事务管理器,用于处理需要事务的语句的执行,概括的说就是保证在一个事务中的所有语句要么都成功,要么都不成功。TransactionManager是一个接口,定义了HSQLDB事务管理器对外提供的公共方法,以及支持的事务并发管理模型。HyperSQL 2支持三种不同的并发控制模型:两阶段锁(2PL),这是默认模型,多版本并发控制(MVCC)以及一个混合模型,两阶段锁加上多版本行(MVLOCKS)。在每一个模型中,支持如下4种标准事务隔离级别中的若干个:READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ 和 SERIALIZABLE。

并发控制模型是设置在database上的并影响所有的sessions,并非为单个session设置。而隔离级别是一个针对单个SQL session的属性,所以不同的session可以拥有不同的隔离级别。
HyperSQL在所有的事务模型中都是完全支持多线程的。sessions可以并发的工作并可充分利用处理器的多个核心。

可以通过语句修改一个正在运行的database的并发控制模型。通过DBA角色的用户执行:

SET DATABASE TRANSACTION CONTROL { LOCKS | MVLOCKS | MVCC }
2.2.5 UserManager

用户管理器,用于管理一个数据库实例中的用户对象。特殊用户PUBLIC_ROLE_NAME和SYSTEM_AUTHORIZATION_NAME就在这里创建和管理。

SYSTEM_AUTHORIZATION_NAME还有一点特殊的就是,它没有在UserManager的userList中维护,而是维护在GranteeManager中(PUBLIC_ROLE_NAME是维护在userList中)。

其内部持有角色管理器GranteeManager的实例,与Database及GranteeManager均为一一对应的关系。

2.2.6 LobManager

大字段管理器,里面包含了LobStore的概念。

2.2.7 HsqlNameManager

提供了SQL对象的命名管理。定义了静态内部类HsqlName,代表一个囊括了schema,parent,owner以及type概念的名字,并改进了对于引擎中含有多个数数据库时的自动命名机制。其中的方法检查用户定义的名字,并分发系统创建的名字。
一些以SYS_开头的名字是保留给系统生成名的。这些名字在isReserveName(String name)方法中进行判断,并通过makeAutoName(String type)工厂方法来生成。
sysNumber字段用来创建系统生成名。它被设置为已存在的以SYS_xxxxxxx_INTEGER格式的名字所用到的大值。正如数据库定义语句DDL一定是在任何更新命令之前执行的,所以任何新的系统生成名都会比已存在的名称拥有一个更大的数字后缀。

2.2.8 GranteeManager

角色管理器,包含了针对某一个数据库的角色(Grantee)对象的集合,并支持创建,查找,修改和删除该数据库中的角色对象的操作;并在其中直接维护了一个拥有Administrative权限的用户,名为SYSTEM_AUTHORIZATION_NAME。
2.2.9 PersistentStoreCollactionDatabase

基于数据库的存储仓储管理器,借助于其对应的database的Logger来实际创建PersistentStore。并在内部维护一个LongKeyHashMap rowStoreMap来缓存本数据库使用的所有持久化仓储,达到持久化仓储统一管理及复用的目的。

2.2.10 CheckPointRunner

定期执行CheckPoint任务的线程,使用SYS权限执行CHECKPOINT命令。
checkpoint是一个内部事件,这个事件激活以后会触发数据库写进程(DBWR)将数据缓冲(DATABUFFER CACHE)中的脏数据块写出到数据文件中。
在数据库系统中,写日志和写数据文件是数据库中IO消耗大的两种操作,在这两种操作中写数据文件属于分散写,写日志文件是顺序写,因此为了保证数据库的性能,通常数据库都是保证在提交(commit)完成之前要先保证日志都被写入到日志文件中,而脏数据块则保存在数据缓存(buffer cache)中再不定期的分批写入到数据文件中。也就是说日志写入和提交操作是同步的,而数据写入和提交操作是不同步的。这样就存在一个问题,当一个数据库崩溃的时候并不能保证缓存里面的脏数据全部写入到数据文件中,这样在实例启动的时候就要使用日志文件进行恢复操作,将数据库恢复到崩溃之前的状态,保证数据的一致性。检查点是这个过程中的重要机制,通过它来确定,恢复时哪些重做日志应该被扫描并应用于恢复。

一般所说的checkpoint是一个数据库事件(event),checkpoint事件由checkpoint进程(LGWR/CKPT进程)发出,当checkpoint事件发生时DBWR会将脏块写入到磁盘中,同时数据文件和控制文件的文件头也会被更新以记录checkpoint信息。

2.2.11 TimeoutRunner

与数据库相关的,多个session公用的超时管理线程。其中维护了需要管理动作超时的session的列表,session中持有TimeoutManager实例,TimeoutManager是与session相关的,针对某一个动作的超时管理器。TimeoutRunner与TimeoutManager一起,负责管理针对某一个数据库的所有session的超时检查。

TimeoutRunner借助HsqlTimer定时每秒一次执行其run方法,在run方法中将sessionList中的每一个session的timeoutManager执行一次checkTimeout()方法来判断是否超时。若超时,就将该session从sessionList中删除。

TimeoutManager内部记录了其对应的session当前执行动作的actionTimestamp、剩余多长时间超时(单位是秒)、是否被废除的标志(aborted)。并实际判断动作是否超时:将剩余时间减一后判断是否小于等于0,若超时,则重置当前session,其中会将session.abortAction置为true。

2.2.12 Logger

为持久化和日志提供公共接口,实现了一个存储管理包装类,为数据库类提供了一致的且总是可用的存储管理接口。例如数据库表的实际持久化仓储就是通过Logger来分配的。

3. 源码结构概述

去除掉auth(可选的认证模块), cmdline, cmdline.sqltool, sample, test, util, util.preprocessor等外围代码,总共有java类390多个,去掉空格后的代码20W行左右。各部分大概说明如下:

3.1 basic包

ClientConnection及ClientConnectionHttp: 服务端Session在客户端的远程代理实现,实现接口SessionInterface,基于socket或http通过Result封装的基本数据单元与Server端的session通信。
ColumnBase: 代表变量、数据表或查询结果的字段的基本实现类
ColumnSchema:表示SQL表字段元数据的实现类
Constraint: 表约束的实现,关联了该约束所使用的索引。约束类型有:FOREIGN_KEY; MAIN; UNIQUE; CHECK; PRIMARY_KEY; TEMP;
ConstraintCore: 保存了Constraint的核心字段,并提供duplicate复制方法。
DatabaseManager, Database及其相关类。数据库引擎核心,内部维护了数据库引擎的各个模块(各种Manager),并通过Session与外部通信。
Expression及其子类:泛指表达式,子类代表着各种各样的表达式,常用的例如:常量表达式、字段表达式、逻辑表达式等
HsqlDateTime: 提供了一堆用于转换Date和Timestamp字符串到对应的JavaObject的静态方法。
HsqlNameManager: 提供了SQL对象的命名管理。其中的方法检查用户定义的名字,并分发系统创建的名字。定义了静态内部类HsqlName,代表一个囊括了schema,parent,owner以及type概念的名字。
Like: 执行like查询时的可复用对象,在ExpressionLike中使用。
NumberSequence: 维护数字序列,例如ID的自增序列等。
Op*: 表达式操作类型的枚举。
ParserBase及其子类:对于各种各样SQL语句的解析,将其解析为不同类型的Statement具体实现类。语句类型有Command,DML,DQL,DDL等等,由不同类型的parser实现类解析为不同类型的Statement实现类。
RangeVariable: 代表了一个包含过滤条件的有一定取值范围的数据源,该数据源的类型可以是一个表(Table及TableDerived),参数及变量等。例如select * from table t where t.id>3; 解析成StatementQuery以后,其rangeVariables数组中就会有一个RangeVariable对象,代表了包含t.id>3这个condition的t表。
RangeGroup: 代表持有了一组范围数据源的概念,其中每一个数据源都代表一个取值范围。实现该接口的类从逻辑上讲都可能对应着多个数据源,例如:QueryExpression,QuerySpecification,Routine,StatementCompound。
RangeVariableResolver: 决定在查询过程中JOIN和WHERE表达式如何使用,并确定访问table时使用哪个索引。
Routine: 数据库中被SQL调用的方法或存储过程。HyperSQL2.4支持SQL标准的两个部分:用SQL写成的Routines使用SQL/PSM标准;用Java写成的Routines由SQL/JRT标准。HyperSQL也支持用户定义的聚合方法(用SQL或Java写的),这扩展了SQL的标准。这些聚合方法保存在SchemaManager中,在StatementSchema(DDL)中进行处理(注册、删除、更改)。ParserRoutine专门提供了解析Routine的方法。
RoutineSchema: 管理多个同名的Routine,由此支持Routine的重载。这些routine的名称相同但是参数不同,拥有相同的name但是各自有不同的specificName。
Row及其子类:代表了数据库中行数据的对象。在Table中通过Session的txManager,由RowAction来具体操作。
RowActionBase,RowAction: 代表了一个针对某个Row的单个的insert/delete/rollback/commit动作。
Scanner,Token,Tokens: 用于扫描和语法上解析SQL语句,供Parser使用。
SchemaManager,SchemaObject: 表示Schema对象及对Schema对象的管理,文档中对SchemaObject的定义为包含数据或者管理数据或者对数据执行操作的database对象,例如:表、视图、等等。直观的说,schema对象就是用于描述数据库的各个概念及这些概念之间逻辑关系的元数据。
Schema: 代表了一组schema对象及相应权限的概念,是在catalog之下又进行的一层逻辑划分。
SessionManager, Session及相关类:SQL session的实现类,代表了与数据库的连接通信,与客户端的ClientConnection和ClientConnectionHttp同处于一个层次上。
SetFunction: SQL集合函数的实现(当前只有聚合函数)
SortAndSlice: 查询语句中的ORDER BY和LIMIT的实现。代表排序和分页,在Expression中包含。
SqlInvariants: 不变的Schema对象
Statement及其子类:将各种类型的SQL语句解析后会形成不同类型的Statement,其中包含了条件表达式Expression,字段表达式ExpressionColumn[]和返回值元数据ResultMetaData等等。
Table及其相关类(包括View):持有一个数据库表对应的数据结构,和创建一个表的方法。内部持有各种约束(Constraint)定义,字段定义,触发器(Trigger)定义等等,以及该表对应的PersistentStore。
TransactionManager及其子类:事务管理器及其具体实现,包括了二阶段提交2PL实现,多版本并发控制MVCC实现等等
Trigger和TriggerDef:代表了一个Hsql Trigger的定义。Trigger就是内嵌在当前运行的数据库中,在SQL数据改变时会被监控并执行动作。Trigger可以是Java也可以是SQL实现。
TypeInvariants:不变的类型Schema

3.2 dbinfo包

系统表管理的基类。可方便的查询获得系统表,内部含有一个获得当前classpath中存在的Jar包中完整实现的类。

3.3 error包

错误处理,定义了所有的错误码,并提供一系列静态函数来生成HsqlException。

3.4 index包

NodeAVL:是AVL平衡二叉树节点的基类。各子类的实现在持有AVL树上其他节点的方式,或持有本节点的行数据的实现上有所不同
IndexAVL:使用AVL平衡二叉树的方式实现的Index结构,其与PersistentStore结合实现对于表记录的保存,实际存储在store的indexList和accessorList中。

3.5 jdbc包

基于底层的SessionInterface的实现,提供了JDBC API的实现。JDBCConnection依赖于ClientConnection或ClientConnectionHttp。而其他概念例如:JDBCResultSet, JDBCStatement, JDBCDriver, JDBCPreparedStatement等等都基于JDBCConnection。
jdbc.pool:提供了数据库连接池实现、提供了XAConnection,XADataSource及XAResource接口的事务型数据库、连接及资源的实现。

3.6 lib包及子包
lib.java: 用于处理JDK5与其上版本的差别
lib.tar:处理压缩文件
lib:提供了一系列基本的工具及数据结构
• HsqlByteArrayInputStream:是java.io.ByteArrayInputStream和java.io.DataInputStream的替代类。定义了一个以字节数组buffer保存的输入源,并定义了从中读取各种类型数据的方法。

3.7 map包

提供了一系列基于Map的私有实现,以满足不同需求

3.8 navigator包


包含了对于Object列表的遍历功能。基类提供基于方位的导航和检索功能,子类提供对象获取的具体实现。
其中的RowSetNavigatorClient是一个All-In-Memory的实现,内部持有SessionInterface的代理实现。并在getCurrent时,若发现currentpos>=currentoffset+table.length,就会触发getBlock方法,其中调用session.getRows(id, offset, recnum)

3.9 persist包

CachedObject, CachedObjectBase及其子类:用于表示可在缓存中存储东西的类:BitMapCachedObject, DirectoryBlockCachedObject, IntArrayCachedObject
PersistentStore及其实现类:RowStoreAVL系列,基于AVL平衡二叉树实现的持久化仓储(针对不同类型的Table提供了不同类型的实现);SimpleStore及其子类,使用基于file的管理实现持久化仓储。
PersistentStoreCollection及其实现类:管理基于某个概念范围(一个库或一个session)的一组持久化仓储。在该范围内持久化仓储是可复用的。
DataFileCache及其子类:基于file对CACHE类型的Table进行持久化管理。
DataFileDefrag:基于文件的CACHE表实现的碎片整理器。
DataSpaceManager及其子类:数据空间管理,用于管理DataFileCache。
EventLogInterface,Logger:为持久化类和日志类提供公共接口,实现了一个存储管理包装类,为数据库类提供了一致的且总是可用的存储管理接口。其中Log类型的成员对象负责管理一些数据文件,如.properties, .data, .log, .lob等。
HsqlProperties及其子类:对Properties进行封装,将取值限定到特定的类型,并允许保存和加载。
LobManager:管理大字段,持有LobStore的概念,代表了大字段的存储管理,其子类LobStoreInJar, LobStoreInMem, LobStoreInRAFile提供了具体存储的不同实现。
LockFile:合作用的锁文件的基类实现。
RandomAccessInterface及其子类:包装一个random access的文件,用来作为CACHED Table的实际存储仓储。
RAShadowFile:包装random access file来达到.data文件备份时追加增长的效果。
ScriptRunner:从SQL log日志中将Database恢复过来。意思是读取脚本文件并执行其中的脚本。
TableSpaceManager:管理Table的rows的空间分配(其中具体动作委托给DataSpaceManager去执行)。
TextFileReader及其子类:读TEXT格式的文件。



3.10 result包


提供了协议层的Session层通信的基础语义单元Result以及对应的常量定义、属性及元数据描述



3.11 rights包


提供了角色(Guarantee),用户(User),权利(Right)。以及角色管理器和用户管理器来统一管理。



3.12 rowio包


从一个数据库表的行(Row)读取/写入数据,定义了与存储无关的方法,由各不同的子类去实现存储相关的方法
RowInputBase:继承自HsqlByteArrayInputStream的一个抽象类,以模板方法模式定义了从数据库一行数据中读取不同类型数据的方法,并提供一系列具体的与存储相关的抽象方法供子类去实现。
RowInputInterface:用于读取数据库行数据的公开接口(实现类会实现从各种不同来源中读取数据库的行数据)
RowInputBinary:继承自RowInputBase并实现了RowInputInterface接口。提供了从字节数组中读取数据库行数据的方法。数据格式就是v.1.6.x版本的数据库用来存储CACHED表的格式。
RowInputBinary180:继承自RowInputBinary,重写了读取时间与日期的方法,在时间与日期的读取上与RowInputBinary有所不同。
RowInputBinaryDecode:继承自RowInputBinary,添加了解码能力,与RowOutputBinaryDecode一起在RowInputBinary/RowOutputBinary的基础上增加了加密解密功能
RowInputText:继承自RowInputBase并实现了RowInputInterface接口。用于从TEXT类型的表的文件中读取数据库行数据。
RowInputTextQuoted:继承自RowInputText并处理原始文件中带引号的字段,因为TEXT类型的表文件中的字段并不需要加引号。该类中的方法对于加引号的字段去掉其引号,并可处理这种情况下导致的字段内部引号再加引号的情况。
RowInputTextLog:继承自RowInputBase并实现了RowInputInterface接口。用于从script文件中读取数据库的行数据。



3.13 scriptio包


写/读一个脚本或日志文件,并执行其中的操作。
ScriptReaderBase:所有脚本读取器的抽象基类。提供了readDDL,readExistingData,readLoggedStatement供子类实现。正常启动时需要执行readDDL和readExistingData从数据库文件.script中读取数据库结构定义DDL语句与数据操作DML语句来重建库与表。
ScriptReaderText:继承自ScriptReaderBase,读取通过ScriptWriterText写的.script文件或.log文件中的脚本语句,并执行这些语句。本类与ScriptWriterText对应。
ScriptReaderDecode:继承自ScriptReaderText,在其基础上增加了解码功能。



3.14 server包


数据库Server层面的代码:启动服务、建立Socket并监听端口,并为每一个连接过来的socket创建一个新线程处理。



3.15 *包


HyperSQL所使用的类型定义。



3.16 resources包
提供了一系列的配置文件、脚本文件、sql文件等。并提供一个ResourceBundle的帮助类。


————————————————
版权声明:本文为CSDN博主「不动明王1984」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_37962779/article/details/79127513

相关文章