连接池连接

连接池概述

连接池可以让一个应用系统使用数据库连接时,不用每次都去重建一个新的连接,而是直接从连接池已经连接好的连接中获取连接;当连接池中的一个链接一旦创建,应用程序可以重复使用这个链接而不需要去创建一个全新的连接。

用连接池中的连接可以显著提高性能,因为应用程序可以接受进行连接所需的时间开销。这对于应用中间层或者需要反复连接和断开的应用程序尤其重要,否则连接会有较高的时间开销浪费在连接和断开上。

除了提高性能,连接池结构还可以实现让环境句柄和其管理的连接在单个过程(进程)被多个独立组件所使用,这就意味着同一个过程(进程)中的独立组件可以相互交互而不会察觉到。连接池中的一个连接可以被多个组件重复使用。

微软ODBC3.8标准支持了连接池功能,连接池要求ODBC驱动必须是完全线程安全的,且连接不能有线程亲和力的。意味着驱动能够处理任何线程任何时间的调用,且能够在一个线程中去做连接,在另外一个线程去使用,在第三个线程中去关闭。

连接池是由驱动管理器管理的,因此让任何数据库的ODBC驱动都具备了连接池功能。当应用程序调用SQLConnect 或者 SQLDriverConnect是从连接池中拿一个链接,调用SQLDisconnect将连接返回到连接池中。连接池的大小根据请求资源自动增长,如果连接长时间不用会被回收:如果某个连接在一段时间内不活跃(没有被使用过),则会将其从连接池中删除。连接池数量的限制受内存和服务器limits等因素。 驱动管理器决定是否使用连接池中的连接,通过调用SQLConnect 或 SQLDriverConnect并传入参数或者设置连接属性。

当驱动管理器设置为连接池模式时,在分发连接之前,他需要能确定连接是否在工作状态(未被使用且连接正常),否则,每当发生临时网络故障连接断开时,驱动程序管理器就会继续将死连接发送给应用程序去使用,导应用程序异常。 ODBC3.x标准中制定了SQL_ATTR_CONNECTION_DEAD这个连接属性,这个属性时只读的,返回SQL_CD_TRUE或SQL_CD_FALSE,SQL_CD_TRUE表示连接已失效,而值SQL_CD_FALSE表示连接仍处于活动状态。

驱动程序必须有效地实现此选项,否则会削弱连接池的性能,具体如下:调用链接的这个属性不会与服务端交互,相反的是,驱动需要立即返回连接的最后一个已知状态;最后一次与服务器交互失败,则连接失效,成功则未失效。

连接池使用步骤

  1. 通过调用SQLSetEnvAttr将SQL_ATTR_CONNECTION_POOLING环境属性设置为SQL_CP_ONE_PER_DRIVER或SQL_CP_ONE_PER_HENV来启用连接池。必须在应用程序分配要为其启用连接池的共享环境(环境句柄分配之前)之前进行此调用。对SQLSetEnvAttr的调用中的环境句柄应设置为null,这会使SQL_ATTR_CONNECTION_POOLING成为进程级属性:SQL_CP_ONE_PER_DRIVER(指定每个驱动的最大连接池数),则每个驱动程序都支持单个连接池,如果应用程序可使用许多驱动程序且环境很少,则这可能会更有效率,因为可能需要较少的比较。SQL_CP_ONE_PER_HENV(指定每个环境的最大连接池数)为每个环境都支持单个连接池,如果应用程序可在许多环境中使用且驱动程序很少,则这可能会更有效率,因为可能需要较少的比较。通过将SQL_ATTR_CONNECTION_POOLING设置为SQL_CP_OFF来禁用连接池。
  2. 用SQLAllocHandle 来分配SQL_HANDLE_ENV环境句柄时,如果开启了连接池,则环境句柄被隐式分配成了共享环境句柄,分配完成后并没有被使用,而是在分配SQL_HANDLE_DBC连接句柄时,传入环境句柄,这样才确定使用了环境句柄。
  3. 分配SQL_HANDLE_DBC连接句柄时,传入的环境句柄以方便创建连接池,驱动管理器会从存在的环境句柄中查找应用传入的环境句柄,如果没有这个环境句柄,则创建一个,引用计数为1(由驱动程序管理器维护);如果找到了,则将这个环境句柄返回给应用程序,并且其引用计数会增加。通过调用SQLConnect或SQLDriverConnect来决定一个连接什么时候被使用。
  4. 调用SQLConnect或SQLDriverConnect来获得一个连接,驱动管理器使用SQLConnect(或SQLDriverConnect的调用中的连接字符串中的关键字)的连接属性设置,在分配连接后设置的连接属性来确定应使用池中的哪个连接。

在调用 SQLConnect 之前由应用程序设置的连接属性与池中连接的连接属性不匹配时,将应用以下规则:

  • 如果在建立连接前必须设置连接属性:

    如果SQL_ATTR_CP_MATCH属性值设为SQL_CP_STRICT_MATCH ,则共用连接中的 SQL_ATTR_PACKET_SIZE 必须与应用程序设置属性相同。

    如果其值设为 SQL_CP_RELAXED_MATCH,则 SQL_ATTR_PACKET_SIZE 的值可能不同。

  • 如果在建立连接之前或之后可以设置连接属性:

    如果应用程序尚未设置连接属性,但已在池中的连接上设置该属性,并且有一个默认值,则会将共用连接中的连接属性设置回默认值,并声明一个匹配项。

    如果没有默认值,则不会将共用连接视为匹配项。

    如果连接属性已由应用程序设置,但尚未对池中的连接进行设置,则池上的连接属性将更改为应用程序设置的,并声明匹配项。

    如果连接属性已由应用程序设置,并且还在池中的连接上设置,但这些值不同,则使用应用程序的连接属性的值并声明匹配项。

  • 如果驱动程序特定的连接属性的值不相同并且 SQL_ATTR_CP_MATCH 设置为 SQL_CP_STRICT_MATCH,则不会使用池中的连接

  1. 连接用完后,调用SQLDisconnect。 连接将返回到连接池,并可供重用。
  2. 示例代码:
int i = 0;
      RETCODE    rc = SQL_SUCCESS;
      SQLHENV         henv;
      SQLHDBC         hdbc;
      SQLHSTMT        hstmt;

      SQLSetEnvAttr(SQL_NULL_HENV, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_ONE_PER_DRIVER, 0);
      SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &henv);
      SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
      while (i<10)
      {
              int num = 0;
              SQLLEN ind = -3;
              SQLCHAR ConnStrIn[MAXBUFLEN] = { 0 };
              strncpy((char*)ConnStrIn, (char*)conn, strlen((char*)conn) + 1);
              if (MAXBUFLEN - strlen((char*)conn) > 40)
              {
                      strncat((char*)ConnStrIn, "CPTimeout=500;CPTimeToLive=0;", 40);
              }

              SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
              SQLDriverConnect(hdbc, NULL, ConnStrIn, SQL_NTS, oscarconnet, MAXBUFLEN, 0, SQL_DRIVER_NOPROMPT);
              SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
              SQLPrepare(hstmt, (SQLCHAR*)"select count(*) from v_sys_sessions;", SQL_NTS);
              SQLBindCol(hstmt, 1, SQL_C_SHORT, &num, 0, &ind);
              SQLExecute(hstmt);
              SQLFetch(hstmt);
              SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
              SQLDisconnect(hdbc);
              SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
              i++;
      }

连接池注意事项

执行以下sql(而不是通过ODBC API)可能会改变连接池的状态并导致意外问题:

  • 更改一个连接中的默认数据库;
  • 用set命令去更改配置选项;
  • 创建临时表和存储过程。

如果这些操作不是通过odbc的接口去操作的,则下一个用户会自动继承上一个用户的这些设置,因为这些设置是针对当前连接的,SQLDisconnect时不会清除这些临时设置。

同时不要期望连接会给你保留某些设置,应用程序应该自行进行设置,并确保使用完成后去移除这些设置。

连接池可以用CPTimeout来配置连接池的超时时间;用CPTimeToLive参数来设置连接池中的连接生命周期。

神通驱动内置连接池

前面讲到的连接池是有驱动管理器来做的,也就是windows下有操作系统提供;Linux有unixodbc提供,但如果要使用连接池,就必须在编译时引用的是驱动管理器提供的动态库。 但由于部分应用场景是直接调用神通数据库的ODBC驱动,不会走数据源管理器的驱动接口,这种情况就没有连接池了,因此神通数据库的ODBC也实现了连接池的功能,实现的逻辑和驱动管理器实现的基本一致。使用步骤保持一致。