linux基础——linux下数据库连接池的原理及编程实现

本文介绍了一种在Linux环境下实现MySQL数据库连接池的方法。详细解释了连接池的工作原理及为何能够提高数据库操作性能,并提供了具体的C语言实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、数据库连接池简介
对于一个简单的数据库应用,如果对数据库的访问不是很频繁,通常的做法是创建一个连接,用完就关闭,这样做不会有明显的性能上的开销,但是对于一个副复杂的数据应用,建立一个数据库连接需要消耗大量系统资源,频繁的创建数据库连接会大大的削弱应用性能,因此考虑到性能的问题,并不是每个用户都创建并独占一个数据库连接,数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个,使用完不会销毁而是放回连接池。这项技术能明显提高对数据库操作的性能。

二、linux下的mysql函数
1.初始化一个连接句柄
  1. MYSQL *mysql_init(MYSQL *mysql);//mysql是NULL指针,该函数将分配、初始化、并返回新对象。否则,将初始化对象,并返回对象的地址。  
2.建立物理连接
  1. MYSQL *mysql_real_connect(MYSQL *connection,const char *server_host,const char *sql_user_name,const char *sql_password,const char *db_name,unsigned int port_number,const char *unix_socket_name,unsigned int flags);//connection参数为通过mysql_init函数创建的句柄,port_number和*unix_socket_name的值分别为0和NULL即可。如果连接成功,返回MYSQL*连接句柄(和第一个参数相同)。如果连接失败,返回NULL。  
3.断开连接
  1. void mysql_close(MYSQL *connection);//断开指定的链接。  
4.选项设置
  1. int mysql_options(MYSQL *mysql, enum mysql_option option, const void *arg);//可用于设置额外的连接选项,并影响连接的行为。可多次调用该函数来设置数个选项。应在mysql_init()之后、以及mysql_connect()或mysql_real_connect()之前调用mysql_options()。  
  2. 主要选项:  
  3. MYSQL_OPT_RECONNECT //显式方式设置再连接行为的方法。  
  4. MYSQL_OPT_CONNECT_TIMEOUT //以秒为单位的连接超时。  
5. 错误处理
  1. unsigned int mysql_errno(MYSQL *connection);  
  2. eg.if (mysql_errno(&my_connection))  
  3. {  
  4.     fprintf(stderr, “Connection error %d: %s/n”, mysql_errno(&my_connection),mysql_error(&my_connection));  
  5. }  
6.执行SQL命令
  1. int mysql_query(MYSQL *connection, const char *query);//该函数用来执行SQL命令,如果执行成功则返回0。  
7.数据控制
  1. MYSQL_RES *mysql_store_result(MYSQL *connection);//该函数成功时返回一个指向返回集的指针,否则返回NULL。  
  2. my_ulonglong mysql_num_rows(MYSQL_RES *result);//该函数获取返回集中的行数,若为0表示没有返回行。  
  3. MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);//该函数从mysql_store_result返回集中取出一行,当没有数据或者出差时返回NULL  
  4. my_ulonglong mysql_num_rows(MYSQL_RES *result);//该函数获取返回集中的行数,若为0表示没有返回行。  
8. 数据处理
  1. unsigned int mysql_field_count(MYSQL *connection);//该函数获取返回集中数据的列数。此外,我们还可以将该函数用于其它情况,比如说确定mysql_store_result函数失败的原因;如果mysql_store_result返回NULL,而mysql_filed_count返回了一个大于0的数,说明是检索错误,但如果mysql_field_count返回0,则表明为存储错误。  
  2. MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);//该函数可获得与列字段相关信息的结构指针。  

三、实现
以下实现了一个简单的数据库连接池
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6.   
  7. #define IP_LEN 15  
  8. #define PORT_LEN 8  
  9. #define DBNAME_LEN 64  
  10. #define DBUSER_LEN 64  
  11. #define PASSWD_LEN 64  
  12. #define POOL_NUMBER 5  
  13. #define LOG_STR_BUF_LEN 64  
  14.   
  15. //连接节点  
  16. typedef struct _sql_node  
  17. {  
  18.         MYSQL fd;//mysql对象文件描述符  
  19.         MYSQL *mysql_sock;//连接句柄  
  20.         pthread_mutex_t mutex;//互斥锁  
  21.         char used;//连接使用标志  
  22.         int index;//下标  
  23.         enum{DB_DISCONN, DB_CONN} sql_state;//当前节点与数据库的连接状态  
  24. }sql_node;  
  25.   
  26. //数据库连接池  
  27. typedef struct _sql_conn_pool  
  28. {  
  29.         sql_node sql_pool[POOL_NUMBER];//连接池中的连接  
  30.         char ip[IP_LEN + 1];//ip  
  31.         int port;//端口 mysql端口默认3306  
  32.         char db_name[DBNAME_LEN + 1];//数据库名称  
  33.         char user[DBUSER_LEN + 1];//用户名  
  34.         char passwd[PASSWD_LEN + 1];//密码  
  35.         int pool_number;//池中连接数量  
  36. }sql_conn_pool;  
  37.   
  38. //创建数据库连接池  
  39. sql_conn_pool * sql_pool_create(int connect_pool_number, char ip[], int port, char name[], char user[], char passwd[])  
  40. {  
  41.         sql_conn_pool *sp = NULL;  
  42.   
  43.         if (!connect_pool_number || connect_pool_number < 1)  
  44.         {  
  45.                 connect_pool_number = 1;  
  46.         }  
  47.   
  48.         sp = (sql_conn_pool *)malloc(sizeof(sql_conn_pool));  
  49.   
  50.         if (sp == NULL)  
  51.         {  
  52.                 printf("malloc for sql connect pool error!\n");  
  53.                 return NULL;  
  54.         }  
  55.   
  56.         strcpy(sp->ip, ip);  
  57.         sp->port = port;  
  58.         strcpy(sp->db_name, name);  
  59.         strcpy(sp->user, user);  
  60.         strcpy(sp->passwd, passwd);  
  61.   
  62.         //创建连接  
  63.         for (int index = 0; index < connect_pool_number; index++)  
  64.         {  
  65.                 //创建失败  
  66.                 if (-1 == create_connect(sp, &(sp->sql_pool[index])))  
  67.                 {  
  68.                         sql_pool_destroy(sp);  
  69.                         return NULL;  
  70.                 }  
  71.   
  72.                 printf("create database pool success\n");  
  73.                 sp->sql_pool[index].index = index;  
  74.                 sp->pool_number++;  
  75.         }  
  76.   
  77.         return sp;  
  78. }  
  79.   
  80. //创建连接  
  81. static int create_connect(sql_conn_pool *sp, sql_node *node)  
  82. {  
  83.         int ret = 0;  
  84.         int opt = 1;  
  85.   
  86.         //初始化对象并返回对象地址  
  87.         if (NULL == mysql_init(&node->fd))  
  88.         {  
  89.                 printf("mysql init error\n");  
  90.                 ret = -1;  
  91.         }  
  92.         else  
  93.         {  
  94.                 //mysql_real_connect非线程安全,用前需要加锁  
  95.                 pthread_mutex_init(&node->mutex, NULL);  
  96.   
  97.                 if (!(node->mysql_sock = mysql_real_connect(&node->fd, sp->ip, sp->user, sp->passwd, sp->db_name, sp->port, NULL, 0)))  
  98.                 {  
  99.                         printf("couldn't connect to engine! %s \n", mysql_error(&node->fd));  
  100.                         node->sql_state = DB_DISCONN;  
  101.                         ret = 1;  
  102.                 }  
  103.                 else  
  104.                 {  
  105.                         node->used = 0;  
  106.                         node->sql_state = DB_CONN;  
  107.                         mysql_options(&node->fd, MYSQL_OPT_RECONNECT, &opt);  
  108.                         opt = 3;//3s  
  109.                         mysql_options(&node->fd, MYSQL_OPT_CONNECT_TIMEOUT, &opt);  
  110.                         ret = 0;                          
  111.                 }  
  112.   
  113.         }  
  114.   
  115.         return ret;  
  116. }  
  117.   
  118. //销毁数据库连接池  
  119. void sql_pool_destroy(sql_conn_pool *sp)  
  120. {  
  121.         for (int index = 0; index < sp->pool_number; index++)  
  122.         {  
  123.                 if (NULL != sp->sql_pool[index].mysql_sock)  
  124.                 {  
  125.                         mysql_close(sp->sql_pool[index].mysql_sock);  
  126.                         sp->sql_pool[index].mysql_sock = NULL;  
  127.                 }  
  128.                 sp->sql_pool[index].sql_state = DB_DISCONN;  
  129.         }  
  130. }  
  131.   
  132. //从数据库连接池中获取一个未使用的连接  
  133. sql_node *get_db_connect_from_pool(sql_conn_pool *sp)  
  134. {  
  135.         int start_index = 0, index = 0, loop_index;  
  136.         int ping_ret;  
  137.   
  138.         srand((int)time(0));  
  139.         start_index = rand() % sp->pool_number;  
  140.   
  141.         for (loop_index = 0; loop_index < sp->pool_number; loop_index++)  
  142.         {  
  143.                 index = (start_index + loop_index) % sp->pool_number;  
  144.   
  145.                 if (!pthread_mutex_trylock(&sp->sql_pool[index].mutex))  
  146.                 {  
  147.                         if (DB_DISCONN == sp->sql_pool[index].sql_state)  
  148.                         {  
  149.                                 //重新连接  
  150.                                 if (0 != create_connect(sp, &(sp->sql_pool[index])))  
  151.                                 {  
  152.                                         //重新连接失败  
  153.                                         release_sock_to_sql_pool(&(sp->sql_pool[index]));  
  154.                                         continue;  
  155.                                 }  
  156.                         }  
  157.                 }  
  158.   
  159.                 ping_ret = mysql_ping(sp->sql_pool[index].mysql_sock);  
  160.   
  161.                 if (0 != ping_ret)  
  162.                 {  
  163.                         printf("mysql ping error!\n");  
  164.                         sp->sql_pool[index].sql_state = DB_DISCONN;  
  165.                         release_sock_to_sql_pool(&(sp->sql_pool[index]));  
  166.                 }  
  167.                 else  
  168.                 {  
  169.                         sp->sql_pool[index].used = 1;  
  170.                         break;  
  171.                 }  
  172.         }  
  173.   
  174.         if (loop_index == sp->pool_number)  
  175.         {  
  176.                 return NULL;  
  177.         }  
  178.         else  
  179.         {  
  180.                 return &(sp->sql_pool[index]);  
  181.         }  
  182. }  
  183.   
  184. //将使用完的连接还给连接池  
  185. void release_sock_to_sql_pool(sql_node *node)  
  186. {  
  187.         node->used = 0;  
  188.         pthread_mutex_unlock(&node->mutex);  
  189. }  
  190.   
  191. int main()  
  192. {  
  193.         MYSQL_FIELD *fd;  
  194.         sql_conn_pool *sp = sql_pool_create(POOL_NUMBER, "192.168.238.132", 3306, "yexin", "root", "1234567890");  
  195.         sql_node *node = get_db_connect_from_pool(sp);  
  196.   
  197.         if (NULL == node)  
  198.         {  
  199.                 printf("get sql pool node error.\n");  
  200.                 return -1;  
  201.         }  
  202.   
  203.         if (mysql_query(&(node->fd), "delete from student where sno = '0174'"))  
  204.         {  
  205.                 printf("query error.\n");  
  206.                 return -1;  
  207.         }  
  208.         else  
  209.         {  
  210.                 printf("delete succeed!\n");  
  211.         }  
  212.   
  213.         sql_pool_destroy(sp);  
  214.   
  215.         return 0;  
  216. }  
运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值