Firebird嵌入式数据库服务使我们可以发布脱离数据库服务器的轻型程序,因此收到广大小型项目开发者的欢迎,但在嵌入式数据库服务器给大家带来便捷的同时,一些问题也会困扰我们,比如,只能单线程访问数据库文件,数据库效率低下等。
而最近的一个嵌入式数据库项目的问题,却让我寝食难安,经过反复验证,最终找到了原因。
起因:
最近的一个由Delphi语言和.NET语言共同开发的项目使用到了Firebird数据库,而为了方便起见,开发过程中使用的是Firebird的Server模式,没有出现任何问题。而前几天对各子系统进行集成的时候,我将数据库切换到了嵌入式版本,恶梦开始。
首先是ASP.NET下,当可执行程序放到含有中文的目录中时,会出现找不到数据库的问题,这个问题随后得到解决(见前文:修正Firebird Net Provider 1.7中文路径BUG(提供下载) ),而紧接着,更致命的问题出现了:程序关闭时,进程却无法退出,虽然界面已经退出,但在任务管理器中却还可以看到其占用内存,必须手动结束。而且无论是Delphi还是.NET工程,均有此问题。
一叶障目:
问题出现之后,首先想到的是嵌入式服务器的原因,因此,我使用Delphi建立了简单的测试项目,使用TIBDataBase来连接嵌入式数据库,但,经测试,一切正常,程序可以正常退出,因此排除了嵌入式服务器的原因。(而就是这一结果,造成了之后几天的努力都是与答案背道而驰)。
由于我在项目中使用到了自己写的一个简单数据层,因此,我又把目光集中在了这个层上,当然,首先想到的就是内存泄漏和连接泄漏。而由于嵌入式数据库服务器使用到了线程自管理,那么出现连接泄漏的可能性最大。
与之前测试项目不同的地方在于,数据层使用了动态创建的数据库连接组件,很容易造成这两种泄漏,而且在数据连接上使用了自定义连接池来处理不同字符串创建的连接。
经过仔细查验,我似乎找到了问题所在。为了提高效率和数据的一致性,我在连接池中保存的连接,默认情况下会保持连接状态(因为嵌入式数据库连接唯一的特殊性,也让我对此方法没有疑虑),直到程序退出的时候,连接会被关闭。
对于自定义的数据库组件连接池,我使用了Delphi 2006最新引进的class var关键字来定义静态成员,而这个成员连接了一个基于TstringList的对象来保存和管理数据库连接,该对象会在析构函数中关闭所有的连接并释放组件对象。
而问题似乎恰恰出现在这个析构函数的执行上。使用debug信息来跟踪代码执行,发现,该析构函数并未发出实现设置好的debug信息,由此可以初步断定,就是连接泄漏造成了Firebird嵌入式数据库服务器无法正常结束线程,造成始终占用内存的问题。
对此,我在每个数据层的对象中都显式的设置为,对象销毁即关闭连接,并在程序退出的时候,手动清理连接池,折衷的解决了该问题。问题解决的结论是:Delphi的静态数据管理在某种情况下存在连接泄漏问题(似乎原因很牵强),但故事仍然继续…
迷雾尚存:
Delphi下的问题解决了,但ASP.NET下同样问题的迷雾却始终没有消散。ASP.NET的模块,我使用了Cassini嵌入式Web服务器,使用一个winform程序来提供Web服务,由于项目使用了NHibernate数据层,且.NET环境无法直接手动释放一个对象,因此该问题一直没有有效解决。
我已经在代码中显式close了session,而且将session设置为null,甚至手动调用了GC来强行收回内存,但关闭的服务器仍然阴魂不散。
峰回路转:
为了进一步验证和探查Delphi对于静态数据类型的管理机制,我专门写了一个程序来验证。考虑到程序在结束并清理内存的时候,不一定能够传出debug信息(在编译器级别,如果内存的释放是由执行环境控制,则上层debug信息无法被执行),因此我使用了文件独占检测模式,即,在程序运行时独占一个文件,并在静态对象释放的时候(注意不是手动显式释放)释放该文件。当关闭程序之后,再验证该文件释放被正确释放。其关键代码如下:
unit WillFreeClass;
interface
uses Classes, SysUtils, ucTDataLayer, DB, ADODB, ib, IBDatabase;
type
TWillFreeClass = class(TObject)
private
Fconn1: TADOConnection;
Fconn2: TIBDatabase;
FTmpFile: Integer;
public
constructor Create;
destructor Destroy; override;
end;
TStaticClass = class(TObject)
private
class var
FWillFreeClass: TWillFreeClass;
public
class procedure executeClassMethod;
end;
var
gacWillFreeClass: TWillFreeClass;
implementation
uses ucTConsoleDebuger;
constructor TWillFreeClass.Create;
begin
FTmpFile := FileOpen('e:abc.txt', fmOpenWrite);
Fconn1 := TADOConnection.Create(nil);
Fconn1.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E: est.mdb;Persist Security Info=False';
Fconn1.LoginPrompt := false;
Fconn1.Open;
Fconn2 := TIBDatabase.Create(nil);
Fconn2.LoginPrompt := false;
Fconn2.Params.Text := 'user_name=SYSDBA'+#10#13+'password=masterkey'+#10#13+'lc_ctype=GB_2312';
Fconn2.DatabaseName := 'E:developcodeClassVarTestBIDDB.FDB';
Fconn2.Open;
end;
destructor TWillFreeClass.Destroy;
begin
FileClose(FTmpFile);
Fconn1.Close;
Fconn1.Free;
Fconn2.Close;
Fconn2.Free;
inherited;
end;
class procedure TStaticClass.executeClassMethod;
begin
if not Assigned(FWillFreeClass) then
begin
FWillFreeClass := TWillFreeClass.Create;
end;
end;
end.
经过验证,使用fileopen函数独占的文件,无论是否在静态对象析构函数中释放独占,程序关闭之后都会正确释放该文件。同样Access数据库,无论是否在静态对象析构函数中释放独占,程序关闭之后都会正确释放数据库文件,只是会留下*. ldb文件。而对于TIBDatabase控件连接firebird嵌入式数据库,则再次出现“阴魂不散”的问题。难道,是TIBDatabase存在连接泄漏吗?为了验证这一点,我打开了Firebird Server模式,并跟踪数据库连接数,测试发现,虽然没有手动释放TIBDatabase,它却在程序退出之后正确的还回了数据库连接。
那么很明显,是嵌入式数据库版本的问题,由于在解决“可执行程序放到含有中文的目录中时,会出现找不到数据库”的问题的时候,我看到新的Firbird数据库版本2.1.0发布了,因此就顺便更新了嵌入式数据库的版本,而随后的Delphi工程问题的顺利解决,使我进一步忽略了这个动作,当然恰恰就是那个看似正确的结论让我一周寝食难安。
我将2.1.0版本换回2.0.1版本,问题顺利解决。
结论:
1、 对于问题的排查和验证,需要在同一环境下一一测试,每次仅更换一种配置,以达到最高的可能性覆盖率。
2、 解决问题过程需记录下每一次操作,以备后续排查工作。
3、 不迷信任何程序。
分享到:
相关推荐
与其他数据库系统相比,Firebird嵌入式数据库的优势在于它不需要独立的服务器进程,而是直接集成在应用程序内部,提供了一个简单、高效且易于管理的解决方案。 1. **Firebird数据库特性**: - **事务支持**:...
一个基于FireBird嵌入式数据的共享文件管理器,可实现远程访问,含全部源码. 适用于Delphi XE及更高版本. 代码主要涉及技术: 1.FireBird连接的动态参数化设定及访问 2.磁盘文件存取及属性控制 3.DataSnap应用
Firebird的嵌入式版本允许数据库文件与应用程序打包在一起,无需独立的服务器进程,简化了部署流程。 对于开发人员来说,Firebird支持多种编程语言的接口,包括Java (JDBC),.NET (ADO.NET),PHP,Python等,使得在...
Firebird与C#的连接及嵌入式开发需要经过下载和安装Firebird数据库管理系统,创建数据库,编写C#程序连接Firebird数据库,并下载Firebird嵌入式版本文档和数据库文件。只有通过这些步骤,才能实现Firebird与C#的连接...
Linux平台下运用Lazarus Firebird开发数据库应用程序.pdf Linux操作系统作为目前最流行的开源操作系统之一,在数据库应用程序开发中扮演着重要角色。为了满足数据库应用程序的开发需求,需要选择合适的开发工具和...
近年来,飞速发展的计算机网络通信及无线网络,以及众多公司、组织和社会团体对于数据库应用的巨大需求,分布式数据库系统在集中式数据库系统成熟技术的基础上应运而生。分布式数据库即为数据库技术和网络技术两者优化...
在本文中,我们将深入探讨如何使用C#连接到Firebird嵌入式数据库。Firebird是一种开源的关系型数据库管理系统,提供跨平台支持,并且有多种语言的API,包括.NET框架下的.NET Provider。本实例将基于Visual Studio ...
在市场竞争方面,各大数据库厂商,如Oracle、IBM、Sybase、InterSystems、日立和Firebird等,都在积极开发和推广嵌入式数据库解决方案。Oracle通过收购TimesTen和Sleepycat,增强了其在嵌入式数据库市场的地位,提供...
此外,注意处理错误和异常,确保在程序中包含适当的错误处理机制,如`try...except`块,以便在出现连接问题或数据库操作失败时提供反馈。 总结,通过Delphi 7与ODBC驱动的结合,我们可以轻松地连接和操作Firebird...
在PHP中连接Firebird 2.1数据库是一个常见的任务,特别是在需要将PHP应用程序与Firebird数据库集成时。Firebird是一款开源、多用户的关系型数据库管理系统,具有高度的可移植性和性能。以下是一些关于如何使用PHP...
然而,传统的基于文件系统的数据管理方式已无法满足日益复杂的应用需求,因此嵌入式数据库成为了解决方案。 嵌入式数据库的主要特点是其对实时性和可靠性的要求。它们必须能在无人干预的情况下持续运行,保证数据...
总之,通过使用Firebird .NET Data Provider和C#的ADO.NET功能,开发者可以轻松地访问和操作Firebird 2.5.8嵌入式数据库。无论是简单的CRUD操作还是复杂的业务逻辑,C#都能提供强大而灵活的支持。记得在实际应用中,...
1. **消费电子产品**:如智能手机、平板电脑等,其中嵌入式数据库可以管理和存储用户的联系人信息、应用程序数据等。 2. **移动计算设备**:比如手持终端、移动POS机等,这些设备依赖于嵌入式数据库来存储交易记录...
嵌入式数据库是一种专门设计用于嵌入到各种设备或应用程序中的小型数据库管理系统,它们在现代技术设备中扮演着至关重要的角色。随着手机和其他智能设备功能的多样化,嵌入式数据库成为了管理和共享数据的有效手段。...
《嵌入式Firebird:全功能的2 MB运行时数据库》 Firebird数据库系统,作为一个开源的关系型数据库管理系统,以其高效、稳定和易用性在IT业界获得了广泛的认可。尤其在嵌入式环境中的应用,它展示出强大的适应性和...
ASP.NET中使用嵌入式数据库Firebird2.5.8版64位,怎么使用,项目里面有个测试的数据库,demo可以直接运行,IISExpress里面测试会报错,需要部署到本地IIS里面,应用程序池高级设置不要把啟用32位應用程序設置為True...
Java连接Firebird数据库的过程中,驱动包扮演着至关重要的角色,它使得Java应用程序能够与Firebird数据库进行通信。本文将详细讲解如何使用Jaybird驱动包来实现这一目标。 Jaybird是Firebird官方推荐的Java JDBC...