最近都在查看MYsql C API文档,也遇到了很多问题,下面来简单的做一个总结。

mysql多线程问题

单线程

  • 一般情况下,单线程连接mysql代码如下:
 /*
single_thread_mysql_client.cpp
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <pthread.h>
#include <unistd.h> #define DBHOST "localhost"
#define DBUSER "pca"
#define DBPASS "pca"
#define DBPORT 3306
#define DBNAME "dxponline"
#define DBSOCK NULL //"/var/lib/mysql/mysql.sock"
#define DBPCNT 0 int main()
{
MYSQL_RES *result;
MYSQL_ROW row;
MYSQL_FIELD *field;
unsigned int num_fields;
unsigned int i;
const char *pStatement = "SHOW TABLES";
mysql_library_init(, NULL, NULL);
MYSQL *mysql = mysql_init(NULL);
unsigned int timeout = ;
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL)
{
printf("connect failed: %s\n", mysql_error(mysql));
mysql_close(mysql);
mysql_library_end();
return ;
} printf("connect succssfully\n"); if ( != mysql_real_query(mysql, pStatement, strlen(pStatement)))
{
printf("query failed: %s\n", mysql_error(mysql));
mysql_close(mysql);
mysql_library_end();
return ;
} result = mysql_store_result(mysql); if (result == NULL)
{
printf("fetch result failed: %s\n", mysql_error(mysql));
mysql_close(mysql);
mysql_library_end();
return ;
} num_fields = mysql_num_fields(result);
printf("numbers of result: %d\n", num_fields); while (NULL != (field = mysql_fetch_field(result)))
{
printf("field name: %s\n", field->name);
} while (NULL != (row = mysql_fetch_row(result)))
{
unsigned long *lengths;
lengths = mysql_fetch_lengths(result); for (i = ; i < num_fields; i++)
{
printf("{%.*s} ", (int) lengths[i], row[i] ? row[i] : "NULL");
} printf("\n");
} mysql_free_result(result);
mysql_close(mysql);
mysql_library_end();
return ;
}
  • 执行

    make single_thread_mysql_client LDFLAGS="-g -O2 -L/usr/lib64/mysql -lmysqlclient -lpthread -lz -lm -lssl -lcrypto -ldl"

    即可获得对应单线程二进制。

多线程

  • 多线程主要需要注意以下几点

    • mysql_library_init 和 mysql_library_end 需要放入主线程;
    • 连接句柄需要多个才能加快并发,而连接句柄由 mysql_init 生成,而 mysql_init跟随机函数 rand 有点相似,第一次需要初始化后才能线程安全,所以需要使用mysql_thread_init 和 mysql_thread_end 两个函数来保证线程安全;
  • 一般多线程连接mysql代码如下
 /*
muti_thread_mysql_client.cpp
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <pthread.h>
#include <unistd.h> #define THREAD_NUM 4
#define DBHOST "localhost"
#define DBUSER "pca"
#define DBPASS "pca"
#define DBPORT 3306
#define DBNAME "dxponline"
#define DBSOCK NULL //"/var/lib/mysql/mysql.sock"
#define DBPCNT 0 typedef struct ThreadArgsST
{
int id;
pthread_t *thread_id;
} ThreadArgs; void *func(void *arg)
{
ThreadArgs *args = (ThreadArgs *)arg;
MYSQL_RES *result;
MYSQL_ROW row;
MYSQL_FIELD *field;
unsigned int num_fields;
unsigned int i;
unsigned int timeout = ;
const char *pStatement = "SHOW TABLES";
mysql_thread_init();
MYSQL *mysql = mysql_init(NULL); if (mysql == NULL)
{
printf("[%ld][%d]mysql init failed: %s\n", *args->thread_id, args->id, mysql_error(mysql));
return (void *);
} mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL)
{
printf("[%ld][%d]connect failed: %s\n", *args->thread_id, args->id, mysql_error(mysql));
mysql_close(mysql);
mysql_thread_end();
return (void *);
} printf("[%ld][%d]connect succssfully\n", *args->thread_id, args->id); if ( != mysql_real_query(mysql, pStatement, strlen(pStatement)))
{
printf("[%ld][%d]query failed: %s\n", *args->thread_id, args->id, mysql_error(mysql));
mysql_close(mysql);
mysql_thread_end();
return (void *);
} result = mysql_store_result(mysql); if (result == NULL)
{
printf("[%ld][%d]fetch result failed: %s\n", *args->thread_id, args->id, mysql_error(mysql));
mysql_close(mysql);
mysql_thread_end();
return (void *);
} num_fields = mysql_num_fields(result);
printf("[%ld][%d]numbers of result: %d\n", *args->thread_id, args->id, num_fields); while (NULL != (field = mysql_fetch_field(result)))
{
printf("[%ld][%d]field name: %s\n", *args->thread_id, args->id, field->name);
} while (NULL != (row = mysql_fetch_row(result)))
{
unsigned long *lengths;
lengths = mysql_fetch_lengths(result); for (i = ; i < num_fields; i++)
{
printf("[%ld][%d]{%.*s} ", *args->thread_id, args->id, (int) lengths[i], row[i] ? row[i] : "NULL");
} printf("\n");
} mysql_free_result(result);
mysql_close(mysql);
mysql_thread_end();
return (void *);
} int main(int argc, char *argv[])
{
int thread_num; if (argc == )
{
thread_num = atoi(argv[]);
}
else
{
thread_num = THREAD_NUM;
} mysql_library_init(, NULL, NULL);
printf("argc: %d and thread_num: %d\n", argc, thread_num); do
{
pthread_t *pTh = new pthread_t[thread_num];
ThreadArgs *pArgs = new ThreadArgs[thread_num];
int i; for (i = ; i < thread_num; i ++)
{
pArgs[i].id = i;
pArgs[i].thread_id = &pTh[i]; if ( != pthread_create(&pTh[i], NULL, func, &pArgs[i]))
{
printf("pthread_create failed\n");
continue;
}
} for (i = ; i < thread_num; i ++)
{
pthread_join(pTh[i], NULL);
} delete[] pTh;
delete[] pArgs;
}
while (); mysql_library_end();
return ;
}
  • 执行

    make muti_thread_mysql_client LDFLAGS="-g -O2 -L/usr/lib64/mysql -lmysqlclient -lpthread -lz -lm -lssl -lcrypto -ldl"

    即可获得对应单线程二进制。

  • 连接数与连接句柄是一一对应关系,故一般使用长连接,所以需要连接池,所以上面的代码可以有优化的空间,代码见:

 /*
muti_thread_mysql_client_pool.cpp
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <pthread.h>
#include <unistd.h>
#include <queue>
#include <string> #define THREAD_NUM 4
#define DBHOST "localhost"
#define DBUSER "pca"
#define DBPASS "pca"
#define DBPORT 3306
#define DBNAME "dxponline"
#define DBSOCK NULL //"/var/lib/mysql/mysql.sock"
#define DBPCNT 0 using namespace std; class CBlockQueue;
typedef struct ThreadArgsST
{
int id;
pthread_t *thread_id;
CBlockQueue *pQueue;
} ThreadArgs; class CMutex
{
public:
CMutex()
{
pthread_mutex_init(&_mutex, NULL);
}
~CMutex()
{
pthread_mutex_destroy(&_mutex);
} int32_t lock()
{
return pthread_mutex_lock(&_mutex);
} int32_t unlock()
{
return pthread_mutex_unlock(&_mutex);
} int32_t trylock()
{
return pthread_mutex_trylock(&_mutex);
} private:
pthread_mutex_t _mutex;
}; class CGlobalFunction
{
public:
static MYSQL *connect()
{
unsigned int timeout = ;
mysql_thread_init();
MYSQL *mysql = mysql_init(NULL); if (mysql == NULL)
{
printf("mysql init failed: %s\n", mysql_error(mysql));
return NULL;
} mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL)
{
printf("connect failed: %s\n", mysql_error(mysql));
mysql_close(mysql);
mysql_thread_end();
return NULL;
} printf("connect succssfully\n");
return mysql;
}
}; class CBlockQueue : public CMutex
{
public:
CBlockQueue() : _size()
{
}
~CBlockQueue()
{
}
void set_size(int size)
{
_size = size;
}
int size()
{
this->lock();
int size = q.size();
this->unlock();
return size;
}
bool push(void *m)
{
this->lock();
// TODO
/*
if (q.size() > _size)
{
this->unlock();
fprintf(stderr, "[QUEUE_IS_FULL]queue size over limit from push: %d\n", _size);
return false;
}
*/
q.push(m);
this->unlock();
return true;
} void *pop()
{
this->lock(); if (q.empty())
{
this->unlock();
fprintf(stderr, "[QUEUE_IS_EMPTY]queue is no item from pop");
return NULL;
} void *m = q.front();
q.pop();
this->unlock();
return m;
} private:
queue q;
int _size;
}; void *func(void *arg)
{
ThreadArgs *args = (ThreadArgs *)arg;
MYSQL_RES *result;
MYSQL_ROW row;
MYSQL_FIELD *field;
bool pushed = true;
unsigned int num_fields;
unsigned int i;
const char *pStatement = "SHOW TABLES";
MYSQL *db = (MYSQL *)args->pQueue->pop(); if (db == NULL)
{
db = CGlobalFunction::connect(); if (db == NULL)
{
printf("[%ld][%d]mysql connect failed\n", *args->thread_id, args->id);
return (void *);
}
} if ( != mysql_real_query(db, pStatement, strlen(pStatement)))
{
printf("[%ld][%d]query failed: %s\n", *args->thread_id, args->id, mysql_error(db));
args->pQueue->push(db);
return (void *);
} result = mysql_store_result(db); if (result == NULL)
{
printf("[%ld][%d]fetch result failed: %s\n", *args->thread_id, args->id, mysql_error(db));
args->pQueue->push(db);
return (void *);
} num_fields = mysql_num_fields(result);
printf("[%ld][%d]numbers of result: %d\n", *args->thread_id, args->id, num_fields); while (NULL != (field = mysql_fetch_field(result)))
{
printf("[%ld][%d]field name: %s\n", *args->thread_id, args->id, field->name);
} while (NULL != (row = mysql_fetch_row(result)))
{
unsigned long *lengths;
lengths = mysql_fetch_lengths(result); for (i = ; i < num_fields; i++)
{
printf("[%ld][%d]{%.*s} ", *args->thread_id, args->id, (int) lengths[i], row[i] ? row[i] : "NULL");
} printf("\n");
} mysql_free_result(result);
args->pQueue->push(db);
return (void *);
} int main(int argc, char *argv[])
{
CBlockQueue queue;
int thread_num; if (argc == )
{
thread_num = atoi(argv[]);
}
else
{
thread_num = THREAD_NUM;
} mysql_library_init(, NULL, NULL);
printf("argc: %d and thread_num: %d\n", argc, thread_num); do
{
int i;
pthread_t *pTh = new pthread_t[thread_num];
ThreadArgs *pArgs = new ThreadArgs[thread_num]; for (i = ; i < thread_num; i ++)
{
pArgs[i].id = i;
pArgs[i].thread_id = &pTh[i];
pArgs[i].pQueue = &queue; if ( != pthread_create(&pTh[i], NULL, func, &pArgs[i]))
{
printf("pthread_create failed\n");
continue;
}
} for (i = ; i < thread_num; i ++)
{
pthread_join(pTh[i], NULL);
} delete[] pTh;
delete[] pArgs;
int qsize = queue.size(); for (i = ; i < qsize; i ++)
{
MYSQL *db = (MYSQL *)queue.pop(); if (NULL != db)
{
mysql_close(db);
mysql_thread_end();
}
}
}
while (); mysql_library_end();
return ;
}
  • 执行

    make muti_thread_mysql_client_pool LDFLAGS="-g -O2 -L/usr/lib64/mysql -lmysqlclient -lpthread -lz -lm -lssl -lcrypto -ldl"

    即可获得对应单线程二进制。

  • 上述代码就是利用队列来保持mysql连接,达到优化连接数。

总结

  • mysql连接与多线程处理不好,可能会造成很多问题,如

    • *MySQL Connection failed (#2058): This handle is already connected. Use a separate handle for each connection.*
    • Error in my_thread_global_end(): 1 threads didn't exit
    • 甚至出现coredump
  • 关于多线程连接mysql优化的思想,其实可以扩展到其他连接,如HTTP、Socket等连接中;

测试多线程连接Mysql:

 int main()
{
mysql_library_init(,NULL,NULL);
pthread_t producer;
pthread_t consumer_1;
pthread_t consumer_2;
//produce_fun不涉及数据库链接,不贴出来
pthread_create(&producer,NULL,produce_fun,NULL);
pthread_create(&consumer_1,NULL,consume_fun,NULL);
pthread_create(&consumer_2,NULL,consume_fun,NULL);
//下面的三句非常重要,main线程等待其他三个线程的结束,避免main线程执行到mysql_library_end时退出,而
//其他三个线程仍然在运行并报错的情形
pthread_join(producer,NULL);
pthread_join(consumer_1,NULL);
pthread_join(consumer_2,NULL);
mysql_library_end();
return ;
} void addRecord_d(MYSQL *ptr_db,const char *t_name,int item){
char query_buffer[];
sprintf(query_buffer,"insert into %s values(0,%d)",t_name,item);
int ret=mysql_query(ptr_db,query_buffer);
if(ret){
fprintf(stderr,"%s%s\n","cannot add record to ",t_name);
return;
} unsigned long long update_id=mysql_insert_id(ptr_db);
printf("add record (%llu,%d) ok.",update_id,item);
} void * consume_fun(void *arg){
MYSQL db;
MYSQL *ptr_db=mysql_init(&db);
mysql_real_connect();
//蓝色部分可以改为其他任何带操作数据库语句的代码
//procedure
while(){
printf("consumer...");
int item=consume(&p);
addRecord_d(ptr_db,"test",item);
}
mysql_thread_end();
pthread_exit(NULL);
}

C/C++中调用api设置mysql连接的编码方式

mysql在C/C++中调用api设置连接mysql的编码方式有以下几种方法:

1. mysqli_set_charset
调用示例:

  1. ret = mysql_set_character_set(mysql, "utf8");

说明:
推荐使用的设置方法,与mysql的连接断开自动重连后仍能保持设置的编码格式,并且影响mysql_real_escape_string函数功能,使mysql_real_escape_string函数使用设置的编码格式转义字符串。
但该函数在mysql5.0.5版本以后才支持,故版本太低者...。
2. 执行sql语句:SET NAMES
调用示例:

  1. ret = mysql_real_query(mysql, "SET NAMES UTF8;",
  2. (unsigned long) strlen ("SET NAMES UTF8;"));

说明:
使用sql语句执行,只能影响当前与数据库的连接,断开自动重连后编码格式会重置为默认的配置。
3. 设置MYSQL_SET_CHARSET_NAME属性
调用示例:

  1. ret = mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8");

说明:
跟mysql_set_character_set类似,断开自动重连后仍能保持设置的编码格式,只是不会影响到mysql_real_escape_string函数。
并且在mysql5.0.5版本都可以使用,低版本可以使用该方法代替。
需要特别说明的是只有在调用mysql_real_connect连接数据库之前修改该属性才会生效。

mysql控制自动重连行为

当向mysql server发送statement时,mysql客户端发现连接失效后会自动尝试重新连接server。如果"自动重连"状态是enabled,客户端尝试连接server,并在连接成功后重新发送statement.

"自动重连“默认是disabled的。

如果应用程序需要知道连接是否可用(可以退出程序或显示相应的提示信息),确认“自动重连”是disabled。可以通过调用包含MYSQL_OPT_RECONNECT参数的mysql_options()函数以确认"自动重连"是否disabled:

my_bool reconnect = 0;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);

如果连接已经断开,mysql_ping()依赖“自动重连”的状态。如果“自动重连”是enabled,mysql_ping()会重新获取连接。否则,会返回错误。

有些客户端程序可能提供了控制自动重连的功能。比如,mysql默认是允许重连的,但是使用 --skip-reconnect 选项将关闭这个行为。

如果发生了自动重连(比如,调用了mysql_ping()),自动重连是透明的。为了检查是否发生了重连,在调用mysql_ping()函数前,先调用mysql_thread_id()函数获取原始的连接id,然后调用mysql_ping(),在调用 mysql_thread_id()函数,对比两次 mysql_thread_id()的结果是否变化了。

“自动重连”很方便,因为你不需要再实现自己重连代码,但是一旦发生了自动重连,connection的一些状态在server端就会被重置,而你的应用程序得不到这些变化的通知。

下面列出连接相关的状态变化:

  • 任何未提交的事务将会回滚并且连接autocommit属性将会重置

  • 事务中的锁将会被释放。

  • 所有的临时表将会关闭(并释放)。

  • Session中的变量会重新初始化为系统默认值,包括在statements中显式声明的变量,比如SET NAMES

  • 用户自定义的变量将会丢失。

  • Prepared statements将释放。

  • HANDLER 变量将会关闭。

  • LAST_INSERT_ID()的值将会重置为0.

  • 通过GET_LOCK()获取的锁将会释放。

如果连接失效,可能connection的session在server端依然在运行,当server没有检测到客户端失去连接。在这种情况下,原来connection的锁依然属于那个session,你可以调用mysql_kill()函数kill掉它。

Linux下C语言连接MySQL找不到mysql.h解决办法

昨天是把MySQL装上去了,今天尝试用C语言访问连接MySQL,发现使用编译指令:gcc -I/usr/include/mysql connect.c -lmysqlclient -o connect之后,提示找不到“mysql.h”文件,T_T|||。

原因其实很简单了,昨天我只装了server和client两个包,对于平常的使用,这已经足够了,但对于要在Linux下进行数据库的开发,则还需要安装devel开发包,并且该开发包的版本应该和我之前所安装的server和client包版本一致,否则会发生冲突而导致无法安装。

server和client包的安装见上一篇日志:Linux(Fedora 9)卸载自带的MySQL并安装MySQL的详细步骤

好了,我们来安装devel包,注意版本要保持一致:

  1. [root@localhost install]# rpm -ivh MySQL-devel-community-5.1.46-1.rhel5.i386.rpm
  2. Preparing...                ########################################### [100%]
  3. 1:MySQL-devel-community  ########################################### [100%]

这次为了避免再出现问题,我把共享组件包shared也装上去了,哈哈:

  1. [root@localhost install]# rpm -ivh MySQL-shared-community-5.1.46-1.rhel5.i386.rpm
  2. Preparing...                ########################################### [100%]
  3. 1:MySQL-shared-community ########################################### [100%]

装完之后,再编译就通过了,此时的“mysql.h”已经出现在了/usr/include/mysql目录下面。Happy~

undefined reference to `mysql_init'解决办法

写了一个很简单的测试数据库连接的程序conn.c如下:

 #include <stdio.h>
#include <stdlib.h>
#include "/usr/local/mysql/include/mysql/mysql.h"
#include <string.h>
int main(int argc, char *argv[])
{
MYSQL my_connection;
mysql_init(&my_connection);
if (mysql_real_connect(&my_connection, "localhost", "root", "","mysql",,NULL,CLIENT_FOUND_ROWS))
{
printf("Connection success\n");
mysql_close(&my_connection);
}
else
{
fprintf(stderr, "Connection failed\n");
if (mysql_errno(&my_connection))
{
fprintf(stderr, "Connection error %d: %s\n",mysql_errno(&my_connection),mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}

gcc编译:gcc -o conn conn.c出现如下错误:
/tmp/ccY0JTdh.o(.text+0x1e): In function `main':
: undefined reference to `mysql_init'
/tmp/ccY0JTdh.o(.text+0x47): In function `main':
: undefined reference to `mysql_real_connect'
/tmp/ccY0JTdh.o(.text+0x6d): In function `main':
: undefined reference to `mysql_close'
/tmp/ccY0JTdh.o(.text+0x97): In function `main':
: undefined reference to `mysql_errno'
/tmp/ccY0JTdh.o(.text+0xad): In function `main':
: undefined reference to `mysql_error'
/tmp/ccY0JTdh.o(.text+0xc0): In function `main':
: undefined reference to `mysql_errno'
collect2: ld returned 1 exit status
出现该错误的原因是因为编译器找不到mysql_init,mysql_close等的具体实现.虽然我们包括了正确的头文件,但是 我们在编译的时候还是要连接确定的库.对于一些常用的函数的实现,gcc编译器会自动去连接一些常用库,这样我们就没有必要自己去指定了,如:printf函数.在本程序中要通过-L选项包含库文件的路径:
gcc -o conn conn.c -L /usr/local/mysql/lib/*.a -lz
通过,
用*.a将库全部包含进来拉,其实只要包含mysqlclient.a就可以. -lz什么意思我也不清楚了

undefined reference to 'dlopen';undefined reference to 'dlclose';undefined reference to 'dlerror'等问题

在linux下,编译链接的时候,经常会遇到这样一个问题,undefined reference to.....,引起这个问题的原因在于在链接的时候缺少选项。下面举几个例子,并给出解决办法。

1、undefined reference to `dlerror'

 undefined reference to `dlopen'

 undefined reference to `dlerror';;解决方法:在makefile的 ldflags 后面把选项 -ldl添加上即可

2、undefined reference to `main';;解决方法:在makefile的 ldflags 后面添加 -nostartfiles 选项

3、undefined reference to `pthread_setspecific'

   undefined reference to `pthread_key_delete'

     undefined reference to `pthread_key_create'::解决办法:在makefile的LDFLAGS后面添加 -lpthread 选项。

最后::如果undefined reference to后面的内容是在自己的文件中声明或定义的东西,就不能用这种方法来解决了。这时就需要检查一下自己的makefile涉及到源文件、头文件的地方是否出错了,也有可能是其他的原因

最新文章

  1. mysql 定时任务
  2. iOS_屏幕截图
  3. 在UTF8(linux)下,逆置汉字字符串
  4. 使用Codeblock搭建Windows下Objec-c学习环境
  5. 【node.js】安装express后,&#39;express&#39; 不是内部或外部命令的问题
  6. JSP的隐式对象
  7. 图形化Cisco设备管理实践(附安装配置视频)
  8. Linux Bash Shell学习笔记
  9. [工具] slf4j-api、slf4j-log4j12以及log4j之间的关系
  10. mysql inner join,full outer join,left join,right jion
  11. applicationContext.xml 配置(扫描)
  12. CentOS6.5 添加epel源
  13. tomcat之性能优化
  14. 用雷达统计成绩单、numpy、matplotlib的使用
  15. Codeforces1036F Relatively Prime Powers 【容斥原理】
  16. Lock接口
  17. 基于Python的频谱分析(一)
  18. Mysql在linux下载、安装详情,附带mysql安装包路径
  19. Axure 万年历(日期选择下拉文本框)
  20. angularjs笔记《二》

热门文章

  1. css动画和js动画的差异
  2. 使用http-server创建本地服务
  3. MYSQL 中的 int(11) 到底代表什么意思?
  4. 岭回归和Lasso回归以及norm1和norm2
  5. VS2010生成安装包制作步骤 (转)
  6. NET设计模式 第二部分 行为型模式(15):模版方法模式(Template Method)
  7. Go语言加解密--AES简单实践
  8. 部署openresty遇到的一些坑
  9. 如何写出健壮的Java代码
  10. android利用provider查询同一个数据库中没有对外暴露出来的表