我之前尝试过两次寻找此问题的解决方案,但不幸的是,这些答案并未提供永久修复,所以我在这里再试一次。
我有一个SQL Server存储过程,它返回150万个整数ID的列表。我从ASP.NET / VB代码中调用此SP并执行SqlDataReader:
m_dbSel.CommandType = CommandType.StoredProcedure
m_dbSel.CommandText = CstSearch.SQL.SP_RS_SEARCH_EX
oResult = m_dbSel.ExecuteReader(CommandBehavior.CloseConnection)
然后我将该读者传递给类构造函数以构建Generic List(Of Integer)。代码非常基础:
Public Sub New(i_oDataReader As Data.SqlClient.SqlDataReader)
m_aFullIDList = New Generic.List(Of Integer)
While i_oDataReader.Read
m_aFullIDList.Add(i_oDataReader.GetInt32(0))
End While
m_iTotalNumberOfRecords = m_aFullIDList.Count
End Sub
问题是 - 这并没有读取所有150万条记录,数字不一致,最终计数可能是500K或100万等。(最常见的是“魔力”数 524289 记录退回)。我在执行命令时尝试使用CommandBehavior.SequentialAccess
设置,但结果也是不一致的。
当我在SSMS中运行SP时,它几乎立即返回一定数量的记录并显示它们,但随后继续运行几秒钟,直到完成所有150万条记录 - 它是否与此有关?
的更新 的
<小时/> 过了一会儿,我发现在非常罕见的情况下,上面的循环代码会引发异常:
System.NullReferenceException:未将对象引用设置为实例 一个对象。在 System.Data.SqlClient.SqlDataReader.ReadColumnHeader(Int32 i)
因此发生了一些内部故障。它看起来像我替换
While i_oDataReader.Read
m_aFullIDList.Add(i_oDataReader.GetInt32(0))
End While
使用
处理整数While i_oDataReader.Read
m_aFullIDList.Add(i_oDataReader(0))
End While
处理对象 - 代码似乎运行没有故障并返回所有记录。
去图。
答案 0 :(得分:4)
基本上,正如我们在注释(*)中所说的那样,问题不在于SqlDataRead
,存储过程或SQL中的SQL。相反,您的List.Add
失败了,因为它无法为2 ^(n + 1)项分配额外的内存来扩展List并将现有的2 ^ n项复制到其中。大部分时间你的n = 19(所以524289项),但有时它可能更高。
你可以做三件基本的事情:
Pre-Allocate:正如您所发现的那样,通过预先分配,您应该可以在任何数量的1.5到3倍的任何地方使用gwet。如果您提前知道自己拥有多少项目,那么效果最佳,因此我建议您提前排除SELECT COUNT(*)..
,或者添加COUNT(*) OVER(PARTITION BY 1)
列并从第一列中选出返回行以预分配列表。这种方法的问题在于你仍然非常接近你的极限,并且在不久的将来很容易耗尽内存......
重新配置:现在你只能获得最多2 ^ 22字节的内存,理论上你应该能够得到2 ^ 29-2 ^ 30。这意味着您计算机上的某些内容阻止您将可写虚拟内存限制扩展到高位。可能的原因包括页面文件的大小和来自其他进程的竞争(但还有其他可能性)。解决这个问题,你应该有足够的空间。
流媒体:你真的需要同时内存中的所有150万项内容吗?如果没有,您可以动态确定哪些不需要(或提取您需要的信息),那么您可以像SqlDataReader
一样使用流式传输来解决此问题。只需阅读一行,使用它,然后丢失它并继续下一行。
希望这有帮助。
(* - 显然,谢谢@granadaCoder和@MartinSmith)
如果你真的认为问题完全取决于List数据结构(并且不你刚刚耗尽内存),那么还有其他一些方法可以解决List结构的分配问题行为。一种方法是实现另一个List类(如IList(of Integer)
)。
通过接口,它看起来与List相同,但在内部它将具有不同的分配方案,方法是将数据存储在嵌套的List(of List(of Integer))
中。每1000个项目,它将创建一个新的List(of Integer)
,将其添加到父嵌套列表,然后使用它来添加下1000个项目。
之前我没有提出这个问题的原因是因为,就像预先分配一样,这可能会让你更接近你的记忆限制,但是,如果这是问题,你最终还是会用完(只是与预分配一样)因为此限制太接近您需要的实际项目数(150万)。
答案 1 :(得分:0)
基本上,您使用select query I suggest you to add order by in your query
读取SqlDataReader中的所有记录,并按Acceding顺序对所有记录进行排序,并且它们也在SqlDataReader中以加入顺序读取。
我在上一个项目中也遇到过这个问题我从数据库中读取了 more than 2 million records
,其唯一ID为serialNo,但此记录不按顺序排列 after 1000 records
< / strong>它会跳转到 21, 00, 263th record
,并且所有记录的顺序都是错误的。
然后我使用 (order by serialNo)
此查询,我的问题解决了,你不需要做任何额外的事情,只需在你的选择查询中下订单,它就会对你有用
我希望这对你有所帮助。