执行 OracleDataAdapter.Fill

我有一个使用 .NET Framework 3.5 和 Oracle.Dataaccess 19.1 的 VB.NET 程序,该程序连接到 Oracle 远程数据库服务器 11g (pooling=false) 以查询数据。

程序运行良好,但有一次在一个蓝色的月亮(不固定时间,最快可以在 2 周或几个月后),程序只是挂起。在几次事件后(在每次事件后添加日志),我们能够确定并识别导致挂起的程序行,即 oracleDataAdapter.Fill(dataset),如下面的代码片段所示。

    Private Function FetchData(connection As OracleConnection,str_SQL As String,dataset As DataSet) As Integer
    Dim oracleDataAdapter As New OracleDataAdapter(connection.CreateCommand())

    Try
        If connection.State <> ConnectionState.Open Then
            connection.Open()
        End If

        oracleDataAdapter.SelectCommand.AddToStatementCache = False
        oracleDataAdapter.SelectCommand.NotificationAutoEnlist = False
        oracleDataAdapter.SelectCommand.CommandTimeout = 60
        oracleDataAdapter.SelectCommand.CommandText = str_SQL
        Dim int_Count As Integer = oracleDataAdapter.Fill(dataset)

        Return int_Count
    Catch ex As Exception
        LogError(ex)
    Finally
        oracleDataAdapter.Dispose()
    End Try
End Function

甚至认为我们能够识别挂起的代码,但我们不知道为什么挂起。我们确实尝试通过调用 OracleCommand.Cancel 来中断查询,但没有帮助。

我们考虑到网络问题,因此我们尝试通过在执行查询期间拔掉应用服务器上的网线并在几秒钟后重新插入来模拟 oracle 客户端和数据库服务器通信丢包。在某些情况下,程序只是挂在无限获取数据的行上(如上面的代码段突出显示)。

我们通过将数据库指向 XE 18c 进行了相同的测试。我们无法模拟上面的挂起问题,而是在执行查询期间拔出和插入网线时抛出了 ORA 03113 错误(通常发生在 60 秒内)。这仍然可以,因为程序能够继续而不是挂起,错误处理可以重试并优雅地恢复。

8 月 7 日添加 我们开发了程序(在 .NET 中)来尝试重现挂起问题。该程序只是不断从数据库中获取数据。在正常执行中,查询需要约 4 秒才能获取约 140,000 条记录。挂起可以模拟 15 次中的 4 次(~30%)。

下面是重现挂起的步骤

  1. 运行应用程序。该应用程序只是不断地从 数据库服务器。功能如下图。
  2. 观察查询执行情况,大约在第 2 秒或第 3 秒(根据屏幕上的日志显示),拔掉网线
  3. 在大约第 15 秒插回网线
  4. 程序偶尔会挂在oracleDataAdapter.Fill(dataset)行上

我们针对不同版本的非托管和托管 Oracle 客户端 11、12 和 19 以及数据库服务器 11 XE 和 18 XE 进行了测试,可以重现挂起问题。

这是行为还是 Oracle 客户端?有什么建议可以打破执行吗?或者有什么建议可以处理这种行为?

fei2008e 回答:执行 OracleDataAdapter.Fill

根据OracleDataAdapter Class

线程安全

所有公共静态方法都是线程安全的,尽管实例方法 不保证线程安全。

对于连接,建议只在需要时才打开连接。如果一个连接持续被使用,保持它打开可能是有意义的。否则,每次都打开/关闭连接。当然,如果您有其他需要使用连接执行的操作,您可以将连接作为参数传递给方法。但是,请在完成操作后关闭连接。

以下假设已安装 NuGet 包 Oracle.ManagedDataAccess

尝试以下操作之一:

Imports Oracle.ManagedDataAccess.Client

Public Module Helper

    Public Function FetchDataDS(connectionStr As String,str_SQL As String) As DataSet
        Dim int_Count As Integer = 0
        Dim ds As DataSet = New DataSet()

        Using connection As OracleConnection = New OracleConnection(connectionStr)
            'open
            connection.Open()

            Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.NotificationAutoEnlist = False,.CommandTimeout = 60,.Connection = connection,.CommandText = str_SQL
                    }}

                int_Count = oracleDataAdapter.Fill(ds)
            End Using
        End Using

        Return ds
    End Function

    Public Function FetchDataDS(connection As OracleConnection,str_SQL As String) As DataSet
        Dim int_Count As Integer = 0
        Dim ds As DataSet = New DataSet()

        If connection.State = ConnectionState.Closed Then
            'open
            connection.Open()
        End If

        If Not connection.State = ConnectionState.Open Then
            Throw New Exception("Error (FetchDataDS) - Connection state is: " & connection.State.ToString())
        End If

        Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

            int_Count = oracleDataAdapter.Fill(ds)
        End Using

        Return ds
    End Function

    Public Function FetchDataDS(connectionStr As String,str_SQL As String,ds As DataSet) As Integer
        Dim int_Count As Integer = 0

        Using connection As OracleConnection = New OracleConnection(connectionStr)
            'open
            connection.Open()

            Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

                int_Count = oracleDataAdapter.Fill(ds)
            End Using
        End Using

        Return int_Count
    End Function

    Public Function FetchDataDS(connection As OracleConnection,ds As DataSet) As Integer
        Dim int_Count As Integer = 0

        If connection.State = ConnectionState.Closed Then
            'open
            connection.Open()
        End If

        If Not connection.State = ConnectionState.Open Then
            Throw New Exception("Error (FetchDataDS) - Connection state is: " & connection.State.ToString())
        End If

        Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

            int_Count = oracleDataAdapter.Fill(ds)
        End Using

        Return int_Count
    End Function

    Public Function FetchDataDT(connectionStr As String,str_SQL As String) As DataTable
        Dim int_Count As Integer = 0
        Dim dt As DataTable = New DataTable()

        Using connection As OracleConnection = New OracleConnection(connectionStr)
            'open
            connection.Open()

            Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

                int_Count = oracleDataAdapter.Fill(dt)
            End Using
        End Using

        Return dt
    End Function

    Public Function FetchDataDT(connection As OracleConnection,str_SQL As String) As DataTable
        Dim int_Count As Integer = 0
        Dim dt As DataTable = New DataTable()

        If connection.State = ConnectionState.Closed Then
            'open
            connection.Open()
        End If

        If Not connection.State = ConnectionState.Open Then
            Throw New Exception("Error (FetchDataDT) - Connection state is: " & connection.State.ToString())
        End If

        Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

            int_Count = oracleDataAdapter.Fill(dt)
        End Using

        Return dt
    End Function

    Public Function FetchDataDT(connection As OracleConnection,dt As DataTable) As Integer
        Dim int_Count As Integer = 0

        If connection.State = ConnectionState.Closed Then
            'open
            connection.Open()
        End If

        If Not connection.State = ConnectionState.Open Then
            Throw New Exception("Error (FetchDataDT) - Connection state is: " & connection.State.ToString())
        End If

        Using oracleDataAdapter As OracleDataAdapter = New OracleDataAdapter With {
                .SelectCommand = New OracleCommand With {
                        .AddToStatementCache = False,.CommandText = str_SQL
                    }}

            int_Count = oracleDataAdapter.Fill(dt)
        End Using

        Return int_Count
    End Function
End Module

无论在哪里捕获异常,也要捕获 Oracle.ManagedDataAccess.Client.OracleException -

类似于以下内容:

Try
    'ToDo: add desired code
       ...
Catch ex As OracleException
    'ToDo: log exception
Catch ex As Exception
    'ToDo: log exception
End Try

资源

本文链接:https://www.f2er.com/9672.html

大家都在问