2011-09-08 67 views
1

我刚刚发现我的程序员实际上是将整个SQLite数据库加载到列表中(select * from tablename)(用于搜索和筛选操作),而不是单独执行sql语句。只有少于20,000到50,000条记录时,这种方法运行良好。但是一旦内存不足,或者超过一定数量的记录,Delph 7应用程序就会冻结。如何使用Delphi处理有限内存的非常大的列表

因为我们深入到项目中并且已经部署,所以改变这种方法为时已晚。我没有时间重做解决方案,因此我需要找到一个“快速修复”来减少内存使用量并增加数据库中的记录数。我想知道是否有可能将列表分割成文件然后逐个处理?

更新添加有机磷农药回答质疑

感谢回答。以下是关于如何将sqlite记录加载到整个应用程序中使用的记录中的代码片段。可以有多达20万条记录,这会消耗大量的内存。有没有办法缓冲文件中的记录?

type 
    TMyDatabase = class(TThread) 
    private 
    Owner: TComponent; 
    FSqldb: TSQLiteDatabase; 
    FSltb: TSQLIteTable; 
..... 

type 
    PMyMessageRec = ^TMyMessageRec; 
    TMyMessageRec = record 
    Id: integer; 
    RcptId: integer; 
    PhoneNumber: ShortString; 
    Text: string; 
    ...... 
    end; 

procedure TMyDatabase.Execute; 
........ 
begin 
........... 
     FSltb := FSqldb.GetTable('SELECT * FROM Messages ORDER BY ID LIMIT ' + IntToStr(MaximumMessages)); 
     try 
      Synchronize(SyncLoadAllMessages); 
      Synchronize(SyncLoadMessages); 
     finally 
      FSltb.Free; 
     end; 


procedure TMyDatabase.SyncLoadAllMessages; 
var MessRec: PMyMessageRec; 
....... 
begin 
.... 
while not FSltb.EOF do 
      Begin 
       if TerminateAll then exit; 
       New(MessRec); 
       MessRec.Id := FSltb.FieldAsInteger(FSltb.FieldIndex['ID']); 
       MessRec.RcptId := FSltb.FieldAsInteger(FSltb.FieldIndex['RecipientId']); 
       MessRec.PhoneNumber := FSltb.FieldAsString(FSltb.FieldIndex['RecipientPhone']); 
       MessRec.Text := FSltb.FieldAsString(FSltb.FieldIndex['Text']); 
       MessRec.Charset := FSltb.FieldAsString(FSltb.FieldIndex['Charset']); 
+0

你参考的数据集列表?或者您也可以成功打开数据集,但还有从数据集填充的其他列表(?)? –

+0

你可以发表一些填充列表的实际代码行,以及变量的声明,以便我们可以看到涉及的事物类型? – hatchet

+5

没有这样的事情,“做错了,因为没有时间做正确的事情”。做错了,从长远来看需要更多的时间。 –

回答

3

“内存数据库”本身并不是一个糟糕的设计(市场上有很多产品)。对象关系映射器和对象高速缓存使用此策略来处理较小或较大的数据库部分以提高性能。

试图分裂问题两个部分有一个短期和长期的解决方案:

  1. 短期

    • 增加可用内存(见其他答案)
    • 应用减少内存使用的设计模式,例如flyweight pattern
    • 使用对象缓存算法(延迟加载/删除对象)
    • 找到并消除内存泄漏
    • 使用FastMM使用跟踪器应用程序来搜索其使用大量的内存
  2. 长期

    • 介绍服务层API的应用对象,其隐藏底层对象列表的实现细节,并且最初简单地使用现有的对象列表,然后用SQL查询逐步替换服务层的实现,或者调用ORM
    • 与服务层设计,应用程序也可以分为GUI客户端和服务器(服务)端应用程序后
+0

+1上的延迟加载。这不应该很难实现,并且*如果*在会话期间每次不使用所有列表可能会造成一个不同的世界。 –

2

这可能有帮助,但它是非常多的应用贴膏药的头颅切断!

  1. 切换到64位机器。
  2. 制作您的程序/LARGEADDRESSAWARE
  3. 切换到FastMM(由于旧的Borland MM中的错误,需要使第2步工作)。

然后交叉手指,希望4GB的地址空间,而不是2GB就足够了!

+0

+1为贴标石膏切断头 –

1

即使您提出的解决方案是“改变这种方法”。您将以某种方式结束重构,最好使用SQL引擎尽可能多地进行过滤。

你没有描述实际使用的算法,但我会假设你现在这样做:

  1. 加载整个数据集成在启动某种形式的名单。

  2. 让用户指定过滤条件。

  3. 调用一个传递过滤条件并在完整列表上运行的函数,返回经过过滤和排序的另一个列表。

如果是这样,你应该能够在步骤3重构在步骤1中只需要创建一个空的列表(这样你就不会到这个列表的引用抛出错误)。然后,在步骤3中,使用SQL来完成所有过滤,或者如果过多重构,请弄清楚如何从SQLite获取部分过滤的列表到内存列表中的中间,然后将现有的排序和过滤应用到该列表。

这应该限制你的重构为一个函数(假设我正确地猜测了程序的整体结构)。

0

正如其他人指出的那样,很难给出建议,没有更多的细节。这是说我的两美分。

我的建议是使用探查器来确定“热点”在哪里,并专注于那些。你可以从SmartBear获得一个免费的。

你没有提到你正在使用什么样的列表。如果该列表在设计中根深蒂固,那么可以编写一个从调用者的角度来看就像列表一样工作的包装器,但在内部它依赖于参数化的选择语句。

2

我会根据一些标准(如日期范围)为初始查询添加一个“WHERE”子句,以便您或您的应用可以对初始结果集的大小进行一些控制。我使用DevExpress QuantumGrids,它将整个查询结果加载到内存中,以获得极大的灵活性和速度。 (DevExpress的东西真是太棒了......)我在应用中放置了几个日期控件,我的用户可以与之交互,并为结果集设置StartDate和EndDate范围。这可以在一定程度上控制性能。