在从Oracle数据库获取数据时,使用C#出现了System.OutOfMemoryException错误。

3

我希望重新处理数据库中的350万条数据。我们使用的是Oracle PL/SQL 11g。我尝试每次获取1000行进行处理,但在处理了42000行后,它会抛出内存不足异常。 以下是我的代码:

internal void dataReprocessor(int rowCount)
{

    DataTable dataTable = new DataTable();
    int MaxProcessid = AssetProcessorDbHandler.GetMaxProcessId();
    startCount=AssetProcessorDbHandler.GetErrorCount();
    ShowStatus("Max. processid:" + MaxProcessid);
    ShowStatus("Fetching " + rowCount + " rows....");
    do
    {

        dataTable.Clear();
        dataTable = AssetProcessorDbHandler.GetHundreadrows(rowCount, MaxProcessid);
         if (dataTable.Rows.Count > 0)
         {


             for (int i = 0; i < dataTable.Rows.Count; i++)
             {
                int error = 0;
                mainId = Convert.ToInt32(dataTable.Rows[i]["MAINID"]);
                itemId = Convert.ToInt32(dataTable.Rows[i]["ITEMID"]);
                siteid = Convert.ToInt32(dataTable.Rows[i]["SITE_ID"]);
                equipmentId = Convert.ToInt32(dataTable.Rows[i]["EQUIPMENT_TYPE"]);
                qrCode = dataTable.Rows[i]["QRCODE"].ToString();
                string equipmentname = AssetProcessorDbHandler.Getequipmentname(equipmentId);
                error = DbHandler.CheckQRCofEquipment(siteid, equipmentId, itemId, qrCode,ref assetStatus);
                if (error == 1)//Not available in iMapp.
                {
                    MESSAGE=string.Concat("QRCode ",qrCode," for ",equipmentname," is not available in iMapp!");
                    AssetProcessorDbHandler.UpdateSMSintoQRCtable(itemId, MESSAGE);
                }
                else if (error == 2)//Not available in that site.
                {
                    int mappedsite = AssetProcessorDbHandler.Getmappedsiteid(qrCode);
                    MESSAGE = string.Concat("QRCode ", qrCode, " for ", equipmentname, " is mapped with site IN-", mappedsite);
                    AssetProcessorDbHandler.UpdateSMSintoQRCtable(itemId,MESSAGE);
                }
                else if (assetStatus == 1)//Equipment was in Finance verifiaction pending
                {
                    MESSAGE = string.Concat("Finance verification pending of ", equipmentname, " for site IN-", siteid, " in iMapp. Please contact corporate finance team.");
                    AssetProcessorDbHandler.UpdateSMSintoQRCtable(itemId, MESSAGE);
                }
                 AssetProcessorDbHandler.UpdateReprocessed(MaxProcessid + 1, itemId);
            }
        }
    } while (dataTable.Rows.Count > 0);

    ShowStatus("--Completed.--");
    finalCount = AssetProcessorDbHandler.GetErrorCount();
    ShowStatus(startCount + " ROWS PROCESSED.");
    ShowStatus(finalCount + " ROWS ARE STILL ON ERROR.");

}

请有人帮忙吗?这个问题正在升级。


3
你曾经尝试过每次清空并重新创建数据表吗? - Stefan Steinegger
你的代码使用了自定义库,这使得外部人员很难提供具体的建议。你应该提供一个 MCVE - Vikhram
你尝试过使用ExecuteReader来做同样的事情吗?当我需要高效处理大量行数据时,这是我常用的方法。这也是Oracle驱动程序在后台填充DataTable时所使用的方法。 - Vikhram
2个回答

2

每天我们都要处理数百万条记录,但遇到了相同的问题。

问题是我们打开了太多与Oracle数据库的连接,并且没有正确关闭和处理对象。

为了与Oracle数据库交互,最好使用以下代码片段。

using(var oracleConnection = new OracleConnection(connectionString))
{
      oracleConnection.Open()
      //do your stuff here.

}

是的 - 在Oracle .NET数据提供程序文档中有一些关于这个问题的讨论。我记得几年前就已经处理过这个问题了。 - MetalMikester
@MetalMikester 问题在于每次打开 Oracle 数据库连接时都必须调用 dispose。上述方法总是会调用 dispose。 - Bewar Salah
正确。我们最初并没有这样做 - 好吧,不是“总是”,所以我们偶尔会出现小问题。一个冲刺代码审查/重构在问题变得严重之前解决了它。 - MetalMikester
嗨,@Bewar Salah。我们正在使用自己的框架,所以这些核心部分已经定义好了,无法进行编辑。 - Kannan
1
@Kannan,你必须更换框架或重新设计它。你只能更改连接被打开的部分,然后调用Dispose使用using - Bewar Salah

0
你从未处理过你的数据表。每个循环保持一个数据表并正确处理它。
这部分:
do
{

    dataTable.Clear();
    dataTable = AssetProcessorDbHandler.GetHundreadrows(rowCount, MaxProcessid);
     if (dataTable.Rows.Count > 0)
     {

应该阅读:

while(true)
{
    using(var dataTable = AssetProcessorDbHandler.GetHundreadrows(rowCount, MaxProcessid))
    {
        if (dataTable.Rows.Count == 0) break;

1
@Kannan 嗯,那是你唯一可以改进的事情。也许你正在使用的框架有漏洞和泄漏?如果没有任何关于 AssetProcessorDbHandler 的提示,这里不太可能会得到帮助。 - nvoigt
内部 DataTable GetAssetCount(int siteid, int assetid) { string query = string.Concat("SELECT COUNT(*) CNT FROM V2_SITE_ASSET WHERE SITEID=", siteid, " AND ASSETID=", assetid); DataTable checkeQRCinSiteOrNot = new DataTable(); Select(query, checkeQRCinSiteOrNot); return checkeQRCinSiteOrNot; }这是 AssetProcessorDbHandler 中的一个方法,还包含许多类似结构的其他方法。 - Kannan
1
@Kannan 抱歉,但是你的框架界面和内部实现都严重缺乏。你需要获得一个更好的框架或者一个受过更好教育的框架编写者。也许两者都需要。 - nvoigt
@Kannan,“Select”方法是什么样子的? - nvoigt
1
@Kannan 那么我猜你需要将你的样本作为一个错误报告发送给你的框架编写者。 - nvoigt
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接