本来是个帖子,但是问的问题没有得到实际的答案。所以干脆放到Blog上来好了,当作长期的一个题目。这样如果有人看的话,也许能够给我一些线索。
我用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中。
我所尝试过的方法:
然后
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的应用程序内存地址空间,而报错退出了。
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()放入循环内,并不能对内
存使用有所改善。
用SqlCommand.ExecuteNonQuery()的来执行"Insert Into ..."的SQL语句。这样
作后,发现内存占用大大降低了。起到了想达到的“管道”的效果。可是最之而来
的确实更严重的问题。这样做的磁盘空间占用极大。数据库会产生大量的日志,
和废弃空间。以Access为例,程序运行一段时间后,就超过了2GB最大文件的限
制。当执行“压缩和恢复数据库”后,只有100MB左右。可见产生的数据垃圾多
大。当然,采用SQL Server会没有2GB的限制,但是这么大量的日志和废弃空
间,绝对不是一个适合的结果。而且,如果安这种比例,一个500MB的数据库,
将占用超过10GB的存储空间,这是一个太大的浪费了。当然事后可以压缩,但是
处理过程中的空间浪费不容忽视。
这是个办法,至少一来不占用什么内存,二来,也没有浪费硬盘空间。但是麻烦
的是,丢失了最重要的类型信息。对于字符串和数字还好说,最多就是字符串没
有了长度,数字没有了精度,但都还能够保存下来。比较麻烦的是有些复杂类
型,比如IMAGE, Binary,和GUID等,这种字节流形式的类型是没有办法报存在
CSV中的。
当然用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.
我用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.