求解惑:关于SqlDataReader读取数据库二进制数的问题

easytable 2017-02-10 01:12:47
byte[] buffer = new byte[1024*16]; 
long offset = 0;
int read;
using(MemoryStream stream = new MemoryStream(8192))
{
while((read = reader.GetBytes(col, offset, buffer, 0, buffer.Length)) > 0) {
offset += read;
stream.Write(buffer, 0, read);
}
buffer = stream.ToArray();
}

byte[] buffer = (byte[])reader[col];


上面两段代码中的reader是SqlDataReader,我现在做了一个Asp.Net的接口,给其他应用使用。
问题1:
上面两段代码有什么不同,在网上查了资料,说是大型数据用第一种,中小型用第二种。大型数据这个是多大?
问题2:
我用的第一段代码,下载一个35M左右的文件,每次重启IIS之后,第一次下载没问题,但是第二次下载就会报内存溢出,之后每次下载都会报错,除非重启IIS。这里不管我其他的业务逻辑代码,就我上面的第一段代码,会造成内存使用很大吗?
求解答,谢谢。
...全文
284 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
easytable 2017-02-14
  • 打赏
  • 举报
回复
引用 11 楼 okkk 的回复:
read = reader.GetBytes这里的read是读取的实际字节数,buffer的长度只能在2000以下,不是的吧。 --read是实际读取的字节数没错,但它一般是取决于两个原因: Socket接收到的数据量 还有用户缓冲区的接收容量。 一个socket包的最大数据量只有1200字节不到,这个是网络基础,不会改变。 Socket发送过来的数据可能会在系统空间合并,所以read的值可能会比2000大。 考虑缓冲区的问题,就更麻烦。 所谓的大型数据,就是大于1200字节,就会被分包,就可以用第一种方法。 其实更好的办法是把缓冲区的数据,直接记录到磁盘,节约内存。就是把memerystream,换成filestream,才是第一种方法的实际意义。 代码没有问题,需要更多的信息来调试: 建议: 对大于1M的内存空间,手动回收内存比较好。 弄一个静态的计数器,看看执行到什么时候会抛出异常。
我这个不是Socket通信的,是用SqlDataReader读取SQL数据库里面的二进制字段的。
easytable 2017-02-14
  • 打赏
  • 举报
回复
我这两种都是需要全部读取的,我以为当数据大的时候,需要那种类似的分页处理一样去分段读取的。
easytable 2017-02-14
  • 打赏
  • 举报
回复
引用 12 楼 sp1234 的回复:
“第一种情况”,不能认为 GetBytes 像 Stream.Read 一样去编程。你应该调试抛出异常时的状态,修改你的程序。例如应该在 read<bufferSize 的时候停止循环,而不应该继续进行一次 GetBytes 操作。 所谓“大数据”,估计是在整个数据内容中仅截取一小部分数据放入 buffer 的情况,或者是用很小的 buffer 来作为中介来拷贝数据到其它设备的情况,也就是不占用什么内存的情况。而你的两种程序是一样的,都没有什么在大数据上更好的特性,所以第二种反而更简单更好。 比如说你的字段保存了网页下载结果,现在我们只是读取一个网页的头部1000字节,看看其中的 <meta> 写了什么,而不需要读取都去整个内容。这就可以使用 GetBytes 方式。
谢谢P哥。 弄清楚了什么时候用GetBytes。 现在不知道为什么在使用第一种情况的时候,读取35M的文件,可以看到w3wp.exe进程的内存波动很大,一百两百的往上加,IIS是默认配置,应该是使用物理内存的60%,4G内存,32位系统。 另外在使用第二种的时候,有没有什么限制,或者说需要注意的地方,超过多大的文件会报错。(额,这个我也会自己测试下,只是想确认下,有个底。)
easytable 2017-02-13
  • 打赏
  • 举报
回复
引用 6 楼 okkk 的回复:
考虑到传输层:Socket是分包传送过来的,虽然没有测试,问题1·的答案应该在 2000以下。 其实就是第一段 int read; 的值,可以调试的时候看看。 代码是有问题的: MemoryStream(8192) --->这个8192是什么意思,它居然比 buffer = new byte[1024*16] 小。 去掉这个 8192试试。 内存不足不应该是操作系统抛出的,只分配 24K内存!!
额,reader是SqlDataReader, read = reader.GetBytes这里的read是读取的实际字节数,buffer的长度只能在2000以下,不是的吧。 MemoryStream(8192)这个容量只是初始化的时候给的,它是可以扩展的。
  • 打赏
  • 举报
回复
要知道,所谓“内存溢出”几乎都不是什么物理内存满了的情况,哪怕你只有几百个字节的数据也可能产生“内存溢出”的报告,所以不要见了这个提示就说是“内存很大”。你应该调试并且改正程序的 bug。
  • 打赏
  • 举报
回复
“第一种情况”,不能认为 GetBytes 像 Stream.Read 一样去编程。你应该调试抛出异常时的状态,修改你的程序。例如应该在 read<bufferSize 的时候停止循环,而不应该继续进行一次 GetBytes 操作。 所谓“大数据”,估计是在整个数据内容中仅截取一小部分数据放入 buffer 的情况,或者是用很小的 buffer 来作为中介来拷贝数据到其它设备的情况,也就是不占用什么内存的情况。而你的两种程序是一样的,都没有什么在大数据上更好的特性,所以第二种反而更简单更好。 比如说你的字段保存了网页下载结果,现在我们只是读取一个网页的头部1000字节,看看其中的 <meta> 写了什么,而不需要读取都去整个内容。这就可以使用 GetBytes 方式。
okkk 2017-02-13
  • 打赏
  • 举报
回复
read = reader.GetBytes这里的read是读取的实际字节数,buffer的长度只能在2000以下,不是的吧。 --read是实际读取的字节数没错,但它一般是取决于两个原因: Socket接收到的数据量 还有用户缓冲区的接收容量。 一个socket包的最大数据量只有1200字节不到,这个是网络基础,不会改变。 Socket发送过来的数据可能会在系统空间合并,所以read的值可能会比2000大。 考虑缓冲区的问题,就更麻烦。 所谓的大型数据,就是大于1200字节,就会被分包,就可以用第一种方法。 其实更好的办法是把缓冲区的数据,直接记录到磁盘,节约内存。就是把memerystream,换成filestream,才是第一种方法的实际意义。 代码没有问题,需要更多的信息来调试: 建议: 对大于1M的内存空间,手动回收内存比较好。 弄一个静态的计数器,看看执行到什么时候会抛出异常。
okkk 2017-02-13
  • 打赏
  • 举报
回复
引用 7 楼 easytable 的回复:
[quote=引用 6 楼 okkk 的回复:] 考虑到传输层:Socket是分包传送过来的,虽然没有测试,问题1·的答案应该在 2000以下。 其实就是第一段 int read; 的值,可以调试的时候看看。 代码是有问题的: MemoryStream(8192) --->这个8192是什么意思,它居然比 buffer = new byte[1024*16] 小。 去掉这个 8192试试。 内存不足不应该是操作系统抛出的,只分配 24K内存!!
额,reader是SqlDataReader, read = reader.GetBytes这里的read是读取的实际字节数,buffer的长度只能在2000以下,不是的吧。 MemoryStream(8192)这个容量只是初始化的时候给的,它是可以扩展的。[/quote] // // Summary: // Initializes a new instance of the System.IO.MemoryStream class with an expandable // capacity initialized as specified. // // Parameters: // capacity: // The initial size of the internal array in bytes. // // Exceptions: // System.ArgumentOutOfRangeException: // capacity is negative. public MemoryStream(int capacity); 好吧,是可以扩展的。
easytable 2017-02-13
  • 打赏
  • 举报
回复
引用 8 楼 starfd 的回复:
io读取是要时间的 第一种是给要读取的量很大时用的,比如可能这个读取出来是20m,那么如果你直接以第二种方式读取,这段时间的io都被你这个读取占用了 第二种就是直接读取,因为写入时本身就是byte[]
谢谢。 第一种方式,byte[] buffer和还实例化了一个MemoryStream,是不是内存使用会比较多。我用这两种方法,其他业务代码没改变,但是在使用第一种的时候,会报内存溢出的问题,而第二种确不会,只是读取一个35M的文件。 还有请问,第一种和第二种一般各适用于什么情况?或者说具体多大之类用第二种直接读取即可,超过多少之后用第二种?
  • 打赏
  • 举报
回复
io读取是要时间的 第一种是给要读取的量很大时用的,比如可能这个读取出来是20m,那么如果你直接以第二种方式读取,这段时间的io都被你这个读取占用了 第二种就是直接读取,因为写入时本身就是byte[]
okkk 2017-02-12
  • 打赏
  • 举报
回复
考虑到传输层:Socket是分包传送过来的,虽然没有测试,问题1·的答案应该在 2000以下。 其实就是第一段 int read; 的值,可以调试的时候看看。 代码是有问题的: MemoryStream(8192) --->这个8192是什么意思,它居然比 buffer = new byte[1024*16] 小。 去掉这个 8192试试。 内存不足不应该是操作系统抛出的,只分配 24K内存!!
easytable 2017-02-10
  • 打赏
  • 举报
回复
引用 4 楼 zhchfsky 的回复:
确保SqlDataReader连接关闭了
这个肯定会关闭的。 byte[] buffer = (byte[])reader[col]; reader.GetBytes(col, offset, buffer, 0, buffer.Length) 我现在是想问,这两种有没有什么区别?和我第一段代码,会不会造成内存使用过大的问题?
闪耀星星 2017-02-10
  • 打赏
  • 举报
回复
确保SqlDataReader连接关闭了
easytable 2017-02-10
  • 打赏
  • 举报
回复
引用 1 楼 wangjun8868 的回复:
1:都是 byte[] 没区别,区别是外部生成byte[]的方式 2: 二进制这种存到数据库这种东西,自己学习的时候玩玩还行,如果实际应用效率极差, 尤其是页面上吗,访问的人多了,还还了的,为什么直接存文件路径?现在都是这么做的
额,这个是接手维护的老系统,之前word文件是存在数据库里的。
easytable 2017-02-10
  • 打赏
  • 举报
回复
@以专业开发人员为伍 @caozhy @hanjun0612 求解惑,谢谢。
编程有钱人了 2017-02-10
  • 打赏
  • 举报
回复
1:都是 byte[] 没区别,区别是外部生成byte[]的方式 2: 二进制这种存到数据库这种东西,自己学习的时候玩玩还行,如果实际应用效率极差, 尤其是页面上吗,访问的人多了,还还了的,为什么直接存文件路径?现在都是这么做的

110,502

社区成员

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

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

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