今天我将分享关于数据库内核层面和驱动层面的兼容能力,以及相关的修改和案例。
中国数据库的发展史
首先,让我们回顾一下中国数据库的发展现状和历史。在1978年至1990年期间,中国并没有正式的数据库产品,仅有理论研究。国防和军工核心领域可能有一些简单的应用。1989年至1998年,Oracle逐渐崛起,在金融和电信行业占据较高市场份额。1999年以后,国内第一批数据库厂商开始涌现,MySQL在互联网应用中也逐渐普及。2013年至今,各种数据库产品和厂商纷纷涌现,国内外云厂商也推出了自家的数据库产品。目前,中国的阿里云、腾讯云和华为云都加入了国产数据库能力建设。整体来看,国内外数据库能力的差距正在缩小。
数据库是非常重要的技术组件,支撑着各种网站和应用的后台。我们认为数据库是基础软件皇冠上的明珠,也是技术的大动脉。因此,自主可控的概念逐渐被提出。数据库在计算产业和大数据发展中扮演着重要角色,一些优秀的架构如计算存储分离、ShareNothing架构对各行业的分析业务都起着关键作用。在这种背景下,我们需要考虑如何发展国产数据库,使之更为优秀和自主可控。TDSQL是国产数据库去O的一个方向,这是国产数据库发展的一条道路。去O是一个系统性的工作,涉及诸多方面的内容,如语法功能兼容、引擎层面兼容以及生态工具相关兼容。
语法功能是大家最熟悉的部分,我们需要对Oracle的语法进行兼容。功能性方面,包括结构性缓存和Oracle数据库的工具兼容。此外,我们还需要模拟和兼容Oracle的行为,以便在用户编写逻辑判断和异常处理时进行适配。工具方面,主要涉及接口兼容性,特别是兼容性评估工具。在数据库迁移过程中,这些工具可以评估数据库对象并动态展示迁移过程,为用户提供迁移预期,辅助用户进行迁移。尽管这些看似标准化的后台工作,但在实际实现过程中,我们仍需对兼容性进行深入考虑。在后续的分享中,我将进一步介绍这方面的内容。
TDSQL兼容Oracle
改造方法
经过深入思考,我们决定从以下几个方面对能力进行改造。首先,我们将从内核为基础的语法和功能兼容性入手,包括数据类型、内置函数、PLSQL等基础SQL语法,以及分析表、数据库查询等功能层面的兼容。这样一来,用户便能直观地编写类似Oracle的语法,并在TDSQL中执行。
除了内核层面的兼容,我们还将实现GDBC和ODBC的兼容。GDBC兼容一般是配合内核能力进行的,例如,在内核中增加一个数据类型时,需要相应地修改GDBC中与之相关的对象,或者新增一个Java对象,以及其他相关修改,如新增语法等。此外,我们还需要处理ProC相关的兼容问题。
为了实现生态工具的迁移,我们将提供迁移评估报告、自动化改造和数据同步等功能。此外,我们还将确保操作系统和相关硬件的适配。
内核层面
接下来,我们来看看内核层面的Oracle兼容能力,这也是今天分享的主要内容。
在数据库中,数据类型是一种基本对象。我们在内核中增加了一些Oracle数据类型,以实现对多种数据类型的兼容。例如,数值类型(如Numeric、Slot、Dialogue等)和字符类型(如NYX2)在TDSQL内核中都有对应的实现,可以实现兼容。此外,我们还对BLOB和CLOB相关的兼容、日期类型(如TimeStamp、Date等)以及其他类型(如RAW、Long Raw、BFile等)进行了处理。需要注意的是,由于存储架构的不同,我们在这里无法实现完整的兼容,后续将详细讨论这个问题。
语法层面的兼容距离数据库用户更近,因为大家都是通过SQL与数据库进行交互。我们已经兼容了一些语法,如Merge Into、Connect By、Force View、Pivot和UnPivot等。此外,我们还增加了分区表相关的维护功能,如分裂分区和合并分区、Default分区维护等。我们还支持了各种伪列(如RowNum等)、相关对象(如DBLink、全局索引、系统函数、系统视图等)以及空串和NULL的兼容。
PLSQL语法在Oracle数据库中非常常见,许多用户和业务都喜欢用PLSQL编写业务代码。因此,我们在这方面的工作会相对更多。我们实现了存储过程和函数创建的兼容,支持COMMIT、RollBack事务控制,以及游标属性的兼容和修改。此外,我们还支持OUT出参、集合类型和包等PLSQL对象。其他PL语句的支持包括Bulk Collect等。我们还支持预定义的系统包,如DBMS Lob、DBMS Debug等,以帮助开发人员调试自己编写的程序代码。这些便是我们在内核层面实现的Oracle兼容能力。由于涉及的内容较多,这里仅列举了部分内容。
在处理语句时,会涉及一些细节问题。例如,在层次查询中,如果涉及多个表,我们会在查询之前进行处理,并详细调查Oracle的兼容支持。此外,我们已兼容了伪列,如Level、Connect By Isleaf、Connect By Root和Connect By Path等相关子句。Merge Into是大家较为熟悉的功能,即将一个表的数据与另一个表合并。我们借助Left Join功能对Merge Into进行了改造和实现,其中还支持分布式执行。在执行生成计划后,我们直接判断下层返回原图是否匹配,最终的Merge条件会变成Left Join条件。我们通过这种方式处理细节问题。
接下来,我们来看RowID和RowNum。RowID指定了原组物理位置,包含文件ID。然而,在TDSQL架构下,由于是多版本的,我们无法混合这个位置。因此,我们通过模拟RowID的行为,实现了部分兼容。RowNum则是返回记录编号,与RowID有所不同。分区表功能在业务应用中也较为常见。我们支持Range、List、Hash等类型的分区表,并实现了解质功能。在实现时,我们使用了一些相表,可以单独访问。此外,我们还支持分区键的更新、新增分区后的自动维护Default分区等功能。分区表的合并和分裂功能也较易理解。当分区数量较多时,我们可以将多个分区合并成一个分区,并将合并后的分区放在额外的TableSpace中,以便于管理。分裂分区功能则可以将较大的分区拆分成两个分区,并通过一些条件进行分裂,从而为用户提供更便利的分区管理方式。
分区合并分裂的语法示例
以下是一个分区合并分裂的语法示例。合并功能可以将多个分区合并为一个,而分裂功能则将一个分区分成两个。当然,我们也支持子分区的合并和分裂。接下来,我们来看PLSQL的兼容实现。Oracle存储函数的语法与原生语法有所不同,因此我们需要在PLSQL中进行兼容处理。在分析输入字符串时,如果发现是Oracle存储过程的语法特点,我们会等待"/"作为定义结束,然后发送到服务器。服务器端也会进行类似的处理,以适应Oracle语法。在这之后,实际上没有太大区别,主要是在前面进行了许多语法判断,使得字符串函数功能兼容。
With Function功能很有趣,它允许在查询前面直接使用函数,而无需先创建。这样一来,在系统表中不会留下任何痕迹。我们通过在内核执行编译时为每个查询创建一个Query结构体来实现这一功能。当解析Query各个语法部分时,我们会先查看With Function List,如果存在,则直接使用。With Function List将With Function函数部分放在PL编译后的内存结构中,并存储在Query结构体中,后续处理流程基本上可以重复使用。Package功能也广泛使用,我们对其进行了兼容处理,基本上支持所有特性。我们支持公共和私有包的变量定义、包内自定义类型以及包的数据化等功能。实际实现时,我们利用原生的PLSQL基础架构完成。
自治事务的实现示例
以下是一个自治事务的实现示例。在Oracle中,自治事务由主事务启动,主事务挂起后,自治事务开始执行并在完成后恢复主事务。两者之间是相互独立的。在TDSQL实现中,我们遵循了这种行为。自治事务可以由主事务自由启动,也可以启动新的自治事务。一旦上一个自治事务或相应的存储过程结束,下一个自治事务就会恢复运行。这里的关键是,主事务需要挂起,它们之间必须相互隔离。自治事务可以在函数、匿名块和触发器中运行。
在驱动层面,我们需要进行一些类型的兼容,包括函数和存储过程语法层面的兼容。我们遵循JDBC标准,并支持TDSQL数据库,Java6和Java8平台都能编译。OCI支持初始化环境,用户连接、断开、多会话、错误消息处理等。ProC支持变量声明、建立连接、预编译语句等。
最后,我将分享几个案例。反洗钱系统有很多混合场景,需要Oracle兼容。我们实现了整个平滑迁移,由于是分布式系统,性能得到了显著提升。保险系统的生态环境更新表结构效率提升了50%,Oracle兼容能力的高精度有助于业务短期迁移上线,并实现了同城容灾双中心,提高了系统可靠性。某省公安系统的处理速度达到每秒3.9万条,物理节点超过300台。




