①由于在数据文件中,每条记录的长度不定,数据即使局部更新,也必须重写整个文件;
②由于同样原因,单条记录的检索难于直接定位,而必须读入整个数据文件。
为弥补由此产生的性能下降,本文采用数据Cache加以克服。也就是说,尽可能将数据缓存在内存中,通过减少对物理文件的读写操作来提高数据的性能。
2.4 多线程数据存取的同步与互斥
在Java虚拟机环境下,没有多进程的概念,但对于多任务的处理提供了多线程的手段。本DB引擎组件是公共组件,供上层多个应用程序组件共同使用。由于上层的每个应用程序组件本身由一个或数个线程来执行,因此,DB引擎组件必须考虑多个线程同时存取某个数据时可能引起的冲突问题。对于该问题的解决办法,一般是采用DB锁定的方法。关于DB锁定,进一步细分的话,可区分为读锁和写锁;根据锁定粒度的粗细可分为按表锁、按Page锁、按记录锁等,不一而足。
为设计和实现的简单起见,本DB引擎组件提供按表锁定的方式,同时不区分读写锁之间的区别。这样,大大简化了SQL语句的分析和处理过程,并且可以直接把锁定操作与表的open操作相关联,锁解除与表的close操作相关联。实现时,对应表对象Table的每一个实例,设置一个field变量,用于保存锁定状态,再利用Java语言提供的synchronized手段同,可以较为方便地实现数据表的锁定功能。代表示例如下:
//表锁定。为了避免死锁,有超时判断逻辑
synchronized void LOCk()throws DBError{
long t2,t1;
t1=System.currentTimEMIllis();
//由于可能出现在wait语句被唤醒而却得不到表锁的情况,为提高超时逻辑判断精度,采用循环
while(isLocked){
try{
wait(DBError.TIMEOUT/10);
}catch(Exception e){e.printStackTrace();}
//超时判断
t2=System.currentTimeMillis();
if(t2-t1>DBError.TIMEOUT)break;
}
//发生超时退出循环情况,抛出例外
if(isLocked){
throw new DBError(DBError.TIMEOUT_ERR,name);
}
//设定锁定标志
isLocked=true;
}
//表打开操作
//参数ro只读打开标志
void open(Boolean ro)throws DBError{
lock();
readOnly=ro;
//表数据读入
load();
}
//表关闭操作(同时释放锁)
public synchronized void close() throws DBError{
if(isLocked==false)return;
//关闭前,保存数据
if(iSDIrty)save();
if(isUnload)unload();
//释放锁,通知其它等待线程
isLocked=false;
notify();
}
图3 DB引擎组件主要类的关系
2.5 DB组件实现的结构设计
图3所示为DB引擎组件的主要类之间的关系。其中,Database为数据库类,用于描述和管理整个数据库对象Table为数据表类,用于描述和管理表对象;TabLEData用于描述和管理保存表数据的物理介质(文件);Field为字段类,用于描述和管理字段类型信息;Record为记录类,描述一条数据记录。为简化处理,本组件将Database类设计为singleton模式,即本组件只能创建一个Database实例。这对于嵌入式系统来说,大部分场合已经足够。与数据库的一般物理概念相对应,1个Database实例包含n个Table实例,1个Table实例包含n个Field实例。同时,1个Table实例包含1个TablEDAta实例,1个TableData实例包含n个Record实例。
Connection类用于管理用户访问数据库的会话(Session)过程。对应一个用户的一次会话过程,生成一个Connection实例。Connection类对象保存着当前Session打开的Table列表,当用户提交执行某SQL语句而需要锁定某个Table时,系统首先检查该表是否已经在当前Session已打开的Table列表中。如果已经被打开,则不需要进行重复的锁定操作,直接反回对应的Table对象实例。反之,如果尚未包含在打开的Table列表中,表明当前Session尚未打开和锁定该表,必须执行该表的打开和锁定操作(如果该表已被其它Session打开,则必须等待到其它Session翻放该表为止)。
本DB组件还支持commit与rollback事务处理。能够在如此微小的DB组件实现事务处理,主要得益于上述的Session管理框架。在Table类commit与rollback处理基础上,当一个Session执行commit或rollback操作时,对包含在打开列表中的每个Table实例,调用执行相应的commit或rollback处理即可。
3 结语与展望
本DB组件已实际运行了大约两年时间。这期间除了对该组件进行一些功能追加以外,主体框架上基本保持不变,从而在一定程序上表明了该设计框架的可行性和合理性。该组件编译以后,class文件形成的jar包大小约为68KB,短小精度悍,便于使用。当然,该DB组件目前仍然存在一些不足:首先,较为关键的一点是速度问题。一直以来,Java的执行速度问题就是受批语的缺点所在,因此采用它实现自然也避免不了这人瓶颈。今后改进的思路之一是,将其中Java处理效率不高的部分移出Java,采用C实现;二者通过JNI手段加以连接,以提高总体的运行速度。第二点需要改进的地方是表锁定的粒度问题。由于目前只能整个表进行锁定,并且不区分读锁定与写锁定,因此粒度较粗。虽然这样实现起来较为简单,但在多任务处理环境中可能增加不必要的时间等待。最后,JDBC接口的实现目前还不完全,需要加以完善。