C#的数据库应用如何解决内存消耗太大的问题呢?

DanceFire 2005-11-07 01:23:17

我用C#做数据库应用的时候,经常遇到的一个问题就是内存消耗特别大。这种情
况在作大数据量的数据库导入导出的时候更为明显。
说一个常见的数据库导入导出的Case, 大家看看比较合理的办法是什么:

环境:
有一个数据库SRC, 里面有500万条记录。有A, B, C三个字段。数据库的大小去
掉水分后,大约500MB的数量级。这个作为源数据库。
数据库DEST是目标数据库,里面有AA, BB, CC, DD, EE, FF等5个字段。

其中,AA, BB, CC的值,是根据SRC.A和B的值计算得来的。而且过程复杂,所以
不适合使用存储过程实现。并且AA, BB, CC不是单独得出,而是同时以SRC.A和
SRC.B得出的(当然,运算三遍可以分别取到各值,但显然运算量就大了3倍)。

目的:
将SRC中的所有数据经过变换,导入到数据库DEST中。

我所尝试过的方法:

1. 最开始用最简单的DataAdapter.Fill(DataTable)的办法。
然后
foreach(DataRow rowsrc in dtSrc){
DataRow row = dtDest.NewRow();
....
dtDest.Rows.Add(row);
}
daDest.Update(dtDest);
dtDest.AcceptChanges();

这明显不是处理大数据量迁移的办法。
最明显的问题是内存消耗极大。500MB的数据库表变成DataSet中的DataTable,
体积括大了很多。再加上dtDest中的拷贝,还有daDest.Update所占用的资源,
很容易就超过了2GB的应用程序内存地址空间,而报错退出了。

2. 我尝试用DataReader读入数据,然后写进dtDest
using( ... reader = ...){
while(reader.Read()){
DataRow row = dtDest.NewRow();
....
dtDest.Rows.Add(row);
}
}
daDest.Update(dtDest);
dtDest.AcceptChanges();
内存占用少了很多,因为SRC读入DataReader的数据,似乎会因为之后不用了,
而有所释放。但是已经进入dtDest的数据没有必要存储在内存中的道理。可是又
没有办法。即使daDest.Update和dtDest.AcceptChanges()后,它依然占用这内
存。因此将daDest.Update和dtDest.AcceptChanges()放入循环内,并不能对内
存使用有所改善。

3. 纯SQL语句。不用DataTable
用SqlCommand.ExecuteNonQuery()的来执行"Insert Into ..."的SQL语句。这样
作后,发现内存占用大大降低了。起到了想达到的“管道”的效果。可是最之而来
的确实更严重的问题。这样做的磁盘空间占用极大。数据库会产生大量的日志,
和废弃空间。以Access为例,程序运行一段时间后,就超过了2GB最大文件的限
制。当执行“压缩和恢复数据库”后,只有100MB左右。可见产生的数据垃圾多
大。当然,采用SQL Server会没有2GB的限制,但是这么大量的日志和废弃空
间,绝对不是一个适合的结果。而且,如果安这种比例,一个500MB的数据库,
将占用超过10GB的存储空间,这是一个太大的浪费了。当然事后可以压缩,但是
处理过程中的空间浪费不容忽视。

4. 不输出到数据库,以CSV输出到文本文件
这是个办法,至少一来不占用什么内存,二来,也没有浪费硬盘空间。但是麻烦
的是,丢失了最重要的类型信息。对于字符串和数字还好说,最多就是字符串没
有了长度,数字没有了精度,但都还能够保存下来。比较麻烦的是有些复杂类
型,比如IMAGE, Binary,和GUID等,这种字节流形式的类型是没有办法报存在
CSV中的。

5.导出到XML
当然用XML好一些,但是其Binary存储的Parse也是很耗费资源的,而且,用XML
如何才能导入到数据库中呢?用C#就又回到老路上来了。SQL Server支持XML,
但是其他的数据库呢?例如Access? 毕竟不是所有人都买的起SQL Server或者所
有场合都适于使用SQL Server的。

对于这个问题大家有什么好办法么?我觉得操作大一点的数据库的时候,内存占
用是个很明显的问题。也许有很简单的方法我没有考虑到而走了很多弯路。谢谢了。

Dancefire
---
CCNA
http://www.dancefires.com/
http://blog.csdn.net/dancefire/
MSN: dancefire@263.net
I am interested in Operating System, Embedded System and Network Security.

...全文
792 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
DanceFire 2005-11-14
  • 打赏
  • 举报
回复
To FlashElf(銘龘鶽) :

已经试验完Transaction了。发现试验与想象有差异,不过却搞懂了Transaction的意义了。

我做了2个程序,都是循环向数据库中插入随机的100万条记录。但是程序A,是不用transaction;程序B是传输开始前用一次conn.BeginTransaction(),传输结束后,transaction.Commit()。

第一次运行后,我惊奇的发现,两种方法,都不会造成数据库的过分膨胀。插入完成后20MB的数据,两者的大小竟然一个字节不差。数据库中也没有过多的垃圾数据和日志。这和我前几天测试的结果不大相同,甚为奇怪。

经过了思考后,我开始怀疑,会不会是因为我用access同时打开了这个文件,造成垃圾数据暴涨的?经过测试后,发现果然如此。

对两个程序测试之后发现结果一样。我用Access打开这个mdb数据库,但是什么都不作。然后我开始运行我的程序。当运行完成后,数据库的大小竟然变成了将近400MB。现象和我当初测试的一样。两个程序执行结果竟然文件字节数一个字节也不差,可见Transaction对于文件大小的影像可能会很小。不过,我后来又想到,如果Transaction有效的话,这说明造成的20倍的数据库体积膨胀的原因很有可能是数据垃圾。

不过发现了Transaction一个现象,就是不用Transaction的时候,打开数据表,随时可以看到新增的数据。而用了Transaction后,直到Transaction.commit()后,才看到数据。我想这就是Transaction可以能够回卷的秘密。之前的操作都是虚的,可能存储在了什么位置,直到commit,才将其变实。

至于数据垃圾的问题,从硬盘访问情况来看。我发现,在不打开Access数据库的时候,似乎大部分的操作都在内存中,数据库好像对大批连续的Insert做了缓存,当超出某一定的数额后,批量的写入数据库。而当我用Access打开数据库的时候,似乎因为另外的一个程序打开了数据库,从而导致缓存机制失败,硬盘写操作开始上升,并且数据库的性能下降了,大约比我不同时用Access打开数据库,慢了1.5-2倍。看来Access对于多用户访问的支持还是比较差的。

由此而想到了,网上很多数据库都用Access来作后台,原因就是因为其易于发布,易于处理。但是,在那种多用户的环境下,其数据库的垃圾数据一定也会如此,有不少问题。看来,微软作的没错,在VS中绑定SQL Server express,就是想办法诱导大家从Access上迁移到SQL Express上来。

关于原始的问题,现在看来,我想正如FlashElf所说第三种方法是对的。唯一需要注意的就是,在操作Access数据库的时候,同时不要有任何其他应用程序打开该Access数据库。否则,会造成文件体积的激增。至此,这个问题,应该可以告一段落了。

Thanks again.

Dancefire
--
http://www.dancefires.com/
http://blog.csdn.net/dancefire
msn: dancefire [at] 263.net
beyondtkl 2005-11-09
  • 打赏
  • 举报
回复
呵呵 好问题。。GZ.
伴老思源 2005-11-09
  • 打赏
  • 举报
回复
建个作业丢给SQL去完成
nik_Amis 2005-11-09
  • 打赏
  • 举报
回复
up[
DanceFire 2005-11-09
  • 打赏
  • 举报
回复
To FlashElf(銘龘鶽):
谢谢你。
首先,我确实使用的是Command的Parameter来做改变的,CommandText并没有变化。其次,"Transaction"是一个好的提示。我还没有通过Transaction来改进程序。

另外,你能不能清晰几个在我头脑中的概念,有些关于Transaction的概念,我还不够明确。

举例而言,对于100,000个记录,将其插入到数据库中:
1. 如果我不用Transaction,是不是日志中将会有100,000条Transaction Log?
2. 如果我用Transaction, Transaction Log中的日志条目数,是仅仅10个条目?还是100,000个条目(每一个记录插入都有单独的Transaction Log) +外面嵌套的10个Transaction 条目呢(每个Transaction再有额外的Log)?换句话说,是不是如果我指定Transaction后,系统就不会给每个Insert来一个条目了,而只给整体的Transaction来一个条目?
3. 哪些数据库支持Transaction? 我知道SQL Server支持,其他的如何?特别是Access支持么?

另外,感谢对Access 2003可以导入XML的提示,我以前不知道这个也可以。:)Google之后,发现从2002开始,就已经支持导入XML了,只不过是Access指定的格式,2003以后可以通过指定XSL,来导入各种格式的XML。这个很有帮助。
superljy 2005-11-09
  • 打赏
  • 举报
回复
DanceFire 2005-11-09
  • 打赏
  • 举报
回复
To jFresH_MaN(十一月的萧邦-夜曲):
这些我已经做了,提高了一点点性能,但是没有改变大局。大局在DataTable中还是存储了如此多的数据,而不会去释放。

To seemon(飞虫):
是的。你所提到的那些方法,恰好就是我当前正在使用的方法。可以完成功能。但是,这种方法是代码中被打断成很多块,并且有的时候你必须处理除了分块外的额外的工作。特别是当在进行多线程程序的时候,有一些其他的线程还在使用这个连接的时候,同步问题就变的更为复杂。

Dancefire
--
http://www.dancefires.com/
http://blog.csdn.net/dancefire
msn: dancefire [at] 263.net
DanceFire 2005-11-09
  • 打赏
  • 举报
回复
到家了,重新用中文发一遍好了。对不起各位了。

To Johnny_de(天才):

数据库的缓冲区和操作系统中文件系统缓冲区,确实大大提高了数据访问的性能。但是就这个Case而言,提高的幅度则不大。如果我们使用DAO,或者DTS,他们的结果要明显优于ADO.Net,所以,我认为这个是ADO.Net的问题。也许是有些使用方法适合这个case,而我还不知道如何去使用。:)

To zhzuo(秋枫):
我刚刚拜读了那片文章,其中所说的那些概念是不错的,但是对于这个问题并不足够。Effective C#中说的这些问题确实是真理,不仅仅是C#,其他语言也有类似问题,但是和现在所讨论的问题关系不大。问题是,在这个例子中,没有那么多的对象被创建,只有一些少量的ADO.Net,至于其中内部创建了多少对象,并且是如何处理的,这个就是我无法控制得了。所以,问题应该在ADO.Net的使用方法上。对于这样简单的代码(我指逻辑和代码量),太难优化了。我想,唯一优化的办法,就是我换一种方法来使用ADO.Net, 而不用我上述的那些方法。

Dancefire
--
http://www.dancefires.com/
http://blog.csdn.net/dancefire
msn: dancefire [at] 263.net
DanceFire 2005-11-09
  • 打赏
  • 举报
回复
I am sorry, this computer, which belong to my friend, forgot to be installed any Chinese input method, so I can only see the Chinese, but can't input Chinese, so use English instead of.

To Johnny_de(天才):

The buffer of database and file system do increase the performance of the operation, but it's not so much relative to this case. If we use DAO, maybe we will got better result than ADO.Net. So, I think it should be ADO.Net problem, maybe there are some way to do that which I don't know :)

To zhzuo(秋枫):
I just read the article, it's good, but not enough. What he said is truth, but, actually, not realitive to this case. The problem is, in this case, there is no so much object were created, only ADo.Net stuff. So, the problem should be the way I use ADO.Net. It's very hard to improve such simple code. I think only way to improve it, should be change the way of using ADO.Net.

To jFresH_MaN(十一月的萧邦-夜曲):
I have done this already, it improves the performance a little, not change the whole situation.

To seemon(飞虫):
Yeah, The way you metioned before is actually the way I currently using. However, it always break the code to several part, and sometimes, you must handle additional work to implement this way. And it become very hard in multithread application, which there is another threads using the connection.

To FlashElf(銘龘鶽):
Thanks, first I do use Command Parameter to do the change, not whole CommandText. second, "Transaction" is good hint, I haven't try to use transaction to improve it.
Could you clear some concepts in my head?

For 100,000 records, and insert them into the database,
1) If I don't use transaction, will there be 100,000 entrys of transaction log?
2) If I do use transaction on 10,000 records once, will there be only 10 entrys of transaction log? or more 100,000(for each records) + 10 (10 transactions)?
3) Which database engine support transaction? I know SQL Server support it, how about others, especially Access?

Thanks for the hint of Access 2003 can import XML, which I don't know before. :) After google, I know from 2002, it's already support import XML, but only Access's self format, now, it can transform to any form by specify xsl file. It's helpful.

I don't need customers has MS Access application, only Jet(database engine) is enough, and it cinluded by Windows default distribution. MS Access Jet actually is free, so I can develop the application which can run on any computer without install extra software.

But for SQL Server, there do have a lot limits. First, I need customer provide a computer which is running windows server edition, in most small businesses, it's hard. Second, they should have a LAN, in some cases it's hard too. It will limit the usage of the application. Third, of course, SQL Server will cost so many money. It's not necessary for small businesses.

Since SQL Server 2005 express edition was release yesterday. I will migrate my develop target on 2005 express edition. It is also free.

I know the concept of "using illegal software doesn't matter" has been deeply plant in our Chinese heart, but I am jumping out. Everyone know it really bad for whole software life chain, and so many good Chinese developer can't keep developing the good software, because nobody will to buy it, and no money to support them. Many of them give up.

The concept should be changed, "If I don't have money, I will choose free software. And I will never do anything illegal. No money never means I have no personailty."

Thanks again.
q_po_o 2005-11-07
  • 打赏
  • 举报
回复
mark
曲滨_銘龘鶽 2005-11-07
  • 打赏
  • 举报
回复
在问一句哥们!(你的用户怎么都那么穷)都用 Access ???
Access 也是 D 的,SQL SERVER 也是 D 的,Oracle 也是 D 的
想清楚其实使用 Access 当数据库也属于盗版的
为啥不用好点的。
曲滨_銘龘鶽 2005-11-07
  • 打赏
  • 举报
回复
方法3 是正确的(SQL SERVICE DTS 也是用这种方法的),Access 的问题可以不用考虑

不过
1)不要用连串方式连接 insert 语句(因为每次更改command 的 commandText 都需要重新编译SQL)用参数方式,就没问题了,我以前作 sql Service 到 oracle 的数据迁移都是这么用的


2)如果你的记录比较多开事务,10000 条提交一次,这样事务日至比较少会


//
XML (C# DataSet) Access 是可以导入的 不过要 office 2003 里的 Access
woainin 2005-11-07
  • 打赏
  • 举报
回复
DataReader是最好的方法
seemon 2005-11-07
  • 打赏
  • 举报
回复
我觉得可以分段更新
foreach(DataRow rowsrc in dtSrc){
DataRow row = dtDest.NewRow();
....
dtDest.Rows.Add(row);
}
daDest.Update(dtDest);
dtDest.AcceptChanges();
一次只取出部分数据进行处理,分多次处理这样内存应该占用的少些
jFresH_MaN 2005-11-07
  • 打赏
  • 举报
回复
在适当时侯,置不用的对象为null
调用GC.collect();
能减少一些内存消耗。
marvelstack 2005-11-07
  • 打赏
  • 举报
回复
下面的文章值得参考,主要还是写代码的技巧,
http://news.csdn.net/news/newstopic/28/28530.shtml
Johnny_de 2005-11-07
  • 打赏
  • 举报
回复
多用缓存吧,研究下操作系统,我有本书讲到关于数据库的性能时就要考虑到操作系统了。书名:《数据库--模型、语言、设计》
jxufewbt 2005-11-07
  • 打赏
  • 举报
回复
及时关闭Connection
cansum396 2005-11-07
  • 打赏
  • 举报
回复
及时释放内存吧
gyf19 2005-11-07
  • 打赏
  • 举报
回复
在NET2.0速度更快了!!
加载更多回复(2)

110,566

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧