前言
在数据库系统中,大对象往往是指这样一种类型的数据:这些数据的体积较大,占用的字节数在KB级或MB级,有的甚至达到GB级。对于DBMS来说,这些数据不具备结构性,只是单纯的字节流,存储时需一次性存入,读取时需一次性取出。日常生活中常见的图片、视频、音频以及大数据量大文本文件,都属于大对象数据的范畴。现有的各种关系型数据库管理系统也都提供了丰富的数据类型和完备的存取方法来管理大对象数据。毫无疑问,作为大型通用的数据库管理系统,达梦数据库为大对象的存储类型提供了全面的支持,在大对象数据的存储组织方式上采用B树结合从表的方式达到了很好的存取效率。然而,与Oracle的大对象存取效率相比,达梦数据库还有差距。为此,达梦数据库开发部门在近期对达梦数据库的存取策略进行了改进和优化。测试结果表明,优化后的达梦数据库在大对象存取效率方面与Oracle相比,要略微胜出。本文详细讨论了达梦数据库大对象存取优化的方法和思路,其目的主要有两个:(1)为使用达梦数据库的广大开发人员详细解释系统中大对象的存取方法,使他们能够基于达梦数据库开发出高效的应用系统;(2)抛砖引玉,向广大开发人员介绍达梦数据库,希望能够引起更多人的关注,获得更多的改进建议。
1. 原有的大对象存取方法及其不足
1.1 大对象存储组织方式及其不足
不同于Oracle的簇存储方式,达梦数据库中,大对象数据的存储结构采用和普通数据一样的B树结构。对于每一个具有大对象字段的表(称之为主表),系统会为其设置一个从表,此表专门用来存储这个表的大对象数据。从表的命名规则是主表名加DMBLOB后缀。普通用户对从表亦有访问权限,但不建议对从表直接进行操作。从表的记录格式固定如下:
|
主表中此大对象数据所在记录的行编号 |
主表中此大对象数据所在元组的列编号 |
将大对象数据分片后的分片编号 |
大对象数据 |
|
ROWID |
COLID |
FRAGID |
DATA |
表1 达梦数据库中大对象从表结构
当往主表插入一条含大对象的记录时,系统将判断此记录的大小是否超过950字节,如果小于950字节,那么系统将这条记录直接插入到主表之中;反之,将这条记录中的大对象数据插入到从表之中。系统中同时规定,从表中每条记录的DATA字段的最大值为950个字节,如果一个大对象数据超过950B,那么需要将这个大对象划分成若干分片,每个数据分片表1中的其余三个字段组成一条从表记录插入到从表中。
原有系统的这种做法简单易行,在基本的存储模块上增加了分片模块和重新组装模块即可实现了对大对象的存取。然而,这种做法有两个缺陷。
1.从表记录中数据分片允许的最大值太小(为950个字节),导致一个体积较大的大对象在存储时,需要被分解成很多分片,在提取该大对象时,又需要重新组装众多分片。这些操作将耗费大量CPU时间。
2.数据分片的最大值太小还导致了空间利用率过低。达梦数据库中,从表中的ROWID,COLID,FRAGID字段长度分别为8,2,4字节,每条物理记录另有控制信息26字节。这样,从表中每个数据分片需要有40个字节的数据来进行标识。假设需要往某主表插入100万个大小为1K的大对象,由于1KB > 950B,每个大对象都需要拆成两条记录插入到从表中。简单计算可以得出,存储每个大对象需要的额外字节为80B,100万个大对象,共1GB大约需要80MB额外空间,将近有10%的空间被用来标识大对象的数据分片,由此可以看出空间浪费较为严重。
将数据分片的最大值设定为950字节是没有必要的。可以将这个值适当放大,减少将大对象分片重新组装分片消耗的时间,以及提高空间利用率。
1.2 大对象的写日志机制及其不足
达梦数据库中日志包括redo日志和undo日志。目前的系统中,在插入大对象数据时,redo日志的生成和处理方式和一般的数据插入是完全一样的,undo日志则进行了优化。下面首先对这两种机制进行简要的介绍。
1.2.1 redo日志的处理
达梦数据库中的redo日志记录了系统的对数据块的更新动作,我们可以按照redo日志,重做系统的每一步操作,保证系统的一致性。在系统正常工作时,每当往表中插入一条物理记录时,便会生成一条redo日志。在一个事务提交之前,系统将首先把系统缓冲区中的redo日志写入到磁盘中,然后再提交事务。通过这种方式,可以保证系统发生故障的时候,能够通过redo日志来恢复数据,保证系统数据的完整性和一致性。
对于每一条要插入的物理记录,在将其插入到数据页之前都会构造一条对应的redo日志记录,redo日志记录的结构是一个六元组:
(dbid, file_id, page_no, offset, len, new_data)
其中,前4个参数是为了标识日志记录对应的物理记录在文件中所处的数据块的地址和块内偏移,参数new_data为正式数据,保存物理记录的记录头+记录体信息。参数len表示new_data数据的长度。
系统在每次故障后重启时,使用上次运行时在日志文件中产生的redo日志记录,根据记录中dbid, file_id, page_no, offset四个字段的信息,找到记录对应的物理记录所在的数据块中的地址,并用new_data来覆盖这个物理记录。这样就可以保证系统故障前提交的事务对数据库的更新操作得到执行。

