|
|
导航: |
论坛 -> DELPHI技术
斑竹:liumazi,sephil |
|
作者: |
|
2016/11/30 20:34:54 |
标题: |
解决TClientDataSet导入大数据内存不足的BUG |
浏览:5811 |
|
加入我的收藏 |
楼主: |
一历史程序,采用UniDac直连Oracle数据库,大量使用TClientDataSet作为数据中转组件。
使用三层开发,只是利用了midas高效打包二进制数据的方式, 将TDataSetProvider组件的Data属性值,传到客户端:
CDS := DataSetProvider.Data;
客户端再用TClientDataSet导入,
ClientDataSet.Data := CDS;
然后处理数据(比如将ClientDataSet中数据再次导入客户端数据库,导入完毕释放掉ClientDataSet,再用客户端数据库进行其他处理)
数据量不太大时ClientDataSet.Data := CDS;速度非常快,效率高(比转成XML、JSON效率高)。
当CDS很大的时候(大于53M时,有时业务需要获取很大数据到客户端再处理,别说分页存取之类的话), ClientDataSet.Data := CDS; 会出错:Out of memory:insufficient memory for this operation
采用ClientDataSet.LoadFromFile也一样出错。 XE3和XE7下都会出错。其他版本没测试。
找了好久,没找到其他数据集组件能够像ClientDataSet那样载入CDS。
另外,TClientDataSet很耗内存, ClientDataSet.Data := CDS;后内存耗用急剧增加,52M大小的CDS,导入TClientDataSet内存耗用增加650M左右。
上面用途中,TClientDataSet通常作为数据中转组件,不增删改数据。 有哪位高手知道,有啥现成的组件能载入大数据量CDS又不出错? 占用内存少、高效更好。
或者有其他处理办法?
----------------------------------------------
- |
作者: |
sspeak (sspeak) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/1 1:17:36 |
1楼: |
试试 TFDDataSet.SaveToStream/LoadFromStream
----------------------------------------------
-
|
作者: |
sspeak (sspeak) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/1 2:34:02 |
2楼: |
另外,可参考: http://www.cnblogs.com/hnxxcxg/p/5663372.html http://blog.csdn.net/xieyunc/article/details/48350531 TFDMemTable比ClientDataset好用多了,没有这些古怪的问题。
----------------------------------------------
-
|
作者: |
|
2016/12/1 8:06:41 |
3楼: |
很显然,什么都有极限。解决办法自然是采用分页。一般用户不会一次性看完所有的数据,显示器屏幕不可能无限大。
----------------------------------------------
Everyone will to do best!
|
作者: |
|
2016/12/1 8:29:12 |
4楼: |
分段给被
----------------------------------------------
学DELPHI http://www.studydelphi.com
|
作者: |
|
2016/12/1 9:09:40 |
5楼: |
53M,是百万级别的数据了吧。 整个我觉得和clientdataset没什么关系,delphi要解包然后再载入cds,肯定慢,而且显示这么多数据,也没什么意义吧 像你这种业务情况,一般是服务端用adodataset将数据savetofile并压缩,可以缩小到5M,然后客户端再用adodataset解压读入
用adodataset效率是很高的
----------------------------------------------
-
|
作者: |
|
2016/12/1 9:23:35 |
6楼: |
clientdataset的日志关了 再看下会报错不 还有内存增加的问题 Cds.LogChanges:=False;
----------------------------------------------
-
|
作者: |
|
2016/12/1 9:31:02 |
6楼: |
clientdataset 其实是一份 text file. 慢是必然的。原概念应不会想像这么大。如果你真要三层式,那有两个方法提议。
一是避开 clientdataset 而载入 array / stringlist 之类.
二是把这一层的运作搬上 ramdisk.
当然你必需有相当记忆体, 16GB 配 64bits Windows, 分配 8G 至 10G 給ramdisk, 把 Windows 的暂存快取也搬上 ramdisk . 这就是国外专业的设计.
----------------------------------------------
-
|
作者: |
|
2016/12/1 11:27:48 |
7楼: |
谢谢各位以上的建议。 说明一下,其他地方都没问题,是在将大数据包读入TClientDataset时提示内存不足。 即这一句:ClientDataSet.Data := CDS 或者这句:ClientDataSet.LoadFromFile(filename)
赋值速度很快。6楼误会意思了。是内存不足。
CDS包53M(未压缩时,即使压缩了,赋给ClientDataSet也需要解压缩)大约有15万条记录,并不多,下载这么多是为了给各个客户端自行再次处理用的(筛选、汇总、过滤、合并),可能每个客户端需要处理的字段数、记录数、数据范围都不一样,因此有此需要。
下载那么大数据,不光是为了看的,还为了再处理数据。客户端程序有数据再处理功能。一次下载,多次使用。
客户端电脑较老,配置内存2G居多,4G也有,均为32位CPU,需要适应这种情况。
2楼的意见很有用,看来可替代TClientDataSet。
----------------------------------------------
-
|
作者: |
|
2016/12/1 14:05:34 |
8楼: |
我的意思是说ClientDataSet增加记录时,changelog占用了很大的内存,而且默认是开起来的,给他赋值data之前,先关闭的话,应该不会再有内存不足的问题。
随机生成了3个字段的100万行数据,关闭不关闭LogChanges,差很多的。 没有你具体的data数据,所以你试试。
----------------------------------------------
-
|
作者: |
vmao (毛小毛) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/1 16:44:55 |
9楼: |
刚才用Delphi XE测试了一下,确实如楼主所说,对于大数据读取,ClientDataSet很耗内存(实际存成二进制文件并不大50M)。直接赋值Data,changelog参数没有影响的,除非是一行一样新增。
这样就纠结了,三层应用ClientDataSet是核心组件了,不用ClientDataSet用啥呢?
----------------------------------------------
-
|
作者: |
|
2016/12/1 17:30:00 |
10楼: |
二楼已经说了,TFDMemTable比ClientDataset好用多了,没有这些古怪的问题。
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
|
作者: |
|
2016/12/1 18:34:47 |
11楼: |
我测试用TFDMemTable加载40万条数据共74M,占用内存大约280M
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
|
作者: |
|
2016/12/1 22:46:34 |
12楼: |
kylix2008, 我是指出 ClientDataset 本身其实是文字档, 那是除了读写很需时, 资料转换成文字档也使耗内存数以倍数增长. 你不避开 ClientDataset, 就该把内存倍增, 连系统都搬上内存当然更好. 当然些方法皆采纳才是正路. Memtable 也是避开 ClientDataset 的内存方法, 但直接用 array 等方式比 Memtable 更省内存. 总言之就是那些方向. 必需使用少记忆的电脑, 那就改码用别的记存方法. 你没有其他选择.
----------------------------------------------
-
|
作者: |
|
2016/12/2 8:55:30 |
13楼: |
>>下载这么多是为了给各个客户端自行再次处理用的(筛选、汇总、过滤、合并),可能每个客户端需要处理的字段数、记录数、数据范围都不一样,因此有此需要。
难道这个(筛选、汇总、过滤、合并)不是用户借你的代码实现(他总不会去数格子吧?)或ClientDataset的功能实现?难道你的或Clientdataset的实现一定能比Oracle实现的更高效? 所以还是直接把用户操作转成SQL在oracle上执行效果最好。 如果需要,单独给一个用户分页滚记录的窗口,然后再给用户一个获取分组统计结果的窗口。
假设全表排序,你肯定不能拿grid自带的功能排序,你要重新发个sql获取新排序后的分页数据。
假设我要拿所有这些数据作图,要显示所有数据,我也基本可以分几个SQL获取数据,每类(/个)字段的数据一个clientdataset,一个服务器的表对应n多客户端dataset。
----------------------------------------------
-
|
作者: |
vmao (毛小毛) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/2 11:09:05 |
14楼: |
分页是程序员一厢情愿的做法,或者说是不得不妥协的产物。用户当然希望一眼就看到所有的数据,既快又全,没什么不好。特别是一些要做分析的数据,不是每个需求程序员都能提前预料到做好报表的,有些综合性功能可能一年只用到一次,甚至是老板开个会就要的随机不可预料的需求。大数据导出还是需要的。
另外还有一些需求比如我们公司的排产,希望一些有规律的单子(按客户,交货期)什么得排在一起,结果分页导致了她无法确定是否按她的要求排了,结果排完了一看,他妈的还有数据在第二页上,导致排产错误。那么有人说了,把分页放大,问题是放得再大也可能有跨页的,导致单据无法排在一起。这种就是一种典型的无法用分页的场合。(行和行之间有某种偶合需求的时候)
我们公司的订单,我按月分页,一年10万票订单左右,年底的时候各个部门的人是出统计报表的高峰,让他们一个月一导出都嫌麻烦。有的时候领导要做三年来的对比。分页实际是损失用户体验为前提的。不管你愿意还是不愿意。
----------------------------------------------
-
|
作者: |
vmao (毛小毛) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/2 11:25:43 |
15楼: |
这个FireDac我看了一下,设计很像.net,用adapter离线填充数据的。
但是我很需要的一个传统datasnap,ClientDataSet的fetchondemand,packageRecord的按需求自动提取数据包的好像功能没有哦。有谁知道怎么用的?
----------------------------------------------
-
|
作者: |
|
2016/12/2 14:03:48 |
16楼: |
如果大数据是客户再加工的话(需要导出Excel等),可以在服务器端生成文件,然后HTTP、FTP传送。
----------------------------------------------
Delphi爱好者。
|
作者: |
sspeak (sspeak) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2016/12/2 22:02:22 |
17楼: |
@vmao (毛小毛) 这个,TFDDataSet.FetchOptions。 还有,参见help中的 Setting Options (FireDAC)。
----------------------------------------------
-
|
作者: |
|
2016/12/3 14:09:03 |
18楼: |
对8楼抱歉哈,我在7楼说“6楼误会意思了”,没看到有两个6楼。
14楼的意见符合我的实际,确实需要将需要的数据一次性导到客户端分析(为啥这样做的原因不细说了)。
TClientDataSet可以直接赋值TDataSetProvider.data,data的数据是二进制的,比其它格式小,便于从服务端下载,我就想找个可以直接赋值Data的组件来替代。
我用的unidac直连的Oracle,没用fireDAC。 TUniQuery.SaveToXML(Destination: TStream)可以保存数据,但unidac各组件都没有对应的LoadFromXML函数。 TDataSetProvider.data可以赋值给TClientDataset.Data, 但TFDMemTable不能接收TDataSetProvider.data格式数据。
用TFDMemTable替代TClientDataset,需要改动程序的地方比较多。
midas的数据包TDataSetProvider.data,是不是只有TClientDataset才能直接接受?
客户端有很多个,机器配置有高有低,且老板不愿升级老机器。
谢谢各位高手!
----------------------------------------------
-
|
作者: |
|
2016/12/4 15:30:58 |
19楼: |
可以试试我的 IndyDB 组件
http://download.csdn.net/detail/sczyq/9501063
了解一下并无害处
----------------------------------------------
我84砖家
|
作者: |
|
2016/12/4 16:42:42 |
20楼: |
可以考虑分页查询 每一次查询返回2000条 cds.append := query('select * from table limit offset,2000); offset := offser + 2000 这样查询大数据时多点几次 nextpage就可以了
----------------------------------------------
-
|
作者: |
|
2016/12/5 11:17:01 |
21楼: |
DataSetProvider 跟TClientdataset是配套的 没道理DataSetProvider 读得OK而TClientdataset不行。 当CDS大于53M时,你试试TClientdataset的SetProvider,看下会不会还是内存不足。
----------------------------------------------
-
|
作者: |
|
2016/12/9 18:47:56 |
22楼: |
to 21楼:
CDS := DataSetProvider.Data; DataSetProvider读出CDS时没有用到TClientdataset。
TClientdataset很耗内存,ClientDataSet.Data := CDS时出现内存不足。也许是TClientdataset附加了很多东西增加内存占用。
因为我只需要TClientdataset有TDataSet的基本功能,想找个TClientdataset的简化版,或者找替代控件,可以直接接收CDS数据。
实在不行,只好自己精简TClientdataset。
ClientDataSet.Data := CDS赋值太快了,有人比较过,比很多数据集控件快。我测试了一下,比FDMemTable快得多。
参见Delphi内存表控件性能对比 http://blog.csdn.net/shixueli/article/details/7765783
----------------------------------------------
-
|
作者: |
|
2016/12/9 18:57:51 |
23楼: |
我也遇到这个问题。
好像clientdataset里面的都是用的olevariant对象,所以很占内存。
没找到解决方法
----------------------------------------------
-
|
作者: |
|
2016/12/9 19:10:24 |
24楼: |
高人做的内存表性能测试,LoadfromFile,最快的是TClientDataSet,比FDMemTable快了近10倍。
此帖子包含附件:
大小:41.4K |
----------------------------------------------
-
|
作者: |
|
2016/12/9 19:26:01 |
25楼: |
上面LoadfromFile均是载入内存虚拟盘上的文件,最大限度减少硬盘影响。 从数据文件尺寸上,TClientDataSet、SQLMemTable最小。
此帖子包含附件:
大小:13.6K |
----------------------------------------------
-
|
作者: |
|
2016/12/9 21:25:06 |
26楼: |
最新版的10.1 berlin up2 LoadfromFile,FDMemTable和TClientDataSet的差距很小了,LoadFromStream FDMemTable 已经超过 TClientDataSet了。
此帖子包含附件:
大小:63.6K |
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
|
作者: |
|
2016/12/10 19:27:32 |
27楼: |
26楼: 能否把你的测试程序共享一下?
----------------------------------------------
-
|
作者: |
|
2016/12/20 10:58:39 |
28楼: |
FDMemTable代表的是现在和未来,ClientDataSet代表的是过去。
----------------------------------------------
中间件QQ群: 92449782 博客: http://www.cnblogs.com/hnxxcxg/
|
作者: |
|
2016/12/28 10:59:38 |
29楼: |
@27楼,已上传
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
|
作者: |
|
2016/12/29 20:24:14 |
30楼: |
@29楼: 恕我孤陋寡闻,earthsbest_20161228105935.zip中TIdMemDataSet是啥控件?哪个组件里面的?
----------------------------------------------
-
|
|