数据库管理系统数据系统层(数据库)
时间:2022-11-17 12:30:01 | 来源:信息时代
时间:2022-11-17 12:30:01 来源:信息时代
数据库管理系统数据系统层 : 数据库系统的用户接口层的实现基础。数据系统层向上对用户接口层提供的逻辑数据结构相对于关系数据模型来说,是规范化的关系、视图及用于支持与宿主语言接口的元组,数据系统层向上提供的这一接口称为多元组接口(相对于下一层存取系统层所提供的单元组接口而言)。对这一逻辑数据结构(即关系)所提供的运算有SEQUEL语言、SQL语言、Alpha语言(关系演算)及关系代数等。
这种关系数据库系统的多元组接口向上对用户接口层提供的用户接口语言(典型的如SQL语言)是一种非过程型的描述型数据库语言,对数据的定义、操纵和检查均有统一的语言形式,加上选择适当的逻辑数据结构,它具有高度的数据独立性。这就是说,在所定义的逻辑数据结构上,使用描述型的语言书写的应用程序,对数据库系统的数据结构、存取路径和存储结构的变动具有高度的稳定性和适应性。它与较为低级的数据库系统相比有两个优点:
(1)易于更动数据库结构。在正常操作条件下,可补入新关系,可对现有关系扩充新属性,可在不同属性上动态设立或撤消存取路径。
(2)查询由非过程型的描述型语言来表达。为满足查询而选择最优存取路径的问题由系统自动完成。若所选出的存取路径后来发生了变化甚至被删除,系统会自动选择其他的存取路径,用户程序无需丝毫改变,对用户毫无影响,只可能在执行效率上发生一些变动。
数据库管理系统的数据系统层的一个重要任务就是将由数据库系统用户接口所提交的用非过程型的描述型语言书写的应用程序转换成(编译成)一串由存取系统所提供的原语(中间语言)去执行。在此编译期间,数据系统还必须承担存取检查、完整性检查、存取优化和专用视图支持等任务,以确保正确和高效地执行程序。数据系统的体系结构及其在数据库管理系统总体结构中的地位如图1所示。
图1 数据系统的体系结构
数据系统层的各个系统模块对系统目录和数据库的访问是通过其下层即存取系统层进行的。在编译时数据系统层的各个系统成分激活,在执行时调用存取模块而将控制交给存取模块。
由上所述,数据系统层的首要任务就是将非过程型的描述型的数据库语言书写的程序转换成(翻译成)可执行的单元组基本存取动作序列。存取动作和编译时的目录和存取路径是密切相关的,当存取路径改变了,就应重新优选存取路径,重新编译。
数据库语言(例如SQL语言)程序的编译其步骤如下:
(1) 词法和语法分析。
(2)造表: 将外部关系名和属性名转换为内部名。前者即外部名便于用户记忆和使用,后者整齐划一,由系统内部使用。在造表中需存取系统目录。
(3)存取检查:查阅安全矩阵,审核存取权。如果安全条件与数据的值有关,则在编译时尚无法确定该语句是否允许执行,需生成相应的动作,以便执行时进行检查。
(4)完整性检查:查阅系统目录,按照完整性规则,对数据类型、格式和转换作简单的完整性检查。生成相应的运行时动作和触发子,以便在进行正式的存取时作完整性检查。
(5)存取优化: 包括代数优化和非代数优化。
(6)形成存取模块:按以上五个步骤建立了一系列内部控制表格,形成了和数据库的各种联系(即绑定),如外部关系名和属性名与内部名的联系,与所选定的存取路径的联系等。向存取系统的运算符提供参数、有关完整性检查和存取权审核的运行时动作及所需要的格式转换等亦应包含其中。
上述六个步骤将SQL语言的语句即符号串A转换为一串可执行的存取动作,这个加工过程是一个逐步绑定的过程,即同确定的数据结构、存取路径和存储结构绑定在一起,构成一串确定的存取动作。这一串存取动作包装在存取模块中,在执行时被调用。容易看出,编译时的绑定是密切依赖于数据库的状态的,该状态会由于存取路径的变化,完整性约束的变更,或收回某种存取权而发生改变,从而所形成的存取模块不再有效,需重新进行编译。当然,重新进行编译的时机需另行考虑。
按照绑定时间的早晚,翻译和执行的方法不同,从而导致效率各异,灵活性也大不相同。绑定的时间愈早则效率愈高,但灵活性和适应性也愈低。绑定时间愈晚则应变能力愈强,但效率也愈低。
一种最简单和效率最高的方法是在应用程序设计时进行绑定,它对系统的要求最低但对程序员的要求最高。ADABAS系统和SESAM系统就是采用这种方法。其要点是在应用程序和数据库系统之间开辟一个协议区,在一个十分低级的接口上进行数据库系统的调用。
另一种极端是完全的解释执行,其绑定时间最晚,灵活性最高但效率最低。直到执行之前符号串A均以原始形式保存在源程序中,当真正调用数据库系统时才用解释程序去完成前述的六个步骤并立即执行。它能适应加工过程中数据库状态的变化,保持高度的数据独立性。但在某些情况下如在应用程序的循环体中出现对数据库系统的调用,则每循环一次都要从头执行全部的六个步骤,其开销很大,效率很低。
编译方法介于上述两个极端之间,预编译好的存取模块可以多次调用执行,只有在数据库状态发生变更时才需要重新进行编译。故通常均采用编译方法,它的效率在典型情况下可比全解释方法高10~20倍。
应用程序一般采用过程型编程语言书写,如FORTRAN、PL/1、BASIC、C++等,涉及数据存取时则通过嵌在应用程序中的SQL语句来完成。FORTRAN等称为书写应用程序的宿主语言。对这样的应用程序,通常采用预编译的方法进行加工,即将嵌有SQL语句的宿主语言(如PL/1)源程序进行变换,将所嵌入的SQL语句替换为相应的专用存取模块的调用,余下的PL/1程序则由PL/1编译程序进行编译并生成PL/1目标程序。执行PL/1目标程序时将相应调用由所嵌入的SQL语句编译成的存取模块。
如图1所示,数据系统层向上提供的是多元组接口,向下则基于存取系统层所提供的单元组接口。多元组接口提供的关系和视图等是元组的集合,可以直接输出,提供给用户。但在有些情况下则不合适,例如,对于宿主语言PL/1书写的应用程序,它与嵌入其中的SQL语言语句的交互不能直接进行。SQL语言查询结果是元组的集合,而传统的PL/1语言是过程型语言不是集合论语言,它只能处理一个一个的元组而不是元组的集合。为此,需在两者之间架设一座桥梁,游标(cursor)法就是这样一座桥梁。
游标可定义在SQL语句所说明的元组集合上,它给予该集合一个名字,并指示该元组集合中某个元组的位置。连续调用游标就可为应用程序一个一个地提供所需的元组。
向数据系统层提供支持的存取系统层所提供的单元组接口是在另一个较低的层次上提供一个一个的元组,它依赖于具体的存取路径,而游标则在较高的层次上,与具体的存取路径无关。
数据库管理系统的存取系统层提供单元组接口的机制叫做扫描(scan)。扫描分为两种,一种叫索引扫描,另一种叫关系扫描。索引扫描建立在关系的某个索引上,按索引键的值以升序或降序提供一个一个的元组。如果一个关系有若干个索引,则允许建立若干个索引扫描。若一个关系不存在任何索引,则只能建立关系扫描,按关系的存储位置一个一个地提供元组。显然,有索引时加工更容易,效率要高得多,特别是当用户的查询属性与索引属性相一致时。
数据系统层编译用户的查询语句所生成的存取模块,在执行时就是对这些由存取系统层的单元组接口提供的一个一个元组进行加工的。