unixODBC使用经验分享

unixODBC(http://www.unixodbc.org)和iODBC(http://www.iodbc.org)是UNIX系统上两个开源的ODBC实现。关于二者的比较,有兴趣的朋友可以参考stackoverflow上的这篇帖子。我没有用过iODBC,所以不好评论孰优孰劣。但是从stackoverflow上关于两个项目的标签统计来看,应该是unixODBC用的更广泛一些。在距离上一版本发布两年以后,今年10月,unixODBC发布了2.3.2版本,基本上就是修正了一些bugs(其中有3个bug是我发现并提出修改意见的,偷笑),没有新的feature引入。我从2011年开始在项目中使用unixODBC,2012年中旬项目正式上线,到现在已经运行了1年半时间。总的来说unixODBC还是很稳定的,基本连个错误都没有。在这篇文章中,我把我使用unixODBC的一点经验分享出来,希望能给需要的朋友一些帮助。


(1)源码,编译和安装

在unixODBC的网站上提供了最新版本的源码下载,如果想下载之前的发布版本,可以访问这个网站:ftp://ftp.unixodbc.org/pub/unixODBC/。个人感觉,unixODBC的代码风格还是很清晰的,前后也比较一致(unixODBC里面也包含了一些其它开源的代码,像libltdl,而这些开源代码的风格和unixODBC的风格是不一致的)。只是有些函数有些太长了(像SQLConnect函数就有600多行),似乎不符合KISS(Keep It Small and Simple)原则。

编译unixODBC很简单:

./configure;

make;

make install

我的很多同事都说在Solaris环境下编译源代码是很痛苦的事情,但是编译unixODBC却总是一气呵成,连个错误都没有。

此外,由于默认编译unixODBC是优化的,所以我在实验室里使用时一般都是禁止优化的(./configure CFLAGS='-g -O0 '),这样debug时也比较方便,不会出现代码和函数对不上的问题。只有到了生产环境,才把优化打开。


(2)使用连接池

从2.0.0版本起,uinixODBC引入了连接池。unixODBC的项目负责人Nick Gorham这篇文章里详细介绍了连接池。引入连接池,可以使对数据库连接的使用更高效。下面我就对连接池的原理做个分析:

首先,uinixODBC引入两个全局变量:

/*
 * connection pooling stuff
 */

CPOOL *pool_head = NULL;
int pooling_enabled = 0;
其中pool_head指向连接池链表的首节点,而pooling_enabled则表示是否使用连接池。

其次,在SQLDisconnect函数中,可以看到:

/*
 * is it a pooled connection, or can it go back 
 */

if ( connection -> pooled_connection )
{
    __clean_stmt_from_dbc( connection );
    __clean_desc_from_dbc( connection );

    return_to_pool( connection );

    ......

    return function_return( SQL_HANDLE_DBC, connection, SQL_SUCCESS );
}
else if ( pooling_enabled && connection -> pooling_timeout > 0 ) 
{
    __clean_stmt_from_dbc( connection );
    __clean_desc_from_dbc( connection );

    return_to_pool( connection );

    ......

    return function_return( SQL_HANDLE_DBC, connection, SQL_SUCCESS );
}

如果使用了连接池的话,当断开连接时,连接是回到了连接池里,也就是return_to_pool这个函数。

最后,在SQLConnect函数中,可以看到:

if ( pooling_enabled && search_for_pool( connection, 
                                            server_name, name_length1,
                                            user_name, name_length2,
                                            authentication, name_length3,
                                            NULL, 0 ))
{
    ret_from_connect = SQL_SUCCESS;

    .....

    connection -> state = STATE_C4;

    return function_return( SQL_HANDLE_DBC, connection, ret_from_connect );
}
如果连接数据库时可以在连接池中找到可用的连接(search_for_pool),函数就直接返回,不用再做连接的操作了。


(3)在日志中打印线程

unixODBC的日志输出是通过dm_log_write函数:

if ( !log_info.program_name )
{
    uo_fprintf( fp, "[ODBC][%s]%s[%s][%d]%s\n", __get_pid((SQLCHAR*) tmp ), 
			tstamp_str,
            function_name, line, message );
}
else
{
    uo_fprintf( fp, "[%s][%s]%s[%s][%d]%s\n", log_info.program_name,
        __get_pid((SQLCHAR*) tmp ), 
		tstamp_str,
		function_name, line, message );
}

可以看到,日志输出包含进程名,时间戳,函数名,行号,和消息。但是现在的程序基本是多线程的,所以当有多个线程同时访问数据库时,从日志就很难区分开哪个线程到底做了什么,因此通常我会在日志中增加对线程的打印。因为我们的程序运行在Solaris系统,而在Solaris上,pthread_t类型是个整数,标示当前的线程号,所以下面的改动就可以增加对线程号的输出:

if ( !log_info.program_name )
{
    uo_fprintf( fp, "[ODBC][%s][%d]%s[%s][%d]%s\n", __get_pid((SQLCHAR*) tmp ), pthread_self(),
			tstamp_str,
            function_name, line, message );
}
else
{
    uo_fprintf( fp, "[%s][%s][%d]%s[%s][%d]%s\n", log_info.program_name,
        __get_pid((SQLCHAR*) tmp ), 
		pthread_self(),
		tstamp_str,
		function_name, line, message );
}

由于pthread_t类型在不同的系统定义不一样,所以如果同一份unixODBC代码需要运行在不同系统上,就需要实现定制化:

#ifdef __linux__
......
#endif

#ifdef __sun
......
#endif
关于pthread_t的打印,可以参考stackoverflow上的这篇帖子

以上就是我使用unixODBC的一点经验分享,希望能给需要的朋友一点帮助。如果大家有其它好的经验,也希望能分享出来。

发布了67 篇原创文章 · 获赞 1 · 访问量 11万+
展开阅读全文

使用Log4cxx日志输出至MySQL, 无法找到驱动错误

06-26

Hi, guys! 用log4cxx日志框架对log内容输出至数据库(MySQL)一直未成功,不知道是什么步骤出现了问题。希望大伙们能帮帮忙,或者有什么更好的日志系统可以推荐,谢谢各位了。 ^_^ --- **运行环境**: Ubuntu 12.04 32bit + Eclipse CDT **log4cxx** : apache-log4cxx-0.10.0 log4cxx支持两种ODBC选项(iODBC/unixODBC),这里选择了unixODBC形式进行管理 通过添加选项 ./configure --with-ODBC=unixODBC,编译通过. 之后对unixODBC的驱动和数据源进行配置,可以通过终端 _isql log4cxx_ 成功建立连接 但是代码中使用Log4cxx进行输出的时候一直报下面的错误 ``` log4cxx: Failed to connect to database. - [unixODBC][Driver Manager]Data source name not found, and no default driver specified ``` ``` 怀疑一: ``` > Ecplise中是否没有加环境变量; 查阅ODBC资料,在Eclipse中加入ODBCSYSINI,ODBCINSTINI,ODBCINI变量后,还是没有效果. ``` 怀疑二: ``` > log4cxx配置写的不对;这个有没有什么官方的写法,晕呐! --- 配置内容: **/etc/odbcinst.ini** ``` bash [MySQL] Description = MySQL Driver = /usr/lib/i386-linux-gnu/odbc/libmyodbc.so Setup = /usr/lib/i386-linux-gnu/odbc/libodbcmyS.so FileUsage = 1 ``` **/etc/odbc.ini** ``` c [log4cxx] Description = The Database for Logging System Trace = on TraceFile = stderr Driver = MySQL SERVER = localhost PORT = 3306 DATABASE = log USER = root PASSWORD = 56789 ``` **log4cxx.xml** ``` xml <appender name="OdbcMysqlAppender" class="org.apache.log4j.odbc.ODBCAppender"> <param name="URL" value="Driver={MySQL};Server=localhost;Database=log;User=root;Password=56789;" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="INSERT INTO logs (message) VALUES ('%d - %c - %p - %m')" /> </layout> </appender> <root> <priority value="all" /> <appender-ref ref="OdbcMysqlAppender" /> </root> ``` --- # 进展: ``` _Time: 20150702_ ``` 查看Log4cxx源码,采用数据库方式记录Log,使用的是ODBCAppender ``` src/main/cpp/odbcappender.cpp ``` 作者原先的代码采用的是 **SQLxxxW()** 进行DriverConnect和SQLExecDirect操作,根据ODBC的描述,有 **W** 修饰是采用Unicode编码方式。总之我一直陷在这,一直报找不到数据源。于是乎我将 ``` SQLxxxW() ``` 修改成 ``` SQLxxx() ``` 方式的调用,同时将原先``` SQLWCHAR ``` 修改成对应 ``` SQLCHAR ```,即采用非宽字节的操作方式,成功的找到数据源,并且可以进行调用。 不过这就导致了中文的问题了. **疑惑** 在odbcinst.ini中配置的Driver确实是libmyodbc5w.so,即驱动使用Unicode方式的呀! (*与之相对应的是libmyodbc5a.so*). 至今还不知道自己是哪里出错,作者不可能毫无依据的就采用 SQLxxxW() 形式进行ODBC连接的吧. --- 我在ODBCAppender.cpp中添加了 #define SQL_WCHART_CONVERT,否则make的时候过不去, 即 ``` c++ #if !defined(LOG4CXX) #define LOG4CXX 1 #endif #include <log4cxx/private/log4cxx_private.h> #if LOG4CXX_HAVE_ODBC #if defined(WIN32) || defined(_WIN32) #include <windows.h> #endif #define SQL_WCHART_CONVERT #include <sqlext.h> #endif ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览