在编写 MySQL 客户端程序时,最常见的就是如何连接以及关闭 MySQL,这里需要注意,如果使用不当将会造成内存泄漏。
这里,简单介绍 MySQL 中如何通过 C 进行编程。
简介
单线程环境,一般使用 -lmysqlclient
链接 MySQL 客户库,其中 mysql_init()
会自动调用 my_library_init()
初始化 MySQL 库,所以不需要,需要注意的是这两个函数都是非线程安全的。
示例代码如下。
void wrap_mysql_connect(MYSQL *mysql_conn)
{
my_init();
if (NULL == mysql_init(mysql_conn)) {
write_log("Colud not init mysql.");
return;
}
if (!mysql_real_connect(mysql_conn, g_mysql->host, g_mysql->username,
g_mysql->passwd, g_mysql->dbname, g_mysql->port, NULL, 0)) {
write_log("Mysql connect error : .", mysql_error(mysql_conn));
}
}
void wrap_mysql_close(MYSQL *mysql_conn)
{
mysql_close(mysql_conn);
mysql_conn = NULL;
}
多线程环境,一般调用 -lmysqlclient_r
安全类库,需要在各个线程中调用 mysql_library_init()
mysql_library_end()
来分配和释放 MySQL 资源,或者增加线程锁保护资源,否则会造成内存泄漏。
示例代码如下。
void wrap_mysql_connect(MYSQL *mysql_conn)
{
my_init();
if (NULL == mysql_init(mysql_conn)) {
write_log("Colud not init mysql.");
return;
}
if (mysql_library_init(0, NULL, NULL)) {
write_log("Could not initialize mysql library.");
return;
}
if (!mysql_real_connect(mysql_conn, g_mysql->host, g_mysql->username,
g_mysql->passwd, g_mysql->dbname, g_mysql->port, NULL, 0)) {
write_log("Mysql connect error : .", mysql_error(mysql_conn));
}
}
void pa_mysql_close(MYSQL *mysql_conn)
{
mysql_close(mysql_conn);
mysql_conn = NULL;
mysql_library_end();
}
在使用 MySQL 多线程时,需要注意如下的问题:
mysql_library_init()
和mysql_library_end()
必须要在同一个线程中,否则会出现Error in my_thread_global_end(): 1 threads didn't exit
的报错。- 当通过
mysql_ping()
检测到链接断开后,直接调用mysql_real_connect()
重新链接会导致本次链接的部分内存没有释放。 - 如果设置成自动重新链接,那么在其它线程通过
mysql_ping()
重新链接时,同样会存在问题。
简单示例
#include <mysql.h>
#include <my_global.h>
int main(int argc, char **argv)
{
printf("MySQL client version: %s\n", mysql_get_client_info());
MYSQL *conn = mysql_init(NULL);
if (conn == NULL) {
printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
exit(1);
}
/* arguments: host user password database port unix_socket client_flag */
if (mysql_real_connect(conn, "localhost", "root", "YOUR_PASSWORD", NULL, 0, NULL, 0) == NULL) {
printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
mysql_close(conn);
exit(1);
}
if (mysql_ping(conn)) {
printf("Error: lost connection\n");
return 1;
}
if (mysql_query(conn, "DROP DATABASE IF EXISTS foobardb")) {
printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
mysql_close(conn);
exit(1);
}
if (mysql_query(conn, "SHOW DATABASES")) {
printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
mysql_close(conn);
exit(1);
}
MYSQL_RES *result = mysql_store_result(conn);
int num_fields = mysql_num_fields(result), i;
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
for(i = 0; i < num_fields; i++)
printf("%s ", row[i] ? row[i] : "NULL");
printf("\n");
}
mysql_free_result(result);
mysql_close(conn);
return 0;
}
然后通过如下方式进行编译。
hello: main.c
gcc -o hello main.c `mysql_config --cflags --libs`
MySQL-Python
Python 中提供了统一的数据库接口,也就是 DB-API,在此之前不同类型的数据库实现的接口各不相同,当更换不同数据库时需要做大量的修改,而 DB-API 实际上提供了一个标准的数据库访问接口。
该规范定义了线程安全级别、连接接口、异常、游标等等。
MySQLdb 安装
MySQLdb 是用于 Python 链接 MySQL 数据库的模块,实现了 Python 数据库 API 规范 V2.0,基于 MySQL C API 建立。
----- 可以通过PIP安装
# apt-get install python-pip
# pip install --proxy=http://your-proxy-ip:port mysql-python
----- 或者使用easy_install方式安装
# easy_install mysql-python
----- 也可以通过源码安装
# unzip MySQL-python-1.2.5.zip && cd
# python setup.py install
最简单的还是通过类似 YUM
APT-GET
的包管理工具安装,否则会需要安装 libmysqlclient-dev
之类的依赖库,详细内容可以参考 PyPi MySQL-Python 以及 MySQL Repositories 。
MySQLdb 底层会调用 _mysql
模块,这是一个 C 编写的模块,实际上就是对 MySQL-CAPI 的封装,安装后包保存在 /usr/lib64/python2.6/site-packages/
目录下。
异常处理
MySQLdb 会有两种异常,包括了 MySQLdb.Warning
、MySQLdb.Error
,不太清楚 Warning 是作什么的,不是 SQL 返回的 Warning 。
如果在只捕获 Warning 时,可能会发现程序并未捕获警告,这是因为 Python 程序 try...except...
默认只能捕获到错误异常,可以通过如下方式更改 Warning 的级别,让 Python 程序认为这是一个错误,从而捕捉到它。
from warnings import filterwarnings
filterwarnings('error', category = MySQLdb.Warning)
try:
conn = MySQLdb.connect(... ...)
... ...
except MySQLdb.Warning, w:
print w???
except MySQLdb.Error, e:
print e[0], e[1]