访问一个 IBM® Informix® 数据库所需的时间通常在不到一秒钟到某个期望的时间段之内,这取决于具体的数据库操作。但是,有时花费时间可能会比您预期的要长得多,这有许多原因,比如网络速度、系统性能、系统负载等。在最糟的情况下,Informix 客户端可能会因为无法获得期望的服务器响应而永远被阻塞。本文将解释,在 SQL 或连接请求花费比预期更长时间时,如何中断该 SQL 或请求,以改善您的 Informix 数据库应用程序的性能。

Yunming Wang, 高级支持工程师, IBM

Yunming Wang 是 IBM Informix Advanced Problem Diagnostics (APD) 团队的高级支持工程师。他从 1998 年开始在 IBM 负责 Informix 方面的工作,专注于 Informix 和 DB2 编程 API 和数据库连接。专业领域包括 ODBC、JDBC、OLEDB/.Net、ESQL/C 和 TCP/IP。最近,他加入了一个 IDS 虚拟应用程序项目,负责处理虚拟化和云计算技术。在加入 Informix 之前,他是一名软件开发人员。他于 1995 年获得了阿肯色大学(University of Arkansas)的计算机工程硕士学位。



2012 年 6 月 11 日

免费下载:IBM® Informix® 11.7 试用版(包括 Ultimate Edition、Developer Edition 和 Innovator-C Edition)
下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。

简介

数据可访问性在众多业务中变得越来越重要。Informix 服务器因其快速的性能和高度的可靠性而著称。但是,从客户端访问存储在 Informix 服务器上的数据同样依赖于硬件和操作系统的性能。Informix 提供了许多技术来保证数据访问的性能和可靠性,比如包含连接管理器的 MACH-11、自动数据库统计更新等。但是,本文主要介绍 Informix 如何处理某些由无法预测的随机系统事件所引起的异常场景,这些事件可能会导致数据库操作花费比预期更长的时间,其中包括缓慢的网络速度、意外的系统负载过重、TCP 包的丢失或其他无法预见的事件。

在很多时候,Informix 客户端会使用阻塞模式 中的 TCP 套接字与 Informix 服务器进行通信,这意味着客户端会一直等到有可用的服务器数据来实现通信。对于缓慢的连接请求,您可以中断并重试该请求,其方法是基于您的正常系统行为,将 Informix 环境变量 INFORMIXCONTIMEINFORMIXCONRETRY 设置为合适的值。

在数据库服务器执行一个 SQL 请求时,在来自数据库服务器的预期响应可供读取之前,应用程序会被阻塞。有时您可能想要中断一个 SQL 请求来获得应用程序控制权,因为您不想等到当前 SQL 语句完成操作。例如,如果您无意之中为一个长查询提供了错误的搜索条件,您想取消 SELECT 语句,而不是等待不需要的数据。如果它是一个多层应用程序,那么情况可能更糟。Informix 客户端(包括 CSDK 和 JDBC)允许用户在执行中中断 SQL 请求。应用程序的设计和实现需要提供用户取消 SQL 请求的支持。

如果运行的应用程序是来自第三方供应商,那么若该应用程序中没有相关选项,会很难中断 SQL 请求。为了处理此情形,Informix 在最新的客户端版本中引入了一项新功能,支持您在 Informix 客户端中启用 TCP 套接字超时,以便中断请求而无需更改任何代码。

本文中所列出的样例程序都进行了简化,仅用于演示用途。


Informix 连接超时

概述

连接到一个 Informix 数据库通常只需不到一秒到几秒的时间(具体取决于网络速度),但有时它所花的时间比预期更长。INFORMIXCONTIMEINFORMIXCONRETRY 是两个可控制连接行为的客户端环境变量。

INFORMIXCONTIME

INFORMIXCONTIME 环境变量指定在每次尝试建立数据库服务器连接时,数据库连接请求会持续几秒钟才返回一个错误。如果 INFORMIXCONTIME 环境变量设置为 0 或一个小于 0 的值,那么数据库连接请求将等到该操作成功或失败。如果没有对其变量进行设置,那么各个系统中的默认值可能会有所不同:

  • ESQL/C:INFORMIXCONTIME 的默认值设置为 60 秒。您可以在 OS 环境中使用 OS 命令为 ESQL/C 程序设置 INFORMIXCONTIME 环境变量。
    • UNIX®/Linux®:使用 exportsetenv 命令,具体取决于 shell。
    • Windows®:Set 命令或控制面板。
  • ODBC:它默认设置为 15 秒。相反,您可使用此 SQL_ATTR_LOGIN_TIMEOUT 连接属性指定连接请求的连接超时间隔。如果同时设置了 INFORMIXCONTIMESQL_ATTR_LOGIN_TIMEOUT,则 SQL_ATTR_LOGIN_TIMEOUT 连接属性会具有更高的优先级。
  • OLEDB 和 .NET:默认值为 15 秒。您可为 Informix OLEDB 提供程序设置 INFORMIXCONTIME 环境变量,但这不适用于使用 IfxConnection.ConnectionTimeout 属性来设置连接超时的 Informix .NET 提供程序。
  • JDBC:默认值为 0,表示连接从不超时。可以在 JDBC 连接 URL 或 JDBC DataSource 连接属性中设置 INFORMIXCONTIME 环境变量。JDBC 3.70.JC1 引入了另一个名为 LOGINTIMEOUT 的环境变量。可使用它指定轮询服务器端口来建立连接的时间段(以毫秒为单位)。如果应用程序没有在指定时间内连接到 Informix 数据库服务器,将返回一个错误。它只能设置为一个 DataSource 属性。在 JDBC 连接 URL 中对它进行设置将不会生效。

您可以基于正常应用程序行为将 INFORMIXCONTIME 环境变量设置为合适的值。

如果连接请求涉及到搜索 DBPATH,则可能看到连接请求所花的时间长于 INFORMIXCONTIME 限制,以至于返回了一个错误。如果 DBPATH 包含多个服务器,那么 INFORMIXCONTIME 值需要除以 DBPATH 中包含的服务器条目数量。在此情况下,您应相应地增加 INFORMIXCONTIME 值。

INFORMIXCONRETRY

INFORMIXCONRETRY 环境变量指定客户端在 INFORMIXCONTIME 所指定的时间限制内客户端应对每个数据库服务器执行的连接尝试总数。例如,如果 INFORMIXCONTIME 设置为 30 秒并且 INFORMIXCONRETRY 设置为 2,那么若初始连接尝试无法实现,客户端将尝试在第 15 秒时重新发送连接请求。在终止之前,如果需要,它将在第 30 秒时进行另一次连接尝试。

在 CSDK 和 JDBC 3.70.xC1 以前,INFORMIXCONRETRY 环境变量的默认值为 3。从 JDBC 3.70.xC1 开始,新的默认值为 1,这意味着在初始连接尝试之后仅进行一次重试。


应用程序中的 Informix SQL 请求中断

概述

在 Informix 服务器执行 SQL 请求的过程中,Informix 客户端可出于各种原因而中断该请求,包括 SQL 语句运行时间太长,在 SQL 语句执行时所需数据无法找到,用户发出了一个错误的查询等。如果使用 Informix ODBC、.NET 或 JDBC 客户端,您可以取消 SQL 请求或让驱动程序实现超时。对于 ESQL/C 应用程序,您只能将应用程序设计为通过调用 Informix 库函数来中断它。

无论使用何种 Informix 客户端 API,Informix 客户端都会以相同方式处理 SQL 语句中断,那就是使用 TCP out_of_band 信号向 Informix 数据库服务器发送一个取消请求。这意味着取消请求的包在一个独立的流中以 “紧急或加急数据” 的形式发送。当 Informix 服务器收到取消请求时,它会尝试中断对 SQL 请求的处理。如果服务器成功中断了 SQL 请求,它会向客户端返回错误 -213(ISAM 错误 -157),而无需关闭当前连接。

在中断 SQL 请求后重新获得应用程序的控制权,分配给该 SQL 语句的任何资源将保持其分配。任何打开的数据库、游标和事务均会保持打开。任何系统描述符区域或 SQLDA 结构会保持被分配。应用程序会负责优雅地终止该程序。它必需释放资源并回滚任何打开的事务。如果应用程序使用自动提交事务模式(Informix 服务器的默认事务管理模式),则应用程序无需处理针对中断的 SQL 语句的已打开事务。

如果应用程序在未处理任何打开的事务的情况下终止,则 Informix 服务器会将未完成的事务当作丢弃或长久的事务进行清除,事务中所有经过部分修改的数据会被丢弃。

不是每个 SQL 请求都是可中断。一些类型的数据库操作无法中断,而另一个操作会在某些时刻无法中断。数据库服务器可能没有立即终止 SQL 语句的执行,并且您的应用程序可能未在它发送中断请求后立即重获控制权。您可以在 信息中心 中找到可中断 SQL 语句的列表。

在 JDBC 应用程序中中断 SQL 请求

在 JDBC 应用程序中,一个长期运行的查询可使用以下两个 JDBC 标准方法进行中断:

  • Statement.cancel()
  • Statement.QueryTimeout()

Statement.cancel() 允许一个线程取消由另一个线程执行的语句。它主要用在多线程应用程序中。通常可实现一个监视线程来检查 SQL 语句在工作线程上的执行时间。当 SQL 语句运行时间比超时间隔更长时,监视线程将调用 Statement.cancel() 方法来中断该语句。当调用 Statement.cancel() 方法时,Informix JDBC 驱动程序将向服务器发送取消请求。

Statement.setQueryTimeout() 方法限制了 Informix JDBC 驱动程序等待 SQL 语句执行的秒数。如果超出限制,将抛出一个 SQL 异常。Informix Statement.setQueryTimeout() 方法会调用 Statement.cancel() 方法来中断 SQL 语句。

下面的清单是一个使用 Statement.setQueryTimeout() 方法实现查询超时的示例。

清单 1. 调用 Statement.setQueryTimeout() 的示例
try
{
  String strSQL = "SELECT * FROM mytable";
  Statement stmt = conn.createStatement();

  // setting the query timeout to 5 seconds
  stmt.setQueryTimeout(5);


  // executing the query
  ResultSet rs = stmt.executeQuery(sql);

  rs.close();
  stmt.close();
}
catch(SQLException e)
{
  System.out.println("Query failed!");
  System.out.println("FAILED: " + e.getMessage());
}

在 ODBC 应用程序中中断 SQL 请求

在 ODBC 应用程序中,您可调用以下两个 ODBC API 来中断 SQL 请求:

  • SQLCancel()
  • SQLSetStmtAttr(),其中 SQL_ATTR_QUERY_TIMEOUT 属性设置为秒数

如果发生以下情况,可调用 ODBC 函数 SQLCancel() 来中断 SQL 请求:

  • 它在执行时需要输入数据。在此情况下,SQLCancel() 仅关闭语句句柄而不向服务器发送任何内容,并返回错误代码 -11010(将取消视为 FreeStmt/Close)。
  • 它在线程模式下执行。当中断一个由另一个线程执行的 SQL 请求时,如果服务器正在处理该 SQL 请求,则 SQLCancel() 会向服务器发送一个取消请求,并返回 SQL_SUCCESS。否则,SQLCancel() 会关闭语句句柄,并返回错误代码 -11010(将取消视为 FreeStmt/Close)。

当通过 SQLCancel() 取消 SQL 语句时,应用程序会收到以下 ODBC 错误消息:[Informix][Informix ODBC Driver][Informix]Statement interrupted by user

下面的示例会创建两个线程:一个是执行查询的工作线程,另一个线程在查询运行时间超过 20 秒时取消查询。

清单 2. main 函数
int main()
{
    SQLHENV         henv;
    SQLHDBC         hdbc;
    SQLHSTMT      hstmt;
    SQLCHAR connectOutput[512] = {0};
    SQLSMALLINT result;
    int rc;
    HANDLE hThread[2];
    DWORD dwThreadID[2];

      // Allocate the ODBC environment and save handle.
      rc = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
      if ( (rc != SQL_SUCCESS_WITH_INFO) && (rc != SQL_SUCCESS)) {
         printf("SQLAllocHandle(Env) Failed\n\n");
         return -1;
      }

      // Allocate ODBC connection handle and connect.
      rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
     if (checkError (rc, SQL_HANDLE_ENV, henv, 
         (SQLCHAR *) "Connection Handle Allocation failed\n")) {
         Cleanup();
         return -1;
     }        

     // Connect to Informix
      rc = SQLDriverConnect(hdbc, 
         NULL,
         (UCHAR*) "DSN=ifx1170fc4;",
         (SQLSMALLINT) SQL_NTS
         , connectOutput
         , sizeof(connectOutput)
         , &result
         , SQL_DRIVER_NOPROMPT);
     if (checkError (rc, SQL_HANDLE_DBC, hdbc, 
	(SQLCHAR *) "Connecting to Informix server failed\n")) {
         Cleanup();
         return -1;
      }
      
      /* Allocate the statement handle */
      rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
     if (checkError (rc, SQL_HANDLE_DBC, hdbc, 
	(SQLCHAR *) "Statement Handle Allocation failed\n")) {
         Cleanup();
         return -1;
      }

    hThread[0] = CreateThread( 
          	NULL,               
            	0,                  
            	do_test,      // worker thread function 
            	hstmt,              
            	0,                  
            	&dwThreadID[0]);    
 
    // Check the return value for success. 

    if (hThread[0] == NULL)
	   printf("Unable to create thread!!\n");

    printf("\n The worker thread created!\n");
	
    fprintf (stdout, "\nCreating the cancel thread\n");
    hThread[1] = CreateThread( 
          	NULL,                
            	0,                   
            	do_cancel,     // cancel thread function 
            	hstmt,               
            	0,                   
            	&dwThreadID[1]);     
 
    // Check the return value for success. 
    if (hThread[1] == NULL)
	   printf("Unable to create thread!!\n");

    printf("\n The cancel thread created!\n");

    fprintf (stdout, "Wait until all threads have terminated.\n");
    WaitForMultipleObjects( 2, hThread, TRUE, INFINITE);

   rc = SQLDisconnect(hdbc);
   checkError (rc, SQL_HANDLE_ENV, henv, (SQLCHAR *) "SQLDisconnect failed");
   
   rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);   
   checkError (rc, SQL_HANDLE_ENV, henv, (SQLCHAR *) " Connection Handle Free failed\n");

   rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
    if (rc != SQL_SUCCESS)
    {
        fprintf (stdout, "Environment Handle Free failed\n");
        return -1;
    }
    
   return(0)
}
清单 3. 由取消线程调用的函数
DWORD WINAPI do_cancel (void *arg)
{
    /* Declare variables */
    SQLHSTMT        hstmt;
    SQLRETURN       rc = 0;

    hstmt = (SQLHSTMT)arg;
    if (hstmt == NULL)
    {
       fprintf (stdout, "Cancel thread: Invalid statement handle\n");
       return -1;
    }

    Sleep(5000);
    
    rc = SQLCancel(hstmt);

    if (checkError (rc, SQL_HANDLE_STMT, hstmt, 
	(SQLCHAR *) "Error in cancel thread - cancelling the statement failed\n"))
	return -1;
	
    printf ("do_cancel: the worker thread is cancelled due to running for too long.\n");
    
    return 0;    
}
清单 4. 由工作线程调用的函数
DWORD WINAPI do_test (void *arg)
{
    /* Declare variables */
    SQLHSTMT        hstmt;
    SQLCHAR SQLStmt[255] = "insert into tab1(col,col2) values (3,'ghl')";
    SQLRETURN       rc = 0;

    hstmt = (SQLHSTMT)arg;
    if (hstmt == NULL)
    {
       fprintf (stdout, "Worker thread: Invalid statement handle\n");
       return -1;
    }
        
    /* Set lock mode to wait for repro assuming the tab1 is locked */
    rc = SQLExecDirect(hstmt, (UCHAR*)"set lock mode to wait;", SQL_NTS);
    checkError (rc,SQL_HANDLE_STMT,hstmt,(SQLCHAR *)"do_test -- set lock mode failed\n");

    /* Prepare and Execute the query */
    rc = SQLExecDirect( hstmt, SQLStmt, SQL_NTS );
    if (checkError (rc, SQL_HANDLE_STMT, hstmt, 
	(SQLCHAR *) "Error in worker thread -- SQL statement execution failed\n"))
    {
	return -1;
    }
    
    return 0;
}

SQL_ATTR_QUERY_TIMEOUT 语句属性用于指定在返回到应用程序之前,等待执行 SQL 语句的秒数。在默认情况下,此属性设置为 0,表示没有超时,客户端将等到 SQL 请求执行完成或失败返回错误。您可将 SQL_ATTR_QUERY_TIMEOUT 属性设置为一个正整数值,以允许 Informix ODBC 驱动程序在 SQL 语句运行时间长于超时间隔时中断该语句。

下面的示例将 SQL 语句超时间隔设置为 25 秒。

rc = SQLSetStmtAttr(hstmt, SQL_ATTR_QUERY_TIMEOUT,  (SQLPOINTER)25,  0);

当超过 25 秒的超时间隔时,Informix ODBC 驱动程序尝试中断该语句,方法是向服务器发送一个取消请求,并在取消完成时报告以下错误。

	[Informix][Informix ODBC Driver]Timeout expired.
	[Informix][Informix ODBC Driver][Informix]Statement interrupted by user.

当 Informix 数据库服务器收到取消请求时,服务器尝试中断 SQL 请求的处理,如果 SQL 请求中断成功,则向客户端返回错误 -213。服务器在中断 SQL 请求时不会中断当前连接。适当处理错误并在必要时关注任何未完成的事务,这是 ODBC 应用程序的责任。

在 .NET 应用程序中中断 SQL 请求

Informix .NET 提供程序支持使用 IfxCommand.Cancel() 方法来取消一个命令的执行。如果取消尝试失败,不会生成异常。如果没有要取消的操作,则不会发生任何事。

在 SQL 语句执行过程中取消它时,会向应用程序返回以下错误消息:

ERROR [HY008][Informix .NET provider][Informix]Statement interrupted by user.sqlerrm(tab1)

其中 tab1 是涉及到的表名称。

下面的示例是一个多线程 C# 程序,它创建两个线程:一个执行长查询;另一个会在查询运行时间长于 5 秒时调用 IfxCommand.Cancel() 来中断查询。

清单 5. 调用 IfxCommand.Cancel() 的样例程序
using System;
using System.Data;
using System.Text;
using System.EnterpriseServices;
using IBM.Data.Informix;
using System.Threading;

class Timeout
{
    public static IfxCommand my_cmd;

    public static void do_Cancel()
    {
        Thread.Sleep(5000);  //sleep for 5 seconds before starting to cancel the command
        try {
           my_cmd.Cancel();
           System.Console.WriteLine("SQL command cancelled" );
        }
        catch (Exception ex)
        {
            System.Console.WriteLine(“ Exception occurred in Cancel()" );
            Console.WriteLine(ex.Message);
        }               
    }

    public static void do_Cmd()
    {
        my_cmd.CommandText = "select count(*) from systables a, systables b, systables c";
        System.Console.WriteLine(“ Running a long query - may take minutes ...");
        try {
           IfxDataReader reader = my_cmd.ExecuteReader();
           System.Console.WriteLine(“ Query finished");
           reader.Close();
        }        catch (Exception ex)
        {
            System.Console.WriteLine(“ Exception occurred in ExecuteQuery()" );
            Console.WriteLine(ex.Message);
        }        
    }

    static void Main()
    {
        string connectionString = 
                   "Server=ifx1170uc4;database=stores;uid=informix;password=mypassword;";
        try
        {
            using (IfxConnection connection = new IfxConnection(connectionString))
            {
                connection.Open();

                my_cmd = connection.CreateCommand();

                System.Console.WriteLine(“ Starting the command thread");
                Thread cmdThread = new Thread(new ThreadStart(do_Cmd));
                cmdThread.Start();

                System.Console.WriteLine(“ Starting the cancel thread");
                Thread cancelThread = new Thread(new ThreadStart(do_Cancel));
                cancelThread.Start();

                cmdThread.Join();
                cancelThread.Join();

            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

在中断长查询时,该程序将显示以下输出:

C:\clientsdk\dotnet>cmdcancel.exe
Starting the command thread
Starting the cancel thread
Running a long query - may take minutes ...
SQL command cancelled
Exception occurred in ExecuteReader()
ERROR [HY008] [Informix .NET provider][Informix]Statement interrupted by user.

您也可以将 IfxCommand.CommandTimeout 属性设置为在终止执行一个命令的尝试或终止执行一个命令之前要等待的秒数。终止之后会生成一个错误。

下面的 C# 样例创建一个 IfxCommand,并将它的 CommandTimeout 属性设置为 20 秒。

清单 6. 设置 IfxCommand.CommandTimeout 属性的样例程序
using System;
using System.Data;
using System.Text;
using IBM.Data.Informix;

class Program
{
    static void Main()
    {
        string connectionString = 
                  "Server=ifx1170uc4;database=stores;uid=informix;password=mypassword;";
        try
        {
            using (IfxConnection connection = new IfxConnection(connectionString))
            {
                connection.Open();
                IfxCommand  my_cmd = connection.CreateCommand();
                System.Console.WriteLine("Running a long query ...");
                my_cmd.CommandText = 
                            "select count(*) from systables a, systables b, systables c";
                my_cmd.CommandTimeout = 5;   // time out if it runs longer than 5 seconds
                IfxDataReader reader = my_cmd.ExecuteReader();
                reader.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

在长查询超时时,该程序将显示以下输出:

C:\clientsdk\dotnet\>cmdtimeout.exe
Running a long query ...
ERROR [HYT00] [Informix .NET provider]Timeout expired.
ERROR [HY008] [Informix .NET provider][Informix]Statement interrupted by user.

在默认情况下,Informix .NET 提供程序将 CommandTimeout 属性设置为 30 秒。如果不在 .NEt 应用程序中设置 IfxCommand CommandTimeout 属性,SQL 命令将在超过 30 秒超时间隔后发生超时。

CommandTimeout 属性设置为 0,以禁用 SQL 命令超时,该命令会一直运行到操作成功或失败。

在 ESQL/C 应用程序中中断 SQL 请求

要中断数据库服务器正在处理的 SQL 请求,您可以在 ESQL/C 应用程序中使用 sqlbreak() 库函数。不同于其他 Informix 客户端 API,Informix ESQL/C 没有 SQL 语句超时功能可供应用程序使用。相反,应用程序的设计和实现必需允许用户在必要时中断 SQL 请求。您可以在 ESQL/C 应用程序中使用两个方法来允许中断 SQL 请求:

  • 为应用程序用户提供在 SQL 请求开始执行后使用用户定义的信号处理函数中断该请求的能力。当用户按下中断键时,应用程序会被解锁并调用 SIGINT 信号处理函数。这个信号处理函数包含一个 sqlbreak() 调用,用于中断数据库服务器。
  • 使用 sqlbreakcallback() 函数指定一个超时间隔。在超过超时间隔后,应用程序就会被解锁并调用该回调函数。这个回调函数包含一个 sqlbreak() 调用来中断数据库服务器。

Informix ESQL/C 演示程序 timeout.ec 演示了如何设置超时间隔。它随带了 Informix CSDK 安装程序,可以位于 UNIX 或 Linux 上的 $INFORMIXDIR/demo/esqlc 下或 Windows 上的 %INFORMIXDIR%\demo\esqldemo 下。

此程序使用 sqlbreakcallback() 函数执行以下操作:

  • 为 SQL 请求的执行指定 200 毫秒的超时间隔。
  • 注册要在一个 SQL 请求开始和结束时以及在超过超时间隔时调用的 on_timeout() 回调函数。

如果 SQL 请求的执行超出了该超时间隔,该回调函数会使用 sqldone() 函数来确保数据库服务器仍在繁忙运行,提示用户确认中断,然后使用 sqlbreak() 函数向数据库服务器发送一个中断请求。查看有关此演示程序的更多信息,请访问 Informix 11.70 信息中心


TCP 套接字层中的 Informix SQL 请求中断

概述

您有时可能遇到以下情景:

  • 您想要中断一个从应用程序发出的查询,而该应用程序没有取消 SQL 语句的选项。
  • 您想要取消一个无法中断的 SQL 请求。
  • 您的应用程序由于某个网络问题而被阻塞且没有响应,比如,导致 Informix 客户和服务器相互等待的 TCP 包缺失。

发生此情况时,很多时候您可以结束当前进程并重新启动它。如果一个应用服务器或代理发生此情况,重新启动该进程会影响到所有当前的用户,有时会导致严重的后果。

为了帮助 Informix 用户处理上述情景,Informix 在最新版本的 Informix 客户端(包括 CSDK 和 JDBC)中引入了一项功能。该功能允许 Informix 用户为 Informix 服务器的特定连接启用 TCP 套接字超时选项。如果为一个 Informix 数据库连接启用了 TCP 套接字超时选项,在超过超时期限后,所有被阻止接收预期服务器响应的 Informix 客户端线程都会由操作系统计时。一定要理解,超时是由 TCP 套接字层执行的,该层实际上是操作系统的一部分并且可中断客户端上任何类型的数据库操作,而不通知 Informix 服务器。

任何时候只要应用程序发生 TCP 超时,Informix 客户端与服务器之间的通信都会由于 TCP 套接字层返回的系统错误而被破坏。根据所使用的 Informix 客户端 API,Informix 客户端会相应地向应用程序返回通信错误或抛出系统异常。应用程序必须适当地处理该错误或异常,以确保它能够照顾任何打开的事务和分配的资源。在检测到破坏的通信时,Informix 服务器会回滚当前的事务并在必要时清除资源。

强烈建议您谨慎使用此超时功能,因为 TCP 套接字超时实际上是一个系统错误或异常,会终止当前的会话而不通知 Informix 服务器。通常应该将其设置为一个永不过期的值。可以将其用作一种帮应用程序摆脱某些异常场景的方式,比如由丢失的 TCP 包导致的挂起。当一个连接被 TCP 套接字超时中断时,在服务器检测到该会话被丢弃之前,Informix 服务器将不会对当前正在处理的 SQL 请求执行任何操作。如果该请求正在等待一个锁,该会话可保留到该锁可用时。

在为您的应用程序启用此功能时,如果有必要,请联系 IBM 技术支持人员寻求帮助。

JDBC 中的 TCP 套接字超时

Informix JDBC 驱动程序使用一个 Java 套接字类与 Informix 服务器进行通信,该类默认使用阻塞模式。出于此原因,当 Informix JDBC 驱动程序从一个套接字连接读取数据时,如果没有来自服务器的直接响应可用,它将一直等待。从 Java™ 1.5 开始,Java 网络应用程序可以调用方法 Socket.setSoTimeout() 以在套接字上设置超时。

 public void setSoTimeout(int timeout) throws SocketException

此方法使用指定的超时(以毫秒为单位)来设置 SO_TIMEOUT 字段。如果将此选项设置为一个非零的超时值,则仅会在此时间内阻塞与此套接字相关联的 InputStream 上的 read() 调用。如果超时过期,则会抛出一个 java.net.SocketTimeoutException,但该套接字仍然有效。

为了充分利用这个可能在必要时为 Informix 用户带来帮助的 Java 功能,Informix 在 JDBC 驱动程序 V3.50.JC4 中引入了一个名为 IFX_SOC_TIMEOUT 的 JDBC 环境变量。如果将它设置为一个正整数值,Informix JDBC 将使用指定的值设置该套接字上的超时,并在读取操作超过该超时时抛出一个异常。

该超时可使用以下两种方式之一进行设置(以毫秒为单位):

  • 在 Connection URL 中:IFX_SOC_TIMEOUT=1000
  • 在一个数据源中:dataSource.setIfxIFX_SOC_TIMEOUT(1000);

超时值 0 被视为无限的超时或没有超时。

ODBC 中的 TCP 套接字超时

对于 Informix 非 Java 客户端,从 CSDK 3.50.xC5 开始提供 TCP 套接字读取超时功能。

Informix ODBC 用户可通过不同方式启用 TCP 套接字读取超时:

  • 使用一个指定的超时来设置 RECVTIMEOUT 连接关键字(以秒为单位)。该示例显示了,在一个连接字符串中将 ODBC 套接字读取超时设置为 180 秒:
          rc = SQLDriverConnect(hdbc, 
    			NULL,
    			(UCHAR*) "DSN=ifx1170fc4; RECVTIMEOUT=180",
    			(SQLSMALLINT) SQL_NTS,
    			connectOutput,
    			sizeof(connectOutput),
    			&result,
    			SQL_DRIVER_NOPROMPT);
  • 在打开连接之前,使用一个指定的超时来设置 SQL_INFX_ATTR_RECVTIMEOUT 连接属性(以秒为单位)。例如:
    rc=SQLSetConnectAttr(hdbc,SQL_INFX_ATTR_RECVTIMEOUT,(SQLPOINTER)180,SQL_IS_UINTEGER);
  • 使用一个指定的超时为 Informix 服务器 SQLHOSTS 条目设置 RTO 选项。如果同时设置了 RTORECVTIMEOUT,RTO 选项具有更高优先级。SQLHOSTS 中的 RTO 选项对无 DSN 连接无效。下面的 SQLHOSTS 条目示例将超时设置为 180 秒:
    ifx1170fc4    onsoctcp      ifx_host1   9088    b=32767,rto=180

如果您同时在 ODBC 和 Informix SQLHOSTS 条目中启用了 TCP 套接字读取超时,SQLHOSTS 中的 RTO 选项将具有更高的优先级。

.NET 提供程序中的 TCP 套接字超时

对于 Informix .NET 提供程序用户,此功能仅能通过设置 Informix 服务器 SQLHOSTS 条目的 RTO 选项来启用,除非为连接使用了一个 ODBC DSN。在 Windows 上,可在 Informix Setnet32.exe 中设置 RTO。您必须将 IfxCommand. CommandTimeout 设置为 0,才能使 RTO 选项生效。

ESQL/C 中的 TCP 套接字超时

只能通过设置 SQLHOSTS RTO 选项来为 ESQL/C 应用程序启用 TCP 套接字超时。这意味着您不可为 ESQL/C 中的特定连接设置此选项,这与 JDBC 和 ODBC 中的不同。当超时发生时,应用程序会收到错误 -25580,表示网络功能中发生了一个系统错误。


结束语

出于许多原因,数据库操作有时花的时间比预期要长得多。本文介绍了如何使用 IBM Informix 提供的选项解决意外的长期运行的数据库请求,从而改善应用程序性能。

参考资料

学习

获得产品和技术

  • 使用 IBM 产品评估试用版软件 构建您的下一个开发项目,可直接从 developerWorks 下载这些软件。
  • 现在您可以免费使用 DB2。下载 IBM 软件下载:IBM DB2 Express-C 10.1,一个面向社区的免费 DB2 Express Edition 版本,提供了与 DB2 Express Edition 相同的核心数据功能,还为构建和部署应用程序提供了牢固的基础。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=820608
ArticleTitle=优化 Informix 数据库访问
publish-date=06112012