数据库
首页 > 数据库> > c# – 使用datareader读取数百万个数据时,如何避免数据库连接丢失问题?

c# – 使用datareader读取数百万个数据时,如何避免数据库连接丢失问题?

作者:互联网

我有一个从数据库表中读取数据的类库.现在这个数据库表是客户端数据库,我的应用程序只有连接字符串和sql查询来打开连接,执行sql查询,读取数据并执行一些操作.
这个操作是什么,它有点复杂(基本上是业务规则).

现在,用户以特定格式提交sql查询,我的类库知道从sql查询结果中选择哪些列.

我不知道我的类库会处理的记录数.它也可能是100,200或数百万的数据.

目前,类库正在处理驻留在oracle上的9000万个数据.我正在使用SQLDATAREADER读取此数据.

现在的问题是避免内存异常我正在使用sql数据读取器读取数据但是逐个读取9千万个数据,然后对每个记录执行一些操作,连接将保持打开状态,目前我面临的问题是连接丢失:

ORA-03135: connection lost contact

1解决方案可能是读取块中的数据,但正如我所说,我不知道我可能正在处理的记录数量,并且SQL查询不在我手中,因为它是由我的类库提取的用户提交的.

有什么办法可以避免连接问题吗?

更新:

public class LongRunningTask : IDisposable
{
        public void Start(DbConnection connection, string sql)
        {
            using (var cmd = connection.CreateCommand())
            {
                cmd.CommandText = sql;
                cmd.CommandTimeout = 0;
                connection.Open();
                using (var dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    //read 1 by 1 record and pass it to algorithm to do some complex processing
                }
            }
        }
}

算法并不慢,这不是问题.主要问题是读取部分,如果当前来自ORACLE的数据为9千万,则速度很慢.

我已经测试了针对SQL SERVER的1亿个数据,我没有遇到过这个问题(虽然有时会出现传输层错误),尽管这个过程花费了很多时间.我只是在使用ORACLE时遇到了这个问题.

解决方法:

将数据读取器打开数小时并不是一个好主意.即使一切配置正确,线路上某处也可能出现瞬态错误(如您提到的传输层错误).

您可以在客户端代码中添加重试逻辑,以使其更加健壮.执行此操作的一种方法是跟踪上次处理的记录,并尝试在连接失败时重新连接并从该位置“恢复”.

private const int MAX_RETRY = 10;
private const int RETRY_INTERVAL_MS = 1000;
private string lastProcessedPosition = null;

public void Start(string connectionString, string sql)
{
    var exceptions = new List<Exception>();
    for (var i = 0; i < MAX_RETRY; i++)
    {
        try
        {
            if (Process(connString, sql, lastProcessedPosition)) return;
        }
        catch(Exception ex)
        {
            exceptions.Add(ex);
        }
        System.Threading.Thread.Sleep(RETRY_INTERVAL_MS);
    }
    throw new AggregateException(exceptions);
}

您的Process()方法将重新连接并跳过已处理的行:

public bool Process(string connString, string sql, string resumeFromPosition = null)
{
    using ()// init your connection, command, reader
    {
        if (resumeFromPosition != null)
        {
            while (dr.Read() && dr.ToPositionString() != resumeFromPosition)
            {
                // skipping already processed records
            }
        }
        while (dr.Read)
        {
            // Do your complex processing

            // You can do this every N records if accuracy is not critical
            lastProcessedPosition = dr.ToPositionString();
        }
    }
    return true;
}

dr.ToPositionString()是一种扩展方法,您可以根据表模式创建一行唯一的行.

标签:c,ado-net,oracle-sqldeveloper
来源: https://codeday.me/bug/20190716/1473780.html